Errors
Every error response is JSON with an error field. On the write endpoints it is a stable code you can branch on, and error_description adds a sentence. On the read endpoints it is a sentence. The token endpoint uses the OAuth2 codes.
Read endpoints
| Status | Meaning |
|---|---|
400 | A list endpoint got a cursor that is not one it returned. |
401 | The Authorization header is missing, or the bearer token is invalid, expired or revoked. |
404 | The resource does not exist, or your token's scopes do not cover it. The two look identical on purpose: an out-of-scope resource should not confirm it exists. |
429 | You have hit a request limit. See Rate limits for Retry-After and the RateLimit-* headers. |
400401404429Token endpoint
| Error | Meaning |
|---|---|
400 invalid_request | A parameter is missing, or the Content-Type is not application/x-www-form-urlencoded. |
400 invalid_grant | The code or refresh token is wrong, expired or revoked, or does not match the redirect_uri or code_verifier. A code that was already used is refused too, and using it twice revokes the tokens the first use issued. |
400 unsupported_grant_type | Use authorization_code or refresh_token. |
401 invalid_client | The client ID is unknown or not approved, or the client secret is wrong. |
400 invalid_request400 invalid_grant400 unsupported_grant_type401 invalid_clientWrite endpoints
A write answers 403 insufficient_scope when the token lacks the write scope, where a read answers 404. Nothing is written by a request that fails.
| Status | Code | Meaning |
|---|---|---|
400 | idempotency_key_required | A write was sent without an Idempotency-Key header. |
400 | idempotency_key_invalid | The Idempotency-Key is not 8 to 128 printable ASCII characters. |
400 | invalid_json | The body is not valid JSON. |
403 | insufficient_scope | The token does not carry the write scope the route needs, for example a token that was only granted shifts:read. |
403 | plan_limit | The company's plan does not allow this. |
404 | employee_not_found | No such employee in this company. An employee of another company looks the same. |
409 | idempotency_request_in_progress | A request with this key is still running. Wait for the Retry-After seconds, then repeat it with the same key. |
409 | shift_overlap | The hours overlap another entry for this employee. |
413 | payload_too_large | The body is larger than the limit. |
422 | validation_failed | The body is not valid. The response lists the offending field names in fields, never their values. Unknown fields are refused. |
422 | idempotency_key_reuse | The key was used before with a different body or route. Use a new key for a new request. |
422 | employee_absent | The employee has approved absence in this period. |
422 | rest_rule_violation | The shift breaks a rest rule for this employee. Partners cannot override it. |
422 | outside_employment | The date is before the employee's employment started. |
422 | in_the_future | Hours cannot be registered for a time that has not happened yet. |
429 | rate_limited | A write limit was reached. Retry after the seconds in Retry-After. |
500 | internal_error | Something went wrong on our side. Repeat the request with the same Idempotency-Key. |
400idempotency_key_required400idempotency_key_invalid400invalid_json403insufficient_scope403plan_limit404employee_not_found409idempotency_request_in_progress409shift_overlap413payload_too_large422validation_failed422idempotency_key_reuse422employee_absent422rest_rule_violation422outside_employment422in_the_future429rate_limited500internal_errorAn employee's first shift
POST /shifts runs the same rest-rule check as planning a shift in Sparro, and a partner cannot override it. That check measures rest against the employee's other shifts within 14 days before and after the new one. With none in that window it has nothing to measure against and answers rest_rule_violation, so a first shift can be refused although it breaks no rule.
To work around it, plan the shift within 14 days of one the employee already has, register hours with POST /shifts/hours for work that has already happened (hours do not go through that check), or ask the company's owner to plan it in Sparro.
Server errors
500 — something failed on our side. Retry with backoff. A write that failed is not remembered, so repeating it with the same Idempotency-Key is safe. If it persists, contact support.