2xx response uses the same application/json envelope:
codeis a stable, machine-readable identifier. Branch your integration on this value. It never changes meaning, so it is safe to compare against.detailis a human-readable sentence meant for logs and debugging. Treat it as informational only: the wording can change at any time, so never match on it.errorsis an array of field-level problems, present mainly oninvalid_request. Each entry has afield(the parameter that failed), acode(a short reason), and amessage(a human explanation). It is an empty array when no single field is at fault.
HTTP status codes
The HTTP status reflects the broad category of the failure, whilecode identifies the exact case:
- 4xx: the request was rejected as sent. Fix the request before retrying (bad input, missing or invalid key, resource not found).
- 5xx: the request failed on our side. It is safe to retry, ideally with backoff.
Handle unknown codes gracefully
This catalog grows over time. We may add newcode values, and new endpoints may return existing codes in new places. Treat the set as open: branch on the codes you know, and fall back to handling anything unrecognized by its HTTP status (for example, retry on 5xx, surface a generic message on 4xx) instead of failing.
| Code | HTTP | Meaning |
|---|---|---|
invalid_request | 400 | The request was rejected during validation. The errors array lists each failing field with a reason, so you can map the failure back to a specific parameter. |
unauthorized | 401 | Authentication failed: the API key is missing, malformed, or revoked. Send a valid key as a bearer token in the Authorization header. Each key is bound to a single environment (test or live). |
invoice_not_found | 404 | No invoice matches the supplied identifier in the environment the API key belongs to. An invoice created with a live key is not visible to a test key, and vice versa. |
internal_error | 500 | An unexpected error occurred on our side. No partial work is persisted, so the request can be retried safely. |