Errors
The SpeyBooks API uses conventional HTTP status codes to indicate the outcome of a request. Codes in the 2xx range indicate success, codes in the 4xx range indicate a problem with the request, and codes in the 5xx range indicate an issue on our end.
Error response format
Every error response follows a consistent envelope. The code field is a machine-readable string you can match against in your code. The message field is a human-readable explanation suitable for logging — it should not be displayed directly to end users.
HTTP status codes
| Status | Meaning |
|---|---|
200 |
Success — the request completed normally |
201 |
Created — a new resource was created |
204 |
No Content — the request succeeded with no response body |
400 |
Bad Request — the request was malformed or contained invalid parameters |
401 |
Unauthorized — no valid authentication credentials were provided |
403 |
Forbidden — the credentials are valid but lack permission for this action |
404 |
Not Found — the requested resource does not exist |
409 |
Conflict — the request conflicts with the current state of a resource |
422 |
Unprocessable Entity — the request was well-formed but failed business logic validation |
429 |
Too Many Requests — you have exceeded the rate limit |
500 |
Internal Server Error — something went wrong on our end |
Error codes
| Code | Status | Description |
|---|---|---|
validation_error |
400 |
One or more fields failed validation. Check the message for details. |
invalid_id |
400 |
The ID does not match the expected format. SpeyBooks uses prefixed IDs — for example, inv_42 for invoices, cont_3 for contacts. |
not_found |
404 |
The resource does not exist, or it belongs to a different organisation. |
unauthorized |
401 |
The request lacks valid authentication. Either the session has expired, the API key is missing, or the token is invalid. |
forbidden |
403 |
Authentication succeeded but the credentials lack the required scope. |
insufficient_scope |
403 |
The API key does not have the required scope for this endpoint. The message field indicates which scope is needed. |
conflict |
409 |
The action conflicts with the current state of the resource — for example, deleting a finalised invoice. |
unprocessable_entity |
422 |
The request is syntactically valid but violates a business rule — for example, declaring a dividend that exceeds retained profits. |
rate_limited |
429 |
Too many requests. Back off and retry after the period indicated in the Retry-After header. |
internal_error |
500 |
An unexpected error on our side. If this persists, contact support with the request timestamp and endpoint. |
Idempotency errors
When using the Idempotency-Key header, two additional scenarios can occur. If you reuse a key with different request parameters, the API returns a 409 Conflict. If the original request failed with a 5xx error, the key is released and can be retried. Keys expire after 24 hours.
Handling errors
For robust integration, your client should:
- Always check the
successfield before accessingdata. - Match on the
error.codestring for programmatic handling, not the HTTP status code alone — multiple error codes can share the same status. - Log the full error response including
codeandmessagefor debugging. - Implement exponential backoff for
429and5xxresponses. - Never display
error.messagedirectly to end users — it is intended for developers and may change between releases.