Errors
Error handling and response codes
VeroID uses standard HTTP status codes and returns detailed error information in JSON format.
HTTP Status Codes
| Code | Meaning |
|---|---|
| 200 | Success |
| 400 | Bad Request - Invalid parameters |
| 401 | Unauthorized - Invalid or missing API key |
| 402 | Payment Required - Insufficient credits |
| 403 | Forbidden - Your account is not a developer account, or is not approved for this environment |
| 404 | Not Found - Resource doesn't exist |
| 422 | Unprocessable Entity - Sandbox screening name not in the test list |
| 429 | Too Many Requests - API rate limit or document cooldown (DOCUMENT_COOLDOWN) |
| 500 | Internal Server Error |
Error Response Format
{
"error": "Error Type",
"code": "ERROR_CODE",
"message": "Human-readable description",
"verificationId": "ver_..."
}Authentication Errors (401)
{
"error": "Unauthorized",
"message": "Missing API key. Include X-API-Key header."
}Credit Errors (402)
{
"error": "Payment Required",
"message": "Insufficient credit balance. Please purchase more credits."
}Sandbox Screening Errors (422)
Returned when a sandbox screening request uses a name that is not one of the predefined test triggers. The response includes testNames with the valid values. See Sandbox for the full list.
{
"error": "Unprocessable Entity",
"code": "SANDBOX_SCREENING_NAME_REQUIRED",
"message": "Sandbox screening only accepts predefined test names...",
"testNames": [
"screenclear",
"screensanction",
"screenpep",
"screenpossible",
"screenmultiple",
"screenmedia",
"screenrca"
]
}Access Errors (403)
Returned when a sk_live_* key is used before the onboarding fee is settled or before VeroID has approved your account for live production verification. This applies to every endpoint, including test-api.veroid.com.au. Use a sk_test_* key on the test endpoint until your account is approved for production.
{
"error": "Forbidden",
"message": "Live verification is locked until your account is approved for the production verification hub. Use a test key against the test endpoint in the meantime."
}If the onboarding fee is the blocker, the message names it instead:
{
"error": "Forbidden",
"message": "Live keys are issued once the one-time developer onboarding fee is paid. Use a sandbox key against the test endpoint in the meantime."
}The API is only available to developer accounts. If the account has been switched to the dashboard track, its keys stop working entirely:
{
"error": "Forbidden",
"message": "API keys are not part of a dashboard account. Switch to a developer account to integrate over the API."
}Expanded verification responses
A successful HTTP 200 from POST /v1/verify can still mean the document did not match. Expanded verification responses (field-level detail in the errors array) are not HTTP errors - they are part of a normal verification outcome when status is failed, invalid_input, or responseCode is D.
{
"success": true,
"responseCode": "N",
"status": "failed",
"match": false,
"errors": [
{ "source": "issuer", "field": "BirthDate", "message": "Date of birth does not match." }
]
}See Expanded Responses in the API reference. Do not retry identity no-match (N), validation failures (invalid_input), or D. Only system errors (S, or HTTP 500) are transient. Live credits are not deducted for invalid_input.
Rate Limits
| Limit | Value |
|---|---|
| Requests per second | 10 |
| Requests per minute | 100 |
| Requests per hour | 1,000 |
When the per-key rate limit is exceeded, the API returns HTTP 429 with a Retry-After header (seconds).
API error codes (code field)
Top-level JSON errors use a code string. Verification and session failures may also include error.code on the resource. Legacy codes prefixed with DVS_ may appear as deprecatedCode during a transition period; integrate against the neutral name only.
| Code | Typical HTTP | Meaning |
|---|---|---|
SANDBOX_SCREENING_NAME_REQUIRED | 422 | Sandbox screening name not in test list |
DOCUMENT_COOLDOWN | 429 | Same document resubmitted too soon |
DOCUMENT_LOCKED | 500 | Issuer temporarily locked the document |
HUB_NOT_CONFIGURED | 500 | Verification hub not configured for this environment |
HUB_SYSTEM_ERROR | 500 | Transient hub or system failure (retry may help) |
INTERNAL_ERROR | 500 | Unexpected server error |
INSUFFICIENT_CREDITS | 402 | Prepaid balance too low |
Verify session error codes (GET /v1/verify/sessions/:id)
| Code | Meaning |
|---|---|
SMS_SEND_FAILED | SMS could not be sent for hosted link sessions |
FACE_MATCH_FAILED | Biometric face match step failed |
NOT_MATCH | Issuer did not match submitted details |
FINISH_IN_PROGRESS | Concurrent finish request; poll session again |
DOCUMENT_LOCKED | Issuer temporarily locked the document |
HUB_NOT_CONFIGURED | Hub unavailable for this check |
Embedded form browser errorCode values
The iframe posts veroid-embed messages with type: "error". Common errorCode values include session_expired, session_cancelled, network_error, camera_denied, validation_error, and submit_failed. Treat these as UX signals; authoritative outcomes are on the session API and webhooks.
Verify session statuses
status on the session API reflects server state. The embed complete event uses submitted only as a browser-side signal that the form was submitted; it is not a session API status.
created- session record createdsms_sent/sms_failed- SMS delivery outcome for link sessionsopened- subject opened the hosted pageconsent_given- consent step completedfields_captured- document fields capturedprocessing- issuer check in progresscompleted- finished with a resultfailed- finished with an errorexpired- pastexpiresAtcancelled- cancelled via API
Idempotency (create endpoints)
Optional Idempotency-Key headers on POST /v1/verify/sessions, POST /v1/verify/embed-sessions, and POST /v1/screening. Keys are scoped per account, test or live environment, and endpoint. Retention is 24 hours. Without a key, behaviour is unchanged.
| Outcome | HTTP | Code / header | When |
|---|---|---|---|
| Successful create or replay | 201 (verify sessions) or 200 (screening) | Idempotent-Replayed: true on replay only | Same key and same JSON body within 24 hours |
| Invalid key | 400 | INVALID_IDEMPOTENCY_KEY | Empty or whitespace-only key, or longer than 255 characters |
| Key reused with different body | 422 | IDEMPOTENCY_KEY_REUSED_WITH_DIFFERENT_PARAMS | Same key within 24 hours but request JSON differs for that endpoint |
| Embed replay unavailable | 409 | IDEMPOTENCY_REPLAY_UNAVAILABLE | POST /v1/verify/embed-sessions only: original session exists but clientSecret cannot be replayed (encryption not configured). Use GET /v1/verify/sessions/:id for the outcome. |
Concurrent retries with the same key and body wait on a server-side lock, then return the original create response (201 or 200 with Idempotent-Replayed: true). They do not return 409 for duplicate in-flight creates.
Document cooldown (429)
On live verification, submitting the same document details again too soon returns HTTP 429 with code DOCUMENT_COOLDOWN - before the request reaches the issuer. This protects against issuer lockouts when users retry quickly after a typo or failed check.
{
"error": "Too Many Requests",
"code": "DOCUMENT_COOLDOWN",
"message": "This document was verified recently. Wait before submitting the same document again.",
"retryAfterSeconds": 45
}If the issuer or verification hub has already locked the document, the cooldown is longer (about 5 minutes) and the message references a temporary document lock. Honour Retry-After and do not auto-retry with the same payload. See Best Practices.
Document temporary lock (500)
When the verification hub returns "Document Temporarily Locked", the API responds with HTTP 500, code DOCUMENT_LOCKED, and a Retry-After header. Wait before submitting the same document details again - do not treat this like a system error (S) retry loop.
{
"success": false,
"error": "Verification Failed",
"code": "DOCUMENT_LOCKED",
"deprecatedCode": "DVS_DOCUMENT_LOCKED",
"message": "Document Temporarily Locked",
"verificationId": "ver_...",
"retryAfterSeconds": 300
}