Verification Sessions
A session represents one identity verification. Creating it returns a widget_url you deliver to your customer, and a session_id you use to correlate the result.
Create a Session
Endpoint: POST /v1/sessions
Authentication: Bearer token with the sessions scope
curl --request POST \
--url https://{FQDN}/v1/sessions \
--header "Authorization: Bearer $ACCESS_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"user_ref": "user-123",
"language": "es",
"delivery": { "mode": "webhook" }
}'
{
"session_id": "9f1c8e42-7c3e-4f2b-9d7a-2b1e5c8f4a10",
"status": "pending",
"widget_url": "https://{FQDN}/verify/8Kd2mQβ¦"
}
Request Fields
| Field | Type | Required | Description |
|---|---|---|---|
user_ref | string (1-255) | Yes | Your identifier for the person being verified. Echoed back in every result |
prefill | object | No | Known data about the person β see Prefill |
data_check | boolean | No | false (default) verifies the document as presented. true additionally compares extracted data against prefill β see Data Checks |
delivery | object | No | How the result reaches you. Defaults to {"mode": "webhook"} |
language | string | No | Widget language, xx or xx-XX (e.g. en, pt, es, pt-BR). Falls back to your configured default |
max_capture_attempts | integer (1-10) | No | Overrides your tenant default for this session only |
Response Fields
| Field | Description |
|---|---|
session_id | Use this to correlate results and to call GET /v1/sessions/{session_id} |
status | Always pending on creation |
widget_url | The customer-facing link. Contains a single-use code valid for 48 hours |
Prefill
prefill carries what you already know about the person. It has two distinct effects.
| Field | Effect |
|---|---|
document_type | passport, drivers_license, or identity_card. Locks the widget to this document β the type-selection screen is skipped, and a different document is rejected. Omit it to let the customer choose |
document_number | Validated for format at this call (see below), and available for cross-checking |
issuing_state | US state code or name for driver's licenses; ISO 3166-1 alpha-3 country for passports and identity cards. Sharpens format validation |
nationality | ISO 3166-1 alpha-3. Used to resolve the jurisdiction when issuing_state is absent |
first_name, last_name | Available for cross-checking |
date_of_birth | YYYY-MM-DD. Available for cross-checking |
{
"user_ref": "user-123",
"prefill": {
"document_type": "drivers_license",
"issuing_state": "PA",
"document_number": "31612967",
"first_name": "Ana",
"last_name": "Silva",
"date_of_birth": "1988-03-04"
}
}
Prefill fields other than document_type do not change the decision unless you set data_check: true. Without it, the values are carried for comparison and reported in the result, but a mismatch does not route the session anywhere.
prefill.document_number is validated at this call, however: a number that violates its jurisdiction's known format is rejected with 422 rather than accepted and failed later. Check a number before you get here with the validator endpoint.
Data Checks
Set data_check: true to verify that the document belongs to the person you expected β not just that the document is genuine.
{
"user_ref": "user-123",
"data_check": true,
"prefill": {
"first_name": "Ana",
"last_name": "Silva",
"date_of_birth": "1988-03-04"
}
}
With data_check: true:
- Extracted data is compared field by field against the prefill payload.
- The result carries
prefill_comparison(per-field outcomes, including fuzzy name-match scores) andprefill_mismatches(the field names that disagreed). - A mismatch adds a failing soft check, routing the session to
in_reviewrather than declining it.
Names are compared with fuzzy matching against a configurable similarity threshold, so ordinary spelling and transliteration variance does not create false mismatches. data_check: true without a prefill payload is a 422 β there would be nothing to compare against.
Delivery
| Mode | Fields | Behavior |
|---|---|---|
webhook (default) | β | The result is POSTed to your configured webhook_url |
redirect | redirect_url (required) | The customer is returned to your URL with the outcome and a signature |
{ "delivery": { "mode": "redirect", "redirect_url": "https://you.example/kyc-done" } }
Requesting webhook mode without a webhook_url configured for your tenant is a 422. See Receiving Results for signature verification in both modes.
Error Responses
All of these return 422 with a detail explaining the specific cause:
| Cause | Notes |
|---|---|
delivery.mode is webhook but no webhook_url is configured | Ask Inyo to register your endpoint, or use redirect mode |
delivery.redirect_url missing in redirect mode | Required whenever mode is redirect |
document_type is not enabled for your tenant | The message lists the document types you may request |
| The document's issuing jurisdiction is not accepted for that document type | The message lists the jurisdictions you accept |
prefill.document_number failed format validation | The message names the rule that rejected it |
data_check: true with no prefill payload | Provide at least one comparable prefill field |
Authentication and authorization failures return 401 or 403 β see Authentication.
Retrieve a Session
Endpoint: GET /v1/sessions/{session_id}
Authentication: Bearer token with the sessions scope
This is the authoritative record of a verification. Poll it when you need a guarantee rather than a push, to reconcile a webhook you may have missed, or to read the outcome of a session that went to manual review.
curl --request GET \
--url https://{FQDN}/v1/sessions/$SESSION_ID \
--header "Authorization: Bearer $ACCESS_TOKEN"
{
"session_id": "9f1c8e42-7c3e-4f2b-9d7a-2b1e5c8f4a10",
"user_ref": "user-123",
"status": "in_review",
"step": "done",
"delivery_mode": "webhook",
"result": { "β¦": "the normalized result, updated in place" },
"created_at": "2026-07-31T14:02:11.481Z",
"updated_at": "2026-07-31T14:04:57.902Z"
}
| Field | Description |
|---|---|
status | pending, approved, declined, in_review, or expired |
step | How far the customer got: document_front, document_back, selfie, or done |
delivery_mode | webhook, redirect, or sync (a server-to-server verification) |
result | The full normalized result once available, null before then. Updated in place when an analyst decides |
Sessions are strictly tenant-scoped. Another tenant's session_id returns 404 β never a partial disclosure.
Validate a Document Number
Endpoint: POST /v1/validators/document-number
Authentication: none required
A stateless format check against the same registry the verification pipeline uses: US driver's-license formats for all 50 states and DC, per-country passport formats, and identity-card formats where the jurisdiction defines one. Nothing is stored and no verification is consumed. Useful for validating input in your own form before creating a session.
curl --request POST \
--url https://{FQDN}/v1/validators/document-number \
--header 'Content-Type: application/json' \
--data '{
"document_type": "drivers_license",
"number": "31612967",
"issuing_state": "PA"
}'
{
"document_type": "drivers_license",
"number": "31612967",
"issuing_state": "PA",
"valid": true,
"rule": "us_dl:PA",
"detail": "document number matches the PA license format"
}
valid is deliberately three-valued:
| Value | Meaning | How to treat it |
|---|---|---|
true | Matches the known format for that jurisdiction | Accept |
false | Violates the known format | Reject before creating the session |
null | No rule exists for that jurisdiction | Accept β an unknown jurisdiction is never a failure |
rule identifies which rule was applied β us_dl:PA, passport:USA, passport:generic, or us_dl:unknown_state when a license was submitted without a resolvable state (which yields valid: null). issuing_state is a US state code or name for driver's licenses, and an ISO 3166-1 alpha-3 country code for passports and identity cards.
Next Steps
- Widget Delivery β getting the customer through the flow
- Receiving Results β webhook and redirect handling
- Tenant Configuration β which document types and jurisdictions you accept
