Inyo

Verifying Documents

To advance a sender to higher compliance levels (Level 2 and above), their identity documents must be verified. There are two ways to get documents verified:

  • KYC Verification Session (recommended) β€” create a session and hand your user a hosted widget URL. The widget captures the document photos and a selfie, verifies them, and the result flows back to the sender's record automatically.
  • Direct document upload (deprecated) β€” POST document image files yourself and wait for OCR/manual review.

⚠️ The direct upload endpoints are deprecated. New integrations should use KYC Verification Sessions. Existing upload integrations keep working, but the session flow adds selfie liveness, document-portrait face matching, and presentation-attack defense that plain file uploads cannot provide.


Endpoint: POST /organizations/{tenant}/v2/people/{personId}/kycSession
Authentication: Tenant-level (x-api-key)

Creates an Inyo360 Identity Verification session for the person. You supply only widget-presentation parameters β€” the document to verify is resolved automatically from the person's declared documents, and the session is prefilled from the person record (name, date of birth, document number, issuing state, nationality), so the user only has to capture their document and selfie.

Declare the document first. The person must have a non-expired, KYC-verifiable document on file (added via POST /v2/people documents[]) before you can open a session β€” otherwise the request returns 422 NO_VERIFIABLE_DOCUMENT.

Request Body

Both fields are required:

FieldTypeRequiredDescription
languagestringYesWidget language, xx or xx-XX (e.g. en, es, pt-BR)
redirectUrlstring (URL)YesWhere to send the user after they finish the widget flow
curl --request POST \
  --url https://{FQDN}/organizations/$TENANT/v2/people/$PERSON_ID/kycSession \
  --header 'Content-Type: application/json' \
  --header "x-api-key: $API_KEY" \
  --data '{
  "language": "en",
  "redirectUrl": "https://your-app.example/kyc/done"
}'

How the Document Is Selected

Inyo walks the person's declared documents in priority order and verifies the first eligible one:

  1. drivers_license
  2. passport
  3. The identity-card family, most-specific first: dni, cc, cpf, consular_id, voter_id, id

