Reference

Errors and limits

The error envelope, the HTTP statuses VerifyMe returns, a catalogue of every error code, rate limits, payload and upload limits, and how to retry safely.

Error envelope

Every error response from /v1 (and from the applicant and portal APIs) has this shape:

{
  "error": {
    "code": "IDEMPOTENCY_KEY_REUSED",
    "message": "This Idempotency-Key was already used with a different request body.",
    "details": [ { "field": "email", "code": "INVALID_EMAIL" } ],
    "traceId": "trc_01M3TRRXRX7XZ052JT63"
  }
}
FieldDescription
codeStable machine-readable code. Branch on this
messageHuman-readable explanation. May change. Do not parse
detailsOptional array. For validation errors: { field, code } entries, sometimes with extra context such as attemptsLeft
traceIdCorrelation id, equal to the X-Request-Id response header. Quote it to support

Unhandled problems return 500 VERIFYME_INTERNAL_ERROR with a generic message; internals are never leaked.

HTTP statuses

StatusMeaning in VerifyMeRetry?
200, 201, 202, 204Success. 201 create, 202 accepted for async work (webhook test), 204 deleten/a
400Validation failed or malformed JSONNo, fix the request
401Missing, invalid, revoked, expired or wrong-environment credentialNo
403Authenticated but not permitted: scope, IP allow-list, suspended workspace, document not availableNo
404Unknown resource, resource of another workspace, or unknown routeNo
409Conflict with current state or idempotencyOnly IDEMPOTENCY_IN_PROGRESS and VERIFYME_STATE_CONFLICT (after a short wait)
410Verification link expired (applicant API)No, issue a new request
413Body or file too largeNo
415Wrong content type for an uploadNo
422Well-formed but not processable (unknown flow, step rules)No
429Rate limitedYes, after Retry-After
500Server errorYes, with backoff
503Dependency temporarily unavailableYes, with backoff

Error code catalogue

API errors (/v1)

HTTPCodeMeaning and what to do
400VERIFYME_VALIDATION_ERROROne or more fields invalid. Read details[].field and details[].code
400VERIFYME_INVALID_JSONBody is not valid JSON
401VERIFYME_UNAUTHENTICATEDBad or missing Authorization: Bearer ...; revoked or expired key; sandbox key on production or the reverse; bad OAuth client credentials
403VERIFYME_INSUFFICIENT_SCOPEThe key lacks the scope named in message
403VERIFYME_FORBIDDENSource IP not on the key allow-list, or the document is not available (not scanned clean)
403VERIFYME_TENANT_SUSPENDEDThe workspace cannot create new requests
403VERIFYME_URL_EXPIREDA signed file URL is expired or invalid. Request a new one
404VERIFYME_NOT_FOUNDUnknown id, other workspace's id, or unknown API route
409IDEMPOTENCY_KEY_REUSEDSame Idempotency-Key, different body
409IDEMPOTENCY_IN_PROGRESSSame key is still being processed. Retry shortly
409DUPLICATE_EXTERNAL_REFERENCEWorkspace policy block_active: an active request already exists for this externalReference
409RESULT_NOT_READYResult requested before the applicant submitted
409VERIFYME_INVALID_STATEAction not allowed in the request's current state (cancel a final request, re-issue after submit)
409VERIFYME_STATE_CONFLICTConcurrent modification. Reload and retry
413VERIFYME_PAYLOAD_TOO_LARGEJSON body over the limit (256 KB)
422FLOW_NOT_FOUNDUnknown or unpublished flowKey, or no default flow
422ENDPOINT_LIMITMore than 10 webhook endpoints
429VERIFYME_RATE_LIMITEDToo many requests. Honour Retry-After
500VERIFYME_INTERNAL_ERRORUnexpected server error
503VERIFYME_DEPENDENCY_UNAVAILABLETemporary dependency failure

Validation detail codes

These appear in details[].code of VERIFYME_VALIDATION_ERROR.

CodeMeaning
REQUIREDA required field is missing or empty
REQUIRED_OR_INVALIDIdempotency-Key missing or not 8 to 120 allowed characters
INVALID_EMAIL, INVALID_MOBILEFormat checks
INVALID_URLNot an absolute URL, too long, or contains credentials
UNSAFE_SCHEMEjavascript:, data:, vbscript: or file: in a URL
HTTPS_REQUIREDHTTP URL in production (returnUrl or webhook URL)
HOST_NOT_ALLOWEDreturnUrl host not on the return URL allow-list
PRIVATE_ADDRESS_NOT_ALLOWEDWebhook URL resolves to a private or loopback address
UNKNOWN_EVENTEvent type not in the catalogue
INVALID_NUMBER, INVALID_VALUE, INVALIDOut of range, not an allowed value, or wrong type
UNSUPPORTED_GRANTOAuth grant_type is not client_credentials
INVALID_SCOPE, ENVIRONMENT_MISMATCHKey creation in the portal: unknown scope, or key environment differs from the deployment

Applicant-flow errors

