Checks & Decisions
Every verification — widget or server-to-server — resolves to the same normalized result. This page is the reference for reading it.
The Result Payload
{
"session_id": "9f1c8e42-7c3e-4f2b-9d7a-2b1e5c8f4a10",
"user_ref": "user-123",
"status": "approved",
"auto_status": "approved",
"document": {
"type": "Passport",
"number": "A12345678",
"personal_number": null,
"issuing_state": "USA",
"issuing_country": "USA",
"issuing_country_name": "United States",
"expiration_date": "2033-09-30",
"date_of_issue": null
},
"person": {
"first_name": "ALEX",
"last_name": "MORGAN",
"full_name": "ALEX MORGAN",
"date_of_birth": "1988-03-04",
"gender": "F",
"nationality": "USA",
"address": null,
"city": null,
"region": null,
"postal_code": null
},
"checks": [
{ "name": "document_authentic", "passed": true, "score": null, "detail": "no screen-recapture signals" },
{ "name": "document_readable", "passed": true, "score": null, "detail": "all core fields extracted" },
{ "name": "mrz_valid", "passed": true, "score": null, "detail": "all check digits valid" },
{ "name": "document_not_expired", "passed": true, "score": null, "detail": "expiration_date 2033-09-30" },
{ "name": "document_number_format", "passed": true, "score": null, "detail": "passport:USA — document number matches the USA passport format" },
{ "name": "screen_pattern", "passed": true, "score": null, "detail": "screen/moiré score 0.42 vs threshold 0.8" },
{ "name": "ai_authenticity", "passed": true, "score": 95.0, "detail": "genuine document — physical document, consistent printing" },
{ "name": "face_detected", "passed": true, "score": null, "detail": "eyes_open=True sunglasses=False frontal=True" },
{ "name": "liveness", "passed": true, "score": 93.2, "detail": "heuristic (selfie mode): face confidence + quality vs threshold 80.0" },
{ "name": "face_match", "passed": true, "score": 98.7, "detail": "CompareFaces similarity vs threshold 90.0" }
],
"prefill_mismatches": [],
"prefill_comparison": [],
"selfie_description": null,
"provider": "inyo"
}
Top-Level Fields
| Field | Description |
|---|---|
session_id | The verification's identifier. Use it with GET /v1/sessions/{session_id} |
user_ref | Your identifier, echoed back exactly as you supplied it |
status | Where the verification landed — see Statuses |
auto_status | What the pipeline decided before any review routing or human decision — see Manual Review |
document | Extracted document data |
person | Extracted identity data |
checks | The individual checks that produced the decision — see Reading checks[] |
prefill_mismatches | Names of prefill fields that disagreed with the document. Empty unless you sent prefill |
prefill_comparison | Per-field comparison detail — see Prefill comparison |
selfie_description | Structured appearance description, or null unless enabled for your tenant |
provider | inyo for a real verification, inyo-mock for a simulated one — see Sandbox |
manual_review | Present only after a human decision. See Manual Review |
notified_at | Present only on delivered notifications, not on the stored result. Use it to order concurrent deliveries — see Receiving Results |
document
| Field | Description |
|---|---|
type | Passport, Driver's License, or Identity Card |
number | Document number as read from the document |
personal_number | Secondary national identifier where the document carries one |
issuing_state | Issuing authority as printed — a US state for licenses, a country code on passports |
issuing_country | Resolved ISO 3166-1 alpha-3 country |
issuing_country_name | Display name for issuing_country (for example United States). Falls back to the code itself when it is not a recognized country, and is null only when issuing_country is null |
expiration_date, date_of_issue | YYYY-MM-DD, or null when the document does not carry it |
person
first_name, last_name, full_name, date_of_birth, gender, nationality, address, city, region, postal_code.
Any field the document does not carry — or that could not be read — is null. Address fields are commonly null on passports and populated from driver's licenses. Names arrive as printed on the document, which for passports means uppercase.
Statuses
status | Meaning | What to do |
|---|---|---|
pending | Created, not yet completed | Wait. No decision exists yet |
approved | Every check passed | Proceed with onboarding |
declined | At least one hard check failed | Do not proceed. The failing check's detail explains why |
in_review | A human decides — not final | Treat as pending. See Manual Review |
expired | The customer never completed it within the link's lifetime | Create a new session if you still need the verification |
Reading checks[]
Every check is exactly four fields: name, passed, score, detail.
| Rule | Detail |
|---|---|
passed is authoritative | It is the check's outcome. Do not re-derive it from score — score and detail explain the outcome, they do not define it |
score is 0-100, higher is better | It expresses confidence that the check is satisfied. It is null whenever the check is a plain pass/fail with no comparable scalar, which is most of them — only liveness, face_match, and ai_authenticity carry one |
detail quotes the bar | Where a check has a threshold, detail names it. The liveness and face_match thresholds are per tenant, so read them here rather than assuming the platform defaults |
| The set varies | Checks appear only when applicable: mrz_valid needs a machine-readable zone, face checks need a selfie, and several are enabled per tenant. Iterate the array; do not index it positionally or assume a fixed length |
Two checks need explicit handling:
screen_pattern carries score: null deliberately. Its underlying measurement runs on an inverted 0-1 scale where higher means more screen-like, which would read as a near-failure beside the 0-100 biometrics. Publishing it as score would mislead more than inform, so the raw value and its threshold live in detail instead. Read passed.
ai_authenticity carries a score you must not gate on. The score is the model's certainty in its own verdict, oriented so higher means "more likely genuine". It is diagnostic only — Inyo deliberately excludes it from review routing. That does not make the check inert: it is a hard check, and passed: false declines the verification outright whatever the score says.
The Checks
Hard checks fail the verification — any one of them failing yields declined.
| Check | Fails when | Present |
|---|---|---|
document_authentic | A phone, monitor, or screen is visible in frame — the document is being photographed off a display | Always |
screen_pattern | Screen/moiré patterns indicate a recapture even with no device in frame | When at least one captured side could be analyzed |
ai_authenticity | Document review identifies a recapture, print, photocopy, or forgery | When enabled for your tenant (default on) |
document_readable | The core identity fields (number, name, date of birth) could not all be extracted | Always |
document_not_expired | The expiration date is in the past | When the document carries an expiration date |
jurisdiction_accepted | The document's issuing country is outside the jurisdictions you accept for that document type | When you have configured accepted jurisdictions |
face_detected | Not exactly one usable face — eyes closed, sunglasses, or a heavily turned head | When a selfie was captured |
liveness | Liveness confidence is below your threshold | When a selfie was captured |
face_match | Similarity between the document portrait and the selfie is below your threshold | When a selfie was captured |
Soft checks route the verification to in_review rather than declining it.
| Check | Fails when | Present |
|---|---|---|
mrz_valid | An MRZ check digit does not validate — an OCR error or tampering | When the document has a machine-readable zone |
document_number_format | The number does not match the known format for its jurisdiction. Advisory — the format tables are published-but-imperfect, so this never auto-declines | When a rule exists for that jurisdiction |
dmv_record_match | Extracted US license data disagrees with the issuing DMV's record | When DMV verification is enabled for your tenant (default off) |
data_match | Data you sent disagrees with the document | When you set data_check: true |
One further check appears only in the widget flow: capture_attempts fails when the customer exhausted the retry limit, which declines the session — see Widget Delivery.
Prefill Comparison
When you send prefill, the result reports how each comparable field lined up. prefill_mismatches is the short list of field names that disagreed; prefill_comparison carries the detail:
"prefill_comparison": [
{ "field": "last_name", "sent": "Silva", "received": "SILVA SANTOS", "match": true, "method": "subset", "score": 0.95, "threshold": 0.85 },
{ "field": "date_of_birth", "sent": "1988-03-04", "received": "1988-03-04", "match": true, "method": "exact", "score": null, "threshold": null },
{ "field": "issuing_state", "sent": "Florida", "received": "FL", "match": true, "method": "normalized", "score": null, "threshold": null }
]
| Field | Description |
|---|---|
field | One of first_name, last_name, date_of_birth, document_type, document_number, nationality, issuing_state |
sent / received | Your value, and the value read from the document |
match | Whether they are considered equal |
method | For names: exact, subset (a multi-part surname containing yours, scored 0.95), fuzzy (similarity scoring), or empty. For states and document types: normalized. Otherwise exact |
score / threshold | Name similarity and the acceptance bar, null for non-name fields |
Fields absent on either side are not compared, so they never appear. Names use fuzzy matching — accents stripped, multi-part surnames accepted as subset matches, OCR near-misses tolerated up to the threshold — so ordinary spelling variance does not create false mismatches. States and document types are normalized first, which is why Florida matches FL.
A mismatch only affects the decision when you set data_check: true, which adds the data_match soft check. Without it, the comparison is informational.
Selfie Description
null unless the appearance description is enabled for your tenant. When present, it carries observable attributes of the selfie for later re-verification comparison:
"selfie_description": {
"estimated_age_low": 30,
"estimated_age_high": 40,
"skin_tone": "light",
"eye_color": "brown",
"hair_color": "dark brown",
"hair_style": "shoulder length, straight",
"facial_hair": "none",
"eyewear": "none",
"headwear": "none",
"distinguishing_features": ["small scar above left eyebrow"],
"notes": null
}
Any attribute that cannot be observed is null. These are sensitive personal data — see the compliance note under Tenant Configuration before enabling it.
Score Convention Cutover
Results created before the current score convention shipped differ in two ways: screen_pattern.score carried a raw 0-1 moiré likelihood (where higher meant worse), and ai_authenticity.score used an undefined scale. Post-cutover rows are identifiable without an external reference — screen_pattern.score is null, and no check carries any field beyond the four documented above. If you store historical results, key your reading on that.
Next Steps
- Manual Review —
in_review,auto_status, andmanual_review - Tenant Configuration — the thresholds these checks compare against
- Receiving Results — how this payload reaches you
