HubstaffDeveloper Portal

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.

Requirements
  • Scopes: hubstaff:read to list / get, hubstaff:write to 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.

bash
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 filter semantics
date[start] and date[stop] must both be provided when filtering by date. They match requests whose start_timestop_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 submitteddenied is allowed. denial_reason is required.

bash
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).

bash
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.

Conflicts can surface one at a time
A retry may reveal another conflict. Preserve previously selected intent flags when retrying until the request succeeds or returns a non-retryable error — for example when collision, member limit, and project budget checks apply to the same approval.

Member limits

Member-limit handling is opt-in. Pass one boolean intent; the server calculates excess time or limit increases.

IntentBodyWhoResult when over limit
Fail closed(omit both)Any approver422 / 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 limitsincrease_limit: trueOwner / manager only Full activity; exceeded daily/weekly limits raised atomically
One strategy only
Send either 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.
bash
# 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
  }'
Original range is preserved
After a successful discard approval the request is 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.

ParameterWhen omittedNotes
accept_collisions422 if an override is required Broad confirm for authorized time / payment / invoice / timesheet / integration collisions.
allow_above_project_budget422 / 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.

bash
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.

HTTPerror_codeMeaning / next step
42214105 Collision — retry with accept_collisions: true if appropriate
42214300 / 14301 Member limit — retry with discard_exceeding_hours or increase_limit
42214304 Project budget — retry with allow_above_project_budget: true
42214451Not submitted (already approved or denied)
42214452 Deny without denial_reason
42214453Self-approval blocked
42214454 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.