These come from the hosted UI's own API. You see them in the sandbox, in the SDK error event, and in your reviewers' step history; they are not returned by /v1.

HTTPCodeMeaning
401VERIFYME_SESSION_EXPIREDApplicant session expired. Reopen the link
404VERIFYME_LINK_INVALIDLink malformed, unknown, re-issued, or used on the wrong domain
410VERIFYME_REQUEST_EXPIREDThe request expired
403VERIFYME_CSRFMissing session nonce (UI bug or tampering)
422INVALID_OTP, OTP_EXPIREDWrong or expired code
429OTP_ATTEMPTS_EXCEEDEDToo many wrong codes. Request a new one
422PAN_INVALID, BANK_ACCOUNT_INVALID, BANK_NAME_MISMATCHProvider says the document is invalid, or name mismatch where the flow blocks
422DIGILOCKER_NOT_COMPLETEDConsent denied or abandoned
422FACE_MISMATCH, FACE_VERIFICATION_FAILED, LIVENESS_FAILEDBiometric checks
422UNDERAGE, CONSENT_REQUIRED, GEO_REQUIRED, DOCUMENT_REQUIRED, MAX_FILES_REACHED, STEPS_INCOMPLETEFlow rules
422 / 413FILE_TYPE_NOT_ALLOWED, FILE_TOO_LARGE, DOCUMENT_SCAN_FAILEDUpload rejected
503PROVIDER_UNAVAILABLEA verification provider is down. Progress is saved; retryAfter is set; the attempt is not consumed
409STEP_BUSY, STEP_LOCKED, STEP_NOT_AVAILABLE, STEP_ALREADY_COMPLETED, DIGILOCKER_NOT_STARTED, NO_CONSENTStep ordering and concurrency

Rate limits

ScopeLimitOn exceed
API key or OAuth token (/v1)600 requests per minute per key (deployment default; configurable)429 VERIFYME_RATE_LIMITED, Retry-After
Failed authentication30 failures per 5 minutes per source IP401s continue; a high-severity security alert is raised
Applicant session120 requests per minute per request429
Opening links40 per minute per IP; 20 unknown tokens per 5 minutes per IP404 for bad tokens, alert on enumeration
OTP sends5 per hour per step, 10 per hour per number per workspace, 30 seconds between sends429 with Retry-After
OTP verification3 wrong attempts per codeOTP_ATTEMPTS_EXCEEDED
PAN and bank checks10 and 8 attempts per hour per request429
Sign-in to the portal10 per minute429

Headers on /v1 responses: X-RateLimit-Limit (per minute), X-RateLimit-Remaining, X-RateLimit-Reset (seconds until the window resets). If you need a higher limit, contact us with your expected peak per minute.

Payload and upload limits

ItemLimit
JSON request body on /v1256 KB (413 VERIFYME_PAYLOAD_TOO_LARGE)
OAuth token request4 KB
externalReference200 characters
applicantType60 characters
flowKey80 characters
returnUrl and webhook url500 characters
metadata4,000 bytes
expiresInHours1 to 720
Idempotency-Key8 to 120 characters; retained 24 hours
Webhook endpoints per workspace10
Allowed origins / return URL hosts20 each
Steps per flow25
Upload size (documents, selfie, geo photos)8 MB per file by default (deployment setting)
Liveness3 to 10 frames, up to 3 MB each
Files per document step3 by default (maxFiles)
File typesJPEG, PNG, WebP; PDF where the step allows it. Detected from content, not file name or extension. PDFs with active content are rejected
Signed document URLvalid 120 seconds
Webhook delivery timeout8 seconds. 8 attempts. Replay window 300 seconds

Time limits

ItemValue
Request link lifetimeDefault 72 hours; 1 to 720 hours. SUBMITTED and REVIEWING do not expire
Applicant session2 hours (deployment setting)
OAuth access token1 hour
Mobile OTP validity5 minutes. Email OTP 10 minutes
Sandbox inbox1 hour, latest 30 messages
Recent API call log (portal)latest 100 calls, 24 hours; bodies and credentials are never stored

Retry guidance

  1. Always send an Idempotency-Key on creates and reuse it for every retry of the same logical operation.
  2. Retry on network errors, timeouts, 429, 500, 502, 503 and 504. Use exponential backoff with jitter: for example 0.4 s, 0.8 s, 1.6 s, 3.2 s, each randomised by 20%, and stop after four tries.
  3. On 429, wait at least Retry-After seconds.
  4. On 409 IDEMPOTENCY_IN_PROGRESS or VERIFYME_STATE_CONFLICT, wait a second and repeat.
  5. Do not retry other 4xx responses. They will fail the same way.
  6. A timeout on create does not mean the request was not created. Retrying with the same key returns the original result rather than creating a duplicate.
  7. For webhooks it is the other side retrying: respond 2xx fast, 5xx for transient problems, and avoid 4xx for anything you want redelivered. See Webhooks.
  8. Circuit-break: if more than half of your calls fail for a minute, pause for 30 to 60 seconds before probing again.
Docs version 1.0.0API version v1Last updated 1 Oct 2026