Documents with an expiration date in the past are skipped (a person with an expired driver's license and a valid passport gets the passport); documents without an expiration date on file are trusted. ssn, itin, and other are never selected β€” they don't map to the KYC document vocabulary.

Data cross-checking is always on: the KYC layer compares the data extracted from the captured document against the prefilled person record.

Response (201):

{
  "sessionId": "9f1c8e42-7c3e-4f2b-9d7a-2b1e5c8f4a10",
  "status": "pending",
  "widgetUrl": "https://{FQDN}/verify/8Kd2mQ..."
}

The Flow

  1. Create the session and open widgetUrl for your user (link, redirect, or webview β€” see Widget Delivery).
  2. The user photographs their document and takes a selfie in the widget.
  3. Inyo receives the signed verification result directly β€” you don't handle any document images.
  4. On a verified result, the person's document records are created/updated automatically with the verification verdict, and the DocumentUpdatedEvents webhook fires.
  5. Check the sender's new level via GET /participants/{id}/complianceLevels.

Sessions move through these states on the Inyo side: PENDING β†’ VERIFIED, REJECTED, PENDING_REVIEW (manual review at the KYC layer), or EXPIRED.

Errors

HTTPErrorCause
404NOT_FOUNDPerson not found in your tenant
422NO_VERIFIABLE_DOCUMENTThe person has no non-expired, KYC-verifiable document on file β€” declare one via POST /v2/people (documents[]) first
422VALIDATION_ERRORlanguage or redirectUrl missing/malformed
502KYC_UPSTREAM_ERRORThe KYC service is temporarily unavailable β€” retry the request

Legacy: Direct Document Uploads (Deprecated)

⚠️ Deprecated. Use KYC Verification Sessions instead. These endpoints remain available for existing integrations. Uploaded documents are verified by AI OCR (where enabled for your tenant) or by the Inyo compliance team.


Identity Documents

Endpoint: POST /organizations/{tenant}/people/{personId}/documents/documentId/{subtype}/upload
Authentication: Tenant-level (x-api-key)
Content-Type: multipart/form-data

The {subtype} path segment is the document type. Both wire format (passport, driversLicense, ...) and canonical uppercase (PASSPORT, DRIVER_LICENSE, ...) are accepted. The full set:

Wire valueCanonical value(s)Description
passportPASSPORTPassport
driversLicenseDRIVER_LICENSEDriver's license
nationalIdDNI, CC, IDNational ID β€” DNI (Spain/Argentina/Peru), CC (Colombia cΓ©dula), or generic government ID
cpfCPFCPF (Brazil)
consularIdCONSULAR_IDConsular ID (matrΓ­cula consular)
voterIdVOTER_IDVoter ID
ssnSSNUS Social Security card
itinITINUS ITIN (IRS)
otherOTHEROther government-issued document

The three national-ID variants (DNI, CC, ID) collapse to the single wire value nationalId, so clients don't have to handle country-specific naming.

An unknown subtype returns 422 INVALID_SUBTYPE with the allowed list in the response. PROOF_OF_FUNDS is deliberately not accepted here β€” use the dedicated source of funds endpoint below.

Form Fields

FieldTypeRequiredDescription
filefileYesThe document image β€” pdf, jpg, jpeg, or png, max 10 MB
idNumberstringNoThe document number printed on the document
issuerstringNoIssuing authority or state
expirationDatestringNoFormat YYYY-MM-DD
curl --request POST \
  --url https://{FQDN}/organizations/$TENANT/people/$PERSON_ID/documents/documentId/PASSPORT/upload \
  --header "x-api-key: $API_KEY" \
  --form "file=@/path/to/passport.jpg" \
  --form "idNumber=AB1234567" \
  --form "expirationDate=2030-12-31"

Response (201):

{
  "id": "4ec66735-216b-4ab4-b1d7-00558baa6d85",
  "subtype": "PASSPORT",
  "fileName": "passport.jpg",
  "verificationStatus": "PENDING",
  "createdAt": "2026-08-04T12:00:00+00:00",
  "idNumber": "AB1234567",
  "issuer": null,
  "expirationDate": "2030-12-31"
}

Source of Funds

Proof of the sender's source of funds (bank statement, pay stub), required for higher compliance tiers.

Endpoint: POST /organizations/{tenant}/people/{personId}/documents/sourceOfFunds/upload
Authentication: Tenant-level (x-api-key)
Content-Type: multipart/form-data

curl --request POST \
  --url https://{FQDN}/organizations/$TENANT/people/$PERSON_ID/documents/sourceOfFunds/upload \
  --header "x-api-key: $API_KEY" \
  --form "file=@/path/to/bank-statement.pdf"

Same file rules as identity documents (pdf/jpg/jpeg/png, max 10 MB). The upload is stored under the PROOF_OF_FUNDS type.


Document Verification Status

After uploading, documents are verified asynchronously β€” by AI OCR (if enabled for your tenant) within minutes, otherwise by the compliance team.

Get Current Verification Status

Endpoint: GET /organizations/{tenant}/documents/{documentId}/verificationStatus/current
Authentication: Tenant-level

curl --request GET \
  --url https://{FQDN}/organizations/$TENANT/documents/$DOCUMENT_ID/verificationStatus/current \
  --header "x-api-key: $API_KEY"

Get Verification Status History

Endpoint: GET /organizations/{tenant}/documents/{documentId}/verificationStatus
Authentication: Tenant-level

curl --request GET \
  --url https://{FQDN}/organizations/$TENANT/documents/$DOCUMENT_ID/verificationStatus \
  --header "x-api-key: $API_KEY"

Returns entries with status, reason, verifiedBy, and createdAt β€” the reason field explains rejections so you can guide the user to re-upload.

List a Person's Documents

Endpoint: GET /organizations/{tenant}/people/{personId}/documents
Authentication: Tenant-level

curl --request GET \
  --url https://{FQDN}/organizations/$TENANT/people/$PERSON_ID/documents \
  --header "x-api-key: $API_KEY"

Verification Statuses

StatusDescription
PENDINGDocument uploaded, awaiting verification
VERIFIEDDocument accepted β€” compliance level may upgrade
REJECTEDDocument rejected β€” check the reason and re-upload

Webhook Notifications

Register for the DocumentUpdatedEvents webhook to be notified when a document's verification status changes β€” one event on upload (PENDING) and another when verification completes (VERIFIED/REJECTED). See Webhooks for the payload reference.


Best Practices

  • Upload high-quality images β€” blurry or cropped images will be rejected.
  • Pass idNumber and expirationDate when you have them β€” they enrich the compliance record and speed up review.
  • Check compliance level after verification β€” once a document is VERIFIED, call GET /participants/{id}/complianceLevels to see if the sender has been upgraded.
  • Handle rejections gracefully β€” surface the rejection reason and prompt the user to re-upload.
  • Use webhooks instead of polling β€” register for DocumentUpdatedEvents to be notified immediately.

Test Data

For sandbox testing, see Test Data and Sandbox Testing β€” certain name/address combinations trigger specific compliance behaviors (approval, hold, rejection).