Verifications
A verification is one identity-check session for one end user. Create it on your
server, hand the client token or hosted URL to the user's browser, then read the decision. All
endpoints are merchant-authenticated with X-Api-Key.
Create a verification
To create a verification, POST /v1/verifications with an optional
external_ref (your user's id), an optional free-form user object, and an
optional redirect_url the hosted flow returns to when finished. The API creates the
verification in status created (expires in 24 hours) and issues a 30-minute
client_token for the hosted flow.
Request body
| Field | Type | Description |
|---|---|---|
| external_refoptional | string | null | Your idempotency/reconciliation handle. Stored verbatim and returned on every read. |
| useroptional | object | Free-form metadata about your user (e.g. email, internal id). Defaults to {}. |
| redirect_urloptional | string | null | URL the hosted flow navigates to after completion (used by redirect-style integrations). |
curl -X POST https://idcheck.now/v1/verifications -H "X-Api-Key: $IDCHECK_API_KEY" -H "Content-Type: application/json" -d '{"external_ref": "user_4821", "user": {"email": "jane@example.com"},
"redirect_url": "https://yourapp.example/kyc/done"}'const res = await fetch("https://idcheck.now/v1/verifications", {
method: "POST",
headers: { "X-Api-Key": process.env.IDCHECK_API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({
external_ref: "user_4821",
user: { email: "jane@example.com" },
redirect_url: "https://yourapp.example/kyc/done",
}),
});
const { id, client_token, url } = await res.json();import os, requests
res = requests.post(
"https://idcheck.now/v1/verifications",
headers={"X-Api-Key": os.environ["IDCHECK_API_KEY"]},
json={
"external_ref": "user_4821",
"user": {"email": "jane@example.com"},
"redirect_url": "https://yourapp.example/kyc/done",
},
)
data = res.json() # {"id": ..., "client_token": ..., "url": ...}201 Created — response body
| Field | Type | Description |
|---|---|---|
| idrequired | uuid | Verification id. Use it for GET calls and to reconcile webhooks. |
| client_tokenrequired | string | 30-minute token for the hosted flow (X-Client-Token). Deliver it to the browser. |
| urlrequired | string | Fully-built hosted-flow URL ({HOSTED_BASE_URL}/verify/{id}?token=…). Redirect or link the user here. |
{
"id": "b3f1c2a4-7d5e-4f6a-9c8b-1e2d3a4b5c6d",
"client_token": "x7Qp9m2LwR4tY8uZcVbN1sDfGhJkL0oP3eA5qW6rT2y",
"url": "https://idcheck.now/verify/b3f1c2a4-7d5e-4f6a-9c8b-1e2d3a4b5c6d?token=x7Qp9m2LwR4tY8uZcVbN1sDfGhJkL0oP3eA5qW6rT2y"
}The client token expires after 30 minutes while the verification itself lives 24 hours. Do not store the hosted url: each time you send the user to the flow, mint a fresh one with POST /v1/verifications/{{id}}/client-token.
Issue a new client token
To send a returning user back into an in-flight verification, POST
/v1/verifications/{verification_id}/client-token. It returns a new 30-minute
client_token and hosted url; the user resumes where they left off. Returns
409 once the verification is submitted, processing,
decided or past expires_at — create a new verification instead.
curl -X POST https://idcheck.now/v1/verifications/b3f1c2a4-7d5e-4f6a-9c8b-1e2d3a4b5c6d/client-token -H "X-Api-Key: $IDCHECK_API_KEY"{
"id": "b3f1c2a4-7d5e-4f6a-9c8b-1e2d3a4b5c6d",
"client_token": "Vn3kR8pQ1wZ6yT4uXcB2mL9sDfHjG0aE5oI7rW1qN2e",
"url": "https://idcheck.now/verify/b3f1c2a4-7d5e-4f6a-9c8b-1e2d3a4b5c6d?token=Vn3kR8pQ1wZ6yT4uXcB2mL9sDfHjG0aE5oI7rW1qN2e"
}List verifications
To list your verifications, GET /v1/verifications. Results are scoped to the
calling API key, newest first. Use status to filter by lifecycle state.
Query parameters
| Field | Type | Description |
|---|---|---|
| limitoptional | integer (query) | Page size, 1–200. Default 50. |
| offsetoptional | integer (query) | Rows to skip. Default 0. |
| statusoptional | string (query) | Filter by exact status, e.g. decided. |
curl "https://idcheck.now/v1/verifications?status=decided&limit=25" -H "X-Api-Key: $IDCHECK_API_KEY"const res = await fetch("https://idcheck.now/v1/verifications?status=decided&limit=25", {
headers: { "X-Api-Key": process.env.IDCHECK_API_KEY },
});
const { total, items } = await res.json();import os, requests
res = requests.get(
"https://idcheck.now/v1/verifications",
headers={"X-Api-Key": os.environ["IDCHECK_API_KEY"]},
params={"status": "decided", "limit": 25},
)
page = res.json() # {"total", "limit", "offset", "items": [...]}{
"total": 137,
"limit": 25,
"offset": 0,
"items": [
{
"id": "b3f1c2a4-7d5e-4f6a-9c8b-1e2d3a4b5c6d",
"external_ref": "user_4821",
"status": "decided",
"decision": "approve",
"score": 96.2,
"created_at": "2026-07-26T14:03:11.204512",
"expires_at": "2026-07-27T14:03:11.204512"
}
]
}Retrieve a verification
To retrieve a verification — including the full decision, every document check, the consent
record and the hash-chained audit log — GET /v1/verifications/{verification_id}.
Verifications belonging to a different API key return 404 verification not found
(never 403 — existence is not leaked).
curl https://idcheck.now/v1/verifications/b3f1c2a4-7d5e-4f6a-9c8b-1e2d3a4b5c6d -H "X-Api-Key: $IDCHECK_API_KEY"const res = await fetch(
"https://idcheck.now/v1/verifications/b3f1c2a4-7d5e-4f6a-9c8b-1e2d3a4b5c6d",
{ headers: { "X-Api-Key": process.env.IDCHECK_API_KEY } }
);
const verification = await res.json();import os, requests
res = requests.get(
"https://idcheck.now/v1/verifications/b3f1c2a4-7d5e-4f6a-9c8b-1e2d3a4b5c6d",
headers={"X-Api-Key": os.environ["IDCHECK_API_KEY"]},
)
v = res.json()
print(v["decision"], v["score"], v["decision_reasons"]){
"id": "b3f1c2a4-7d5e-4f6a-9c8b-1e2d3a4b5c6d",
"external_ref": "user_4821",
"status": "decided",
"decision": "approve",
"score": 96.2,
"created_at": "2026-07-26T14:03:11.204512",
"expires_at": "2026-07-27T14:03:11.204512",
"decision_reasons": [],
"checks": {
"doc_checks": {
"mandatory_elements": {"passed": true, "missing": []},
"not_expired": {"passed": true, "expiry": "2029-04-17"},
"license_recompute": {"passed": true, "skipped": "state_not_supported_or_no_dob"},
"server_client_barcode_match": {"passed": true},
"ofac": {"matched": false, "confidence": "none", "score": 0.412},
"face_dedupe": {"matches": [], "impact": null, "reason": "no_matches"}
},
"face_match_score": 0.91,
"liveness_score": 0.88,
"license_fields": {
"last_name": "DOE",
"first_name": "JANE",
"middle_name": null,
"date_of_birth": "1990-06-15",
"age": 36,
"expiry": "2029-04-17",
"license_number": "D1234567",
"street": "123 MAIN ST",
"city": "PITTSBURGH",
"state": "PA",
"zip": "15201",
"sex": "female"
}
},
"consent": {
"bipa": true,
"recording": true,
"policy_version": "2026-07-01",
"captured_at": "2026-07-26T14:04:02.117300",
"ip": "203.0.113.44",
"user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 19_0 like Mac OS X)"
},
"events": [
{
"seq": 1,
"actor": "merchant",
"action": "session.created",
"payload": {"external_ref": "user_4821"},
"ip": "198.51.100.9",
"event_hash": "b7f2a1c9d4e3f6a5b8c7d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1",
"created_at": "2026-07-26T14:03:11.210044"
}
]
}Beyond the summary fields, the response adds decision_reasons, a
checks object (doc_checks, face_match_score,
liveness_score, license_fields), the consent record, and
events — the ordered, hash-chained audit trail. Every check id in
doc_checks is explained in the decision
object reference.
Get verification media
To download the captured images, GET /v1/verifications/{id}/media. The media
bucket is private — the only access path is the short-lived presigned URLs returned here. Links
expire after 3600 seconds (1 hour); call the endpoint again for fresh links.
Every issuance is audit-logged as media.links_issued. In local-fallback deployments
without S3, url and expires_in are null.
curl https://idcheck.now/v1/verifications/b3f1c2a4-7d5e-4f6a-9c8b-1e2d3a4b5c6d/media -H "X-Api-Key: $IDCHECK_API_KEY"const res = await fetch(
"https://idcheck.now/v1/verifications/b3f1c2a4-7d5e-4f6a-9c8b-1e2d3a4b5c6d/media",
{ headers: { "X-Api-Key": process.env.IDCHECK_API_KEY } }
);
const { media } = await res.json();
for (const [side, info] of Object.entries(media)) console.log(side, info.url);import os, requests
res = requests.get(
"https://idcheck.now/v1/verifications/b3f1c2a4-7d5e-4f6a-9c8b-1e2d3a4b5c6d/media",
headers={"X-Api-Key": os.environ["IDCHECK_API_KEY"]},
)
for side, info in res.json()["media"].items():
print(side, info["url"], info["sha256"])200 OK — response body
| Field | Type | Description |
|---|---|---|
| mediarequired | object | Map of media side → object. Keys: front, back, selfie, selfie_frame_0…selfie_frame_7 (whichever were captured). |
| media.<side>.keyrequired | string | Storage object key. |
| media.<side>.sha256required | string | SHA-256 of the stored bytes — verify downloads against it. |
| media.<side>.urlrequired | string | null | Presigned GET URL (SigV4). Null when storage is in local-fallback mode. |
| media.<side>.expires_inrequired | integer | null | Seconds the URL remains valid (3600). |
{
"media": {
"front": {
"key": "b3f1c2a4-7d5e-4f6a-9c8b-1e2d3a4b5c6d/front-9ab3c4d5e6f70819.jpg",
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"url": "https://s3.us-east-1.amazonaws.com/idcheck-media/b3f1c2a4-.../front-9ab3c4d5....jpg?X-Amz-Signature=…",
"expires_in": 3600
},
"selfie": {
"key": "b3f1c2a4-7d5e-4f6a-9c8b-1e2d3a4b5c6d/selfie-1a2b3c4d5e6f7788.mp4",
"sha256": "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae",
"url": "https://s3.us-east-1.amazonaws.com/idcheck-media/b3f1c2a4-.../selfie-1a2b3c4d....mp4?X-Amz-Signature=…",
"expires_in": 3600
}
}
}The verification object
Summary shape returned by the list endpoint and embedded in every verification response:
Verification summary fields
| Field | Type | Description |
|---|---|---|
| idrequired | uuid | Verification identifier. |
| external_refoptional | string | null | Your own reference, echoed back. |
| statusrequired | string | Lifecycle state: created, consented, doc_front, doc_back, selfie, submitted, processing, decided. |
| decisionrequired | string | null | approve | review | decline; null until decided. |
| scorerequired | number | null | Composite score 0–100. Bands: ≥ 80 approve, 50–79 review, < 50 decline. |
| created_atrequired | ISO 8601 | Creation timestamp. |
| expires_atrequired | ISO 8601 | Creation + 24 hours. |
The decision fields (decision, score, decision_reasons)
are populated when the user submits and the decision engine runs — see
the decision object for scoring, bands and every
reason string.