Inyo

Verification Sessions

A session represents one identity verification. Creating it returns a widgetUrl you deliver to your customer, and a sessionId 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 '{
  "userRef": "user-123",
  "language": "es",
  "delivery": { "mode": "webhook" }
}'
{
  "sessionId": "9f1c8e42-7c3e-4f2b-9d7a-2b1e5c8f4a10",
  "status": "pending",
  "widgetUrl": "https://{FQDN}/verify/8Kd2mQ…"
}

Request Fields

FieldTypeRequiredDescription
userRefstring (1-255)YesYour identifier for the person being verified. Echoed back in every result
prefillobjectNoKnown data about the person β€” see Prefill
dataCheckbooleanNofalse (default) verifies the document as presented. true additionally compares extracted data against prefill β€” see Data Checks
deliveryobjectNoHow the result reaches you. Defaults to {"mode": "webhook"}
languagestringNoWidget language, xx or xx-XX (e.g. en, pt, es, pt-BR). Falls back to your configured default
maxCaptureAttemptsinteger (1-10)NoOverrides your tenant default for this session only

Response Fields

FieldDescription
sessionIdUse this to correlate results and to call GET /v1/sessions/{sessionId}
statusAlways pending on creation
widgetUrlThe 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.

FieldEffect
documentTypepassport, 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
documentNumberValidated for format at this call (see below), and available for cross-checking
issuingStateUS state code or name for driver's licenses; ISO 3166-1 alpha-3 country for passports and identity cards. Sharpens format validation
nationalityISO 3166-1 alpha-3. Used to resolve the jurisdiction when issuingState is absent
firstName, lastNameAvailable for cross-checking
dateOfBirthYYYY-MM-DD. Available for cross-checking
{
  "userRef": "user-123",
  "prefill": {
    "documentType": "drivers_license",
    "issuingState": "PA",
    "documentNumber": "31612967",
    "firstName": "Ana",
    "lastName": "Silva",
    "dateOfBirth": "1988-03-04"
  }
}

Prefill fields other than documentType do not change the decision unless you set dataCheck: true. Without it, the values are carried for comparison and reported in the result, but a mismatch does not route the session anywhere.

prefill.documentNumber 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 dataCheck: true to verify that the document belongs to the person you expected β€” not just that the document is genuine.

{
  "userRef": "user-123",
  "dataCheck": true,
  "prefill": {
    "firstName": "Ana",
    "lastName": "Silva",
    "dateOfBirth": "1988-03-04"
  }
}

With dataCheck: true:

  • Extracted data is compared field by field against the prefill payload.
  • The result carries prefillComparison (per-field outcomes, including fuzzy name-match scores) and prefillMismatches (the field names that disagreed).
  • A mismatch adds a failing soft check, routing the session to in_review rather 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. dataCheck: true without a prefill payload is a 422 β€” there would be nothing to compare against.


Delivery

ModeFieldsBehavior
webhook (default)β€”The result is POSTed to your configured webhookUrl
redirectredirectUrl (required)The customer is returned to your URL with the outcome and a signature
{ "delivery": { "mode": "redirect", "redirectUrl": "https://you.example/kyc-done" } }

Requesting webhook mode without a webhookUrl 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:

CauseNotes
delivery.mode is webhook but no webhookUrl is configuredAsk Inyo to register your endpoint, or use redirect mode
delivery.redirectUrl missing in redirect modeRequired whenever mode is redirect
documentType is not enabled for your tenantThe message lists the document types you may request
The document's issuing jurisdiction is not accepted for that document typeThe message lists the jurisdictions you accept
prefill.documentNumber failed format validationThe message names the rule that rejected it
dataCheck: true with no prefill payloadProvide at least one comparable prefill field

Authentication and authorization failures return 401 or 403 β€” see Authentication.


Retrieve a Session

Endpoint: GET /v1/sessions/{sessionId}
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"
{
  "sessionId": "9f1c8e42-7c3e-4f2b-9d7a-2b1e5c8f4a10",
  "userRef": "user-123",
  "status": "in_review",
  "step": "done",
  "deliveryMode": "webhook",
  "result": { "…": "the normalized result, updated in place" },
  "createdAt": "2026-07-31T14:02:11.481Z",
  "updatedAt": "2026-07-31T14:04:57.902Z"
}
FieldDescription
statuspending, approved, declined, in_review, or expired
stepHow far the customer got: document_front, document_back, selfie, or done
deliveryModewebhook, redirect, or sync (a server-to-server verification)
resultThe full normalized result once available, null before then. Updated in place when an analyst decides

Sessions are strictly tenant-scoped. Another tenant's sessionId returns 404 β€” never a partial disclosure.


Next Steps