idcheck.now API Docs

Errors

Errors use a consistent JSON envelope, machine-readable status codes, and — for verification outcomes — stable reason strings you can branch on.

Error envelope

All errors return a JSON object with a detail field. For request-validation failures (422 from body parsing) detail is an array of field-level errors; for everything else it is a human-readable string that doubles as a machine-checkable message.

JSONTypical error (403)
{
  "detail": "missing scope: verifications:write"
}
JSONValidation error (422)
{
  "detail": [
    {"loc": ["body", "name"], "msg": "ensure this value has at least 2 characters", "type": "value_error.any_str.min_length"}
  ]
}

Status codes

CodeMeaningCommon causes
400Bad requestMalformed request the router could not process.
401Unauthenticatedinvalid API key (missing/unknown/revoked X-Api-Key); invalid or expired client token (bad or 30-min-expired X-Client-Token).
403Insufficient scopeKey is valid but lacks a scope: missing scope: <scope>. Re-create the key with the scope or use *.
404Not foundUnknown id — or a resource owned by a different API key (existence is never leaked): verification not found, api key not found, webhook not found.
409State conflictcannot submit from status <status> — submit requires selfie or doc_back. Check GET /v1/session/state.
413Payload too largepayload too large/v1/signals/collect bodies are capped at 64 KB.
422UnprocessableValidation failures: invalid base64 image, consent is required to proceed, barcode could not be parsed, image_b64 required…, video_b64 or frames_b64 required, field-level validation arrays.
429Rate limitedFree tier exhausted: free tier rate limit exceeded — 30 lookups per hour. Honor the Retry-After header (seconds).
500Server errorUnexpected server-side failure. Retry with backoff; contact support if persistent.
Retries

Idempotent GETs are safe to retry on 429/5xx with exponential backoff. POST /v1/verifications creates a new resource per call — reconcile with your external_ref if you are unsure whether a create succeeded.

Decision reason strings

A decided verification carries decision_reasons — stable, snake_case strings explaining every point lost and every override applied. They are safe to switch on in code.

ReasonMeaning / remediation
barcode_parse_failedThe PDF417 payload could not be parsed as AAMVA CDS. Re-capture the back image in better light.
mandatory_elements_missingRequired AAMVA data elements were absent. Likely a worn/damaged card or unsupported encoding.
document_expiredThe document's expiry date is in the past.
license_number_recompute_failedFL/IL/WI license number did not recompute from name/DOB/sex — strong forgery indicator.
server_client_barcode_mismatchIndependent server re-decode differs from the client-supplied barcode — tamper signal.
server_barcode_decode_unavailableServer-side decode degraded (neutral: full points, but noted).
face_match_below_thresholdSelfie vs license portrait similarity < 0.6.
face_match_unavailableFace-match ML was unavailable; decision is capped at review.
liveness_below_thresholdPassive liveness score < 0.5 — possible presentation attack.
liveness_unavailableLiveness ML was unavailable; decision is capped at review.
ml_degraded_capped_at_reviewScore qualified for approve but a degraded ML signal forced review.
ofac_matchStrong OFAC SDN match → automatic decline.
ofac_possible_matchPossible OFAC match → capped at review.
ofac_screen_unavailableScreening could not run; capped at review (never fails open).
face_duplicate_different_identityThis face was previously verified under a different name → decline.
face_seen_beforeThis face appears in prior verification/fleet data → capped at review.
face_dedupe_unavailableDuplicate-face check could not run; capped at review.

The face_dedupe check can additionally report non-blocking reasons (returning_customer, below_threshold, no_matches, unavailable) in doc_checks.face_dedupe.reason; only the two above reach the decision. Scoring weights and bands are documented in the decision object.