Errors
The API uses conventional HTTP status codes and returns a consistent, machine-readable error envelope so you can branch on a stable code rather than parsing messages.
Error shape
Every 4xx and 5xx response has the same JSON body: an error object with a stable code and a human-readable message.
application/json
Branch on code, not message
Thecode is stable and safe to switch on. The message is meant for humans and may change or name a specific offending field.Error codes
Every code the API can return, with its HTTP status:
| Status | Code | Meaning |
|---|---|---|
400 | invalid_request | The request was malformed — a field is missing, the wrong type, or out of range. The message names the offending field. |
401 | unauthorized | The API key is missing, malformed, or does not match a live key. |
403 | forbidden | The key is valid but read-only, and the operation requires a write-scoped key. |
404 | not_found | No resource with that id belongs to the authenticated merchant. |
409 | conflict | An Idempotency-Key is still processing, or was reused with a different body. |
413 | payload_too_large | The request body exceeded the maximum allowed size. |
429 | rate_limited | The rate limit for this key was exceeded. Honour the Retry-After header. |
503 | price_unavailable | The live BTC price needed to convert USD → sats is temporarily unavailable. Retry shortly. |
500 | server_error | An unexpected error occurred on our side. Safe to retry with the same Idempotency-Key. |
Handling errors
Treat 4xx codes as problems with the request you should fix (bad input, wrong scope, unknown id), and 5xx plus 503 price_unavailable as transient — safe to retry with backoff. When retrying a write, reuse the same Idempotency-Key so you never create a duplicate.