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
{
  "error": {
    "code": "invalid_request",
    "message": "amount_usd must be greater than 0."
  }
}

Branch on code, not message

The code 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:

StatusCodeMeaning
400invalid_requestThe request was malformed — a field is missing, the wrong type, or out of range. The message names the offending field.
401unauthorizedThe API key is missing, malformed, or does not match a live key.
403forbiddenThe key is valid but read-only, and the operation requires a write-scoped key.
404not_foundNo resource with that id belongs to the authenticated merchant.
409conflictAn Idempotency-Key is still processing, or was reused with a different body.
413payload_too_largeThe request body exceeded the maximum allowed size.
429rate_limitedThe rate limit for this key was exceeded. Honour the Retry-After header.
503price_unavailableThe live BTC price needed to convert USD → sats is temporarily unavailable. Retry shortly.
500server_errorAn 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.