Errors
Error envelope, HTTP status codes, and which errors to retry
Every non-2xx response uses the same JSON envelope.
{
"defined": true,
"code": "CONFLICT",
"status": 409,
"message": "Conflict",
"data": {}
}| Field | Description |
|---|---|
defined | true for errors defined in the spec, false for unexpected ones. The shape is the same either way. |
code | Error code string (table below). Branch on code, not on message. |
status | Same as the HTTP status code. |
message | Human-readable description. Wording may change. |
data | Optional details. Currently only data.code on 402 is defined. |
Failed verifications on POST /v1/verify (wrong code, expired, …) are not errors: they return 200 with verified: false. See Verification results.
Status codes
| Status | code | Typical causes | Retry? |
|---|---|---|---|
| 400 | BAD_REQUEST | Invalid payload, missing/blank idempotency key, header/body key mismatch, unknown template or template variables, expiresInSec/maxAttempts/cost out of range, code not 6 digits, invalid limit/cursor/date range | No. Fix the request. |
| 401 | UNAUTHORIZED | Authorization: Bearer key missing, malformed, inactive (revoked/expired), or unrecognized | No. Check the key. |
| 402 | PAYMENT_REQUIRED | Out of credit; data.code is INSUFFICIENT_CREDIT or OVERDRAFT_LIMIT_EXCEEDED (POST /v1/issue only) | After topping up |
| 403 | FORBIDDEN | Key lacks the required scope, a pk_ key called a server-only API, or a pk_ request had no Origin / an unlisted Origin | No. Check the key settings. |
| 404 | NOT_FOUND | Unknown issueId for your app (including other apps' ids) or unknown templateId | No |
| 409 | CONFLICT | Idempotency key reused with a different body, or the key's issue has since been replaced | No |
| 500 | INTERNAL_SERVER_ERROR | Unexpected server failure | Yes, with backoff and the same idempotency key |
| 503 | SERVICE_UNAVAILABLE | Authentication or an internal dependency temporarily unavailable, or an earlier request with the same idempotency key is still being resolved | Yes, with backoff and the same idempotency key |
Per-endpoint error descriptions are on each page of the API reference.
Handling example
type ApiError = {
defined: boolean;
code: string;
status: number;
message: string;
data?: { code?: "INSUFFICIENT_CREDIT" | "OVERDRAFT_LIMIT_EXCEEDED" };
};
function handleIssueError(error: ApiError) {
switch (error.code) {
case "PAYMENT_REQUIRED":
// Alert operators: top-up needed (error.data?.code)
return "Please try again later.";
case "CONFLICT":
// Check whether an idempotency key was reused for a different request
return "The request could not be processed.";
case "SERVICE_UNAVAILABLE":
case "INTERNAL_SERVER_ERROR":
// Retry with the same idempotency key (/guides/idempotency)
return "Please try again later.";
default:
return "We could not send a verification code.";
}
}