When a caller is not allowed to see a resource, the API returns 404 Not Found — not 403 Forbidden. The response is deliberately identical to the one you get for an ID that was never issued, so that the API never confirms whether a resource exists to someone who is not entitled to read it.
A 404 is not proof that a resource was deleted. It means one of:
- the ID does not exist, or never did
- the resource exists but belongs to another account
- the resource exists in your account but your token may not read it
- your token is missing, expired, or invalid
Do not build reconciliation, sync or clean-up logic that treats a 404 as a deletion signal — you will delete records on your side that are alive and well on ours, merely because a permission changed. If you need to know that something was deleted, use webhooks.
These 404 responses have an empty body. There is no message to parse and no code to branch on, so do not expect the error envelope described below.
Every other error returns JSON with a human-readable message:
{
"message": "Sorry, we found issues with some of the submitted data - please review and try again."
}Two responses add a field to it:
| Field | Appears on | Meaning |
|---|---|---|
errors | 422 | Per-field validation failures |
code | Unexpected server errors | A reference to quote to support |
message is translated, so its wording follows the request's locale and is not stable enough to branch on. Branch on the status code and, for 422, on the keys inside errors.
| Status | When |
|---|---|
200 OK | A successful read, update, or a write that returns the resource |
201 Created | A successful create, returning the new resource |
204 No Content | A successful delete, with an empty body |
401 Unauthorized | Bad credentials at the token endpoint only — see below |
403 Forbidden | A business rule refused the action — see below |
404 Not Found | Not found, or not yours — see above |
422 Unprocessable Entity | The request was understood but the data failed validation |
429 Too Many Requests | A rate limit window was exceeded — see Rate limits |
401 is narrower than it looks. It comes from the token endpoint, when the credentials you exchanged for a token were rejected.
It is not what you get for a missing or expired token on a normal API endpoint. That returns the same bare 404 as everything else in the section above. So if a previously working integration starts returning 404 across every endpoint at once, suspect the token before you suspect the data.
Because authorisation failures collapse into 404, a 403 never means "you may not see this". It means the opposite: the resource is yours and we know who you are, but a rule specific to that operation refused it — a quota exhausted, a duplicate that is not permitted, a state that forbids the change.
A 403 is therefore always endpoint-specific, and is documented on the endpoint that can return it, together with the message it sends.
A 422 names every field at fault. errors is keyed by field name, and each value is the list of problems with that field. Nested fields are dotted.
{
"message": "Sorry, we found issues with some of the submitted data - please review and try again.",
"errors": {
"status": ["The selected status is invalid."],
"user_id": ["The user id field is required."]
}
}Read errors rather than the message — the message is the same generic sentence on every validation failure.
If something fails on our side, the response carries a reference instead of the underlying detail:
{
"message": "Unexpected Error on Rosterfy",
"code": "ACEH292"
}Log code and quote it to support; it identifies the specific failure. Treat these as retryable only if the operation is safe to repeat.
Some endpoints return a status that does not follow from any of the above — a 402 where a payment is outstanding, a 410 where a form has closed, a 403 for a rule unique to that operation. Those are documented on the endpoint itself, so check its reference page as well as this one.