Guides
Errors
Error envelope format, stable error codes, and recovery guidance for all 505pay API errors
Errors
All 505pay API errors use a consistent JSON envelope. Error codes are stable strings — you can rely on them across API versions.
Error envelope
{
"error": "code_string",
"message": "Human-readable description of what went wrong.",
"request_id": "01j..."
}| Field | Type | Description |
|---|---|---|
error | string | Stable machine-readable error code |
message | string | Human-readable description; may change between releases |
request_id | string | UUIDv7 identifying this request; include in support tickets |
Always key your error-handling logic on error, not message. The message is for humans and may be updated without notice.
Error catalog
Deep link to any error with #error-<code> — for example #error-idempotency_conflict.
| Code | HTTP | Service | Summary | Remediation |
|---|---|---|---|---|
invalid_credentials | 401 | auth | Invalid client credentials. | Verify CLIENT_ID and CLIENT_SECRET. Check for whitespace or encoding issues in secrets. |
token_expired | 401 | auth | Access token has expired. | Refresh using your refresh_token. Refresh proactively before expiry. |
token_invalid | 401 | auth | Access token is malformed or tampered. | Obtain a new token. Never modify JWT payload manually. |
insufficient_scope | 403 | auth | Token does not have the required scope. | Request a token with the necessary scopes. Check the scope parameter in your token request. |
tenant_suspended | 403 | auth | Tenant account is suspended. | Contact 505pay support. All API calls return this until suspension is lifted. |
tenant_not_found | 404 | auth | Tenant not found. | Verify tenant ID or API credentials. May indicate misconfigured environment (sandbox vs production). |
payment_not_found | 404 | payments | Payment not found. | Verify the payment ID. IDs are UUIDv7 — check for typos. |
payment_already_settled | 409 | payments | Payment has already been settled. | No action needed — treat as success (idempotent). |
insufficient_balance | 422 | payments | Insufficient account balance. | Top up merchant balance or contact ops. Specific to balance-deducted payment methods. |
method_not_supported | 422 | payments | Payment method not supported for this transaction. | Use an alternate payment method. Check enabled methods in your tenant settings. |
idempotency_conflict | 409 | payments | Idempotency key reused with different request body. | Use a unique idempotency key per distinct request, or ensure the request body matches the original. |
rate_limited | 429 | all | Rate limit exceeded. | Retry after the time in X-RateLimit-Reset header. See the Rate Limits guide for backoff guidance. |
validation_failed | 400 | all | Request validation failed. | Check the request body against the API reference. The message field contains field-level details. |
internal | 500 | all | An internal server error occurred. | Retry with exponential backoff. Contact support if persistent — include request_id in your support ticket. |
not_found | 404 | all | Resource not found. | Verify the resource ID and endpoint path. Check you are using the correct base URL for the environment. |
Error handling example
interface ApiError {
error: string;
message: string;
request_id: string;
}
async function callApi(url: string, token: string): Promise<Response> {
const res = await fetch(url, {
headers: { Authorization: `Bearer ${token}` },
});
if (!res.ok) {
const err: ApiError = await res.json();
switch (err.error) {
case 'token_expired':
// Refresh token and retry
token = await refreshAccessToken();
return callApi(url, token);
case 'rate_limited':
// Wait and retry
const resetAt = Number(res.headers.get('X-RateLimit-Reset')) * 1000;
await new Promise(r => setTimeout(r, resetAt - Date.now()));
return callApi(url, token);
case 'internal':
console.error(`Server error [${err.request_id}]:`, err.message);
throw new Error(`API internal error — request ID: ${err.request_id}`);
default:
throw new Error(`${err.error}: ${err.message}`);
}
}
return res;
}Was this page helpful?