All error responses use a consistent JSON envelope:
{
"success": false,
"error": {
"code": "CARD_DECLINED",
"message": "The card was declined by the issuer.",
"details": {}
}
}
HTTP status codes
| Status | Meaning |
|---|
200 | Request succeeded |
400 | Bad request — invalid parameters or business rule violation |
401 | Unauthorized — missing or invalid API key |
403 | Forbidden — valid credentials but insufficient permissions |
404 | Resource not found |
409 | Conflict — duplicate or state violation |
422 | Unprocessable — well-formed request but semantic error |
429 | Rate limited — slow down and retry |
500 | Internal server error — contact support if persistent |
Error codes
Authentication
| Code | HTTP | Description | Resolution |
|---|
INVALID_API_KEY | 401 | The API key is missing or malformed | Check the Authorization: Bearer <key> header |
EXPIRED_API_KEY | 401 | The API key has been revoked or expired | Generate a new key from the dashboard |
INSUFFICIENT_PERMISSIONS | 403 | Key lacks the required scope | Use a key with the required permission scope |
Transactions
| Code | HTTP | Description | Resolution |
|---|
CARD_DECLINED | 400 | Card declined by the issuer | Ask the customer to use a different card |
CARD_EXPIRED | 400 | The card expiration date has passed | Ask the customer for a valid card |
INVALID_CVV | 400 | CVV does not match | Ask the customer to re-enter the CVV |
INSUFFICIENT_FUNDS | 400 | Not enough funds on the card | Ask the customer to use a different card |
DO_NOT_HONOR | 400 | Generic issuer decline | Ask the customer to contact their bank |
TRANSACTION_NOT_FOUND | 404 | No transaction with the given ID | Verify the transaction ID |
TRANSACTION_ALREADY_VOIDED | 409 | Transaction was already voided | No action needed |
CAPTURE_AMOUNT_EXCEEDS_AUTH | 422 | Capture amount is greater than the authorized amount | Reduce capture to at most the authorized amount |
TRANSACTION_NOT_CAPTURABLE | 422 | Transaction type cannot be captured | Only authorized transactions can be captured |
Subscriptions
| Code | HTTP | Description | Resolution |
|---|
SUBSCRIPTION_NOT_FOUND | 404 | No subscription with the given ID | Verify the subscription ID |
SUBSCRIPTION_ALREADY_CANCELLED | 409 | Cannot cancel an already-cancelled subscription | No action needed |
SUBSCRIPTION_PAUSED | 422 | Cannot bill a paused subscription | Resume the subscription first |
INVALID_BILLING_INTERVAL | 400 | The billing interval is not valid | Use daily, weekly, monthly, or yearly |
Settlements
| Code | HTTP | Description | Resolution |
|---|
BATCH_NOT_FOUND | 404 | No batch with the given ID | Verify the batch ID |
BATCH_ALREADY_CLOSED | 409 | Batch has already been settled | Query the closed batch details instead |
BATCH_STUCK | 500 | Batch settlement is stuck | Use the force-close endpoint or contact support |
Webhooks
| Code | HTTP | Description | Resolution |
|---|
WEBHOOK_NOT_FOUND | 404 | No webhook with the given ID | Verify the webhook ID |
INVALID_WEBHOOK_URL | 400 | The endpoint URL is not reachable or invalid | Ensure the URL is HTTPS and publicly accessible |
INVALID_EVENT_TYPE | 400 | Unknown event type requested | See the webhooks guide for valid types |
Rate limiting
Requests are rate-limited per API key:
| Tier | Limit |
|---|
| Default | 100 requests/minute |
| Burst | 20 requests/second |
| Export endpoints | 10 requests/minute |
When rate-limited you receive HTTP 429 with a Retry-After header indicating seconds to wait:
HTTP/1.1 429 Too Many Requests
Retry-After: 15
Content-Type: application/json
{
"success": false,
"error": {
"code": "RATE_LIMITED",
"message": "Too many requests. Retry after 15 seconds."
}
}
Implement exponential backoff with jitter when you receive a 429.