Guidelines
Manual time requests
When an organization requires approval for manual time, members submit manual time requests. Public API v2 lets you list those requests and approve or deny them through a single status endpoint. Creating, deleting, and batch approve/deny are not available through the API.
- Scopes:
hubstaff:readto list / get,hubstaff:writeto approve or deny. - Actor can review the request (organization owner/manager, project manager, or team lead with manual-time approval for the member), and modify-time is allowed for the actor.
Step 1 — List pending requests
Collection is organization-scoped. Filter by status=submitted for the review queue. Use updated_at[start] for incremental sync.
curl "https://api.hubstaff.com/v2/organizations/<org_id>/manual_time_requests?status=submitted&page_limit=100" \
-H "Authorization: Bearer <access_token>" Single-record routes are shallow: GET /v2/manual_time_requests/<id>. Unknown or non-visible IDs return 404 / 12000.
date[start] and date[stop] must both be provided when filtering by date. They match requests whose start_time…stop_time range overlaps the organization-local day range — not equality on the response date field. Overnight entries can appear on adjacent calendar days. Deny
Only submitted → denied is allowed. denial_reason is required.
curl -X PUT https://api.hubstaff.com/v2/manual_time_requests/<id>/status \
-H "Authorization: Bearer <access_token>" \
-H "Content-Type: application/json" \
-d '{
"status": "denied",
"denial_reason": "Outside scheduled hours"
}'Approve
A plain approve creates activity for the full requested range (limits, collisions, budgets, shifts, timesheets, and so on still apply). When the approval is clean, one PUT is enough. When a warning needs an explicit decision, the API returns 422 with a stable error_code — retry the same status endpoint with the matching intent flag (see Handle approval conflicts).
curl -X PUT https://api.hubstaff.com/v2/manual_time_requests/<id>/status \
-H "Authorization: Bearer <access_token>" \
-H "Content-Type: application/json" \
-d '{ "status": "approved" }' Deny and approve are mutually exclusive paths: once a request leaves submitted, further status updates return 14451.
Handle approval conflicts
These are not follow-on steps after a successful approve — they are optional intent flags you send on the same status endpoint when a first attempt returns 422, or when you already know the policy to apply.
Member limits
Member-limit handling is opt-in. Pass one boolean intent; the server calculates excess time or limit increases.
| Intent | Body | Who | Result when over limit |
|---|---|---|---|
| Fail closed | (omit both) | Any approver | 422 / 14300 or 14301 |
| Approve only what fits | discard_exceeding_hours: true | Any approver | Activity shortened from the end; original start_time / stop_time / duration_seconds kept |
| Approve full + raise limits | increase_limit: true | Owner / manager only | Full activity; exceeded daily/weekly limits raised atomically |
discard_exceeding_hours or increase_limit, not both (400 / 11000). increase_limit requires an organization owner or manager — project managers and leads get 403 / 10003. # Discard excess time (keep member limits as-is)
curl -X PUT https://api.hubstaff.com/v2/manual_time_requests/<id>/status \
-H "Authorization: Bearer <access_token>" \
-H "Content-Type: application/json" \
-d '{
"status": "approved",
"discard_exceeding_hours": true
}'
# Raise limits and approve the full range (owner/manager)
curl -X PUT https://api.hubstaff.com/v2/manual_time_requests/<id>/status \
-H "Authorization: Bearer <access_token>" \
-H "Content-Type: application/json" \
-d '{
"status": "approved",
"increase_limit": true
}'approved (with approved_by_id / approved_at), but its start_time / stop_time / duration_seconds stay as originally submitted. Only the created activity is shortened — do not treat those fields as the approved activity length. If the entire requested range exceeds the limit, discard cannot create an activity. Retry with increase_limit if authorized, or deny the request. Collisions and project budget
These flags are independent of member limits.
| Parameter | When omitted | Notes |
|---|---|---|
| accept_collisions | 422 if an override is required | Broad confirm for authorized time / payment / invoice / timesheet / integration collisions. |
| allow_above_project_budget | 422 / 14304 if over budget | Project budget only — not member daily/weekly limits. |
accept_collisions
Confirms approval when the request overlaps existing time the approver is authorized to override. One flag covers ordinary tracked-time collisions and associated data within role permissions:
- Unprocessed paid time (team payments)
- Client invoices
- User (team) invoices
- Timesheets
- Integration-synced time (task or time export)
Approving with this flag may remove time from unprocessed payments and leave invoices or integrations out of sync. Only associations the actor's role may override are accepted — anything else fails the approval.
curl -X PUT https://api.hubstaff.com/v2/manual_time_requests/<id>/status \
-H "Authorization: Bearer <access_token>" \
-H "Content-Type: application/json" \
-d '{
"status": "approved",
"accept_collisions": true
}'allow_above_project_budget
Allows approval when the created activity would exceed the project budget. This is project budget only — not member daily/weekly limits (use discard_exceeding_hours or increase_limit for those).
Common error codes
Always branch on error_code in the JSON body.
| HTTP | error_code | Meaning / next step |
|---|---|---|
| 422 | 14105 | Collision — retry with accept_collisions: true if appropriate |
| 422 | 14300 / 14301 | Member limit — retry with discard_exceeding_hours or increase_limit |
| 422 | 14304 | Project budget — retry with allow_above_project_budget: true |
| 422 | 14451 | Not submitted (already approved or denied) |
| 422 | 14452 | Deny without denial_reason |
| 422 | 14453 | Self-approval blocked |
| 422 | 14454 | Approval target member inactive — denial is still allowed |
Full operation schemas live under the manual_time_requests reference tag. Machine-readable codes are listed in errors.