Error Handling

Understand error responses and handle them gracefully in your integration.

Error response format

All API errors return a consistent JSON structure:

{
  "error": "Invoice with ID inv_abc123 not found",
  "code": "INV001"
}

The error field is a human-readable message and may change - don't parse it programmatically. The code field is a stable machine-readable module code (e.g. INV001) you can match on.

HTTP status codes

4xx Client errors

CodeMeaningWhat to do
400 Bad Request Check request body and query parameters. The message field describes the specific validation failure.
401 Unauthorized API key or secret is invalid, expired, or missing. Verify your X-API-Key and X-API-Secret headers.
403 Forbidden Your API key doesn't have the required permissions for this endpoint. Check your key's permission scope.
404 Not Found The requested resource doesn't exist or belongs to a different organization.
409 Conflict The action can't be performed in the current state. For example, approving an already-settled invoice.
412 Precondition Failed The resource is not in a state that permits this operation - a business-rule or state-machine precondition is not met (e.g., settling an invoice that has not been approved).
429 Rate Limited Too many requests. Back off and retry after the time indicated in X-RateLimit-Reset.

5xx Server errors

CodeMeaningWhat to do
500 Internal Error An unexpected error occurred. Retry with exponential backoff. If it persists, contact support.
502 Bad Gateway A downstream service is temporarily unavailable. Retry after a short delay.
503 Service Unavailable The API is temporarily down for maintenance. Check the status page.
504 Gateway Timeout Request took too long. Retry - for bulk operations, consider smaller batch sizes.

Common error codes

Invoice errors

Error codeStatusDescription
INV001404Invoice ID doesn't exist or isn't accessible
INV004412Action not allowed in current invoice status
INV003409Invoice number already exists for this connection
INV002400Generic invoice validation error (e.g., invalid amount or missing field)
CONN003404Connection ID doesn't exist or isn't accessible
CONN010412Connection is not in the expected status (e.g., suspended or archived)

Payment errors

Error codeStatusDescription
TREAS007412Not enough funds to complete the payment

Authentication errors

All API-key authentication failures - invalid key, invalid secret, revoked key - return the same response, with no distinguishing code:

{
  "error": "Unauthorized",
  "message": "Invalid API key or secret"
}

There is no environment-mismatch mechanism: sandbox and production keys are simply different key values, not separately validated against an environment flag.

Retry strategy

For transient errors (429, 500, 502, 503, 504), implement exponential backoff with jitter:

// Pseudocode
maxRetries = 3
for attempt in 0..maxRetries:
    response = makeRequest()
    if response.status < 500 and response.status != 429:
        return response

    delay = min(2^attempt * 1000, 30000)  // 1s, 2s, 4s... max 30s
    jitter = random(0, delay * 0.1)
    sleep(delay + jitter)
Don't retry 4xx errors Client errors (except 429) indicate a problem with the request itself. Retrying without changing the request will always fail. Fix the request first.

Idempotency and safe retries

Idempotency via the requestId field is enforced only for invoice create/submit and treasury balance/withdrawal operations. If you include a requestId on one of these and the original request succeeded, retrying returns the original response - no duplicate side effects. Other mutations (connections, webhooks, organization settings, API keys, notifications, storage) are not idempotent - a retry may create a duplicate.

// Safe retry pattern (invoice create)
POST /api/v1/invoices
{
  "requestId": "your-unique-id-123",  // same ID on retry
  "connectionId": "conn_abc",
  "amount": "1500.00",
  ...
}

This is especially important for payment operations where duplicate processing would be costly.

Debugging tips