API
Errors
The Hoursmith API uses a consistent error envelope with stable codes. Learn the format, status codes, and how to handle each.
Every error response uses the same JSON envelope, so you can handle failures predictably.
Error format
{
"error": {
"code": "validation_error",
"message": "Request validation failed.",
"fields": { "name": "required" }
}
}code— a stable, machine-readable string. Branch your logic on this, not onmessage.message— a human-readable description (may change wording over time).fields— present only on validation errors; maps each invalid field to a reason.- On
forbidden_plan, the error also includesrequiredPlanandfeature.
Status codes
| HTTP | code | Meaning | What to do |
|---|---|---|---|
400 | bad_request | Malformed request. | Fix the request. |
401 | unauthenticated | Missing/invalid/revoked token. | Check the Authorization header. |
402 | forbidden_plan | Workspace lacks API access. | Upgrade to Studio/Agency. |
403 | forbidden_scope | Role can't perform this action. | Use a token with a higher role. |
404 | not_found | Record doesn't exist or isn't visible to you. | Verify the id and access. |
409 | conflict | Blocked by state. | See below. |
422 | validation_error | Invalid input (or missing Idempotency-Key). | Inspect fields. |
429 | rate_limited | Too many requests. | Respect Retry-After. |
500 | internal | Something went wrong on our side. | Retry with backoff; contact support if it persists. |
When you'll see 409 conflict
The API refuses actions that would corrupt your data:
- Editing or deleting an invoiced, locked time entry or billed expense.
- Deleting a client that still has active projects or invoices.
- Reusing an idempotency key with a different request body.
Handling errors well
Switch on error.code
Treat code as the contract. Don't pattern-match on message.
Surface fields on 422
Show the per-field reasons back to the user or log them — they tell you exactly what to fix.
Retry only the right codes
Retry 429 (after Retry-After) and 500 (with backoff). Don't retry 4xx validation/permission
errors — fix the request instead.
Was this page helpful?