Guides
Errors & retries
Failures use the same envelope as successes, with success: false and a sentence written to be read by a person.
The shape of a failure
{
"success": false,
"error": "assigneeId: Required",
"version": "v1"
}By status
| Status | Means | What to do |
|---|---|---|
| 401 | No key, or the key is wrong, expired or revoked. | Check the header reads Authorization: Bearer oct_live_… A revoked key fails from the next request onward. |
| 403 | Authenticated, but not allowed. | If code is scope_required, requiredScope names what to ask for. Without it, the organisation's plan does not include that module. |
| 400 | Understood and rejected: a missing field, a bad value, a range too wide. | error names the field, e.g. "assigneeId: Required". Retrying unchanged fails identically. |
| 404 | No such operation, or no such record in your organisation. | GET /v1 lists every operation. A record in another organisation reads as absent, deliberately. |
| 5xx | Our fault. | Retry with exponential backoff. A write is safe to retry when the first attempt gave no response at all. |
What is worth retrying
- 5xx and network failures: retry with exponential backoff and jitter.
- 429, if you meet one: back off; you are going too fast.
- 4xx otherwise: never. A 400 will fail identically forever, and retrying a 403 does not grant a scope.
A write with no response at all is safe to retry
If the first attempt returned nothing at all, whether a timeout or a dropped connection, you cannot know whether it was recorded. Retrying is the right move; the worst case is a duplicate you can see and remove, which is better than a change you believe happened and did not.
A record in another organisation reads as absent
You get a 404, not a 403. Distinguishing them would confirm that a record with that id exists somewhere, which is a small leak across a tenant boundary and an unnecessary one.
