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_NOT_FOUND",
  "message": "Invoice with ID inv_abc123 not found",
  "details": {}
}

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

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.
422 Unprocessable Entity The request body is valid JSON but violates business rules (e.g., negative amounts, invalid currency).
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
INVOICE_NOT_FOUND404Invoice ID doesn't exist or isn't accessible
INVOICE_INVALID_STATUS409Action not allowed in current invoice status
INVOICE_DUPLICATE_NUMBER409Invoice number already exists for this connection
INVOICE_INVALID_AMOUNT400Amount must be positive with max 2 decimal places
CONNECTION_NOT_FOUND404Connection ID doesn't exist or isn't active
CONNECTION_NOT_ACTIVE409Connection is suspended or archived

Payment errors

Error codeStatusDescription
INSUFFICIENT_BALANCE409Not enough funds to complete the payment
PAYMENT_ALREADY_SETTLED409This invoice has already been paid
PAYMENT_FAILED500Payment processing failed - check details and retry

Authentication errors

Error codeStatusDescription
INVALID_API_KEY401API key is invalid or doesn't exist
INVALID_API_SECRET401API secret doesn't match the key
API_KEY_REVOKED401This API key has been revoked
ENVIRONMENT_MISMATCH403Sandbox key used against production or vice versa

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

All write operations support the requestId field. If you include a requestId and the original request succeeded, retrying returns the original response - no duplicate side effects.

// Safe retry pattern
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