Skip to main content

Unstructured document verification

End-to-end integration guide for IBAN Verification and Proof of Address steps

This guide covers integrating flows that include an IBAN_VERIFICATION:v1 or PROOF_OF_ADDRESS:v1 step. Both are headless steps — your application collects the document image and submits it to IDnow via API. IDnow performs verification and extracts the relevant data block.


Overview

StepInputOutput (verified route)
IBAN_VERIFICATION:v1DocumentImagesFundingSource, Verification
PROOF_OF_ADDRESS:v1DocumentImagesResidentialAddress, Verification

Both steps support an optional compareIdentity flag. When enabled, a BasicIdentity data block from a prior step is used to cross-check the document holder's identity.


Step 1 — Upload the document image to the Vault

Before creating a session, upload the document image using the Vault upload endpoint. This returns a Vault reference ({ "$ref": "vault", "$id": "<uuid>" }) that you include in the session payload.

curl --request POST https://localhost:3000/api/v1/vault/files \
--header "Authorization: Bearer YOUR_API_KEY" \
--header "Content-Type: image/jpeg" \
--data-binary @document-front.jpg
{
"$ref": "vault",
"$id": "4d293362-9738-4a37-8d74-eb3acbc35749"
}

Repeat for the back side if required by your document type.


Step 2 — Create a session

Pass the Vault reference(s) as documentImages in the session input.

curl --request POST https://localhost:3000/api/v1/flows/{flowId}/live/sessions \
--header "Authorization: Bearer YOUR_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"input": {
"documentImages": {
"documentType": "IBAN",
"frontSide": { "$ref": "vault", "$id": "4d293362-9738-4a37-8d74-eb3acbc35749" }
}
},
"metadata": {
"subjectId": "your-internal-subject-id",
"locale": "en"
}
}'

For a PROOF_OF_ADDRESS:v1 step, use "documentType": "PROOF_OF_ADDRESS". For flows with compareIdentity: true, also include basicIdentity in the input:

{
"input": {
"documentImages": {
"documentType": "PROOF_OF_ADDRESS",
"frontSide": { "$ref": "vault", "$id": "4d293362-9738-4a37-8d74-eb3acbc35749" }
},
"basicIdentity": {
"familyName": "Schmidt",
"givenName": "Max",
"birthDate": "1985-08-22"
}
}
}

Step 3 — Poll for session completion

Poll the session endpoint until sessionStatus is COMPLETED, ERROR, ABORTED, or EXPIRED.

curl https://localhost:3000/api/v1/live/sessions/{sessionId} \
--header "Authorization: Bearer YOUR_API_KEY"

Verification typically completes within a few seconds. Use an exponential back-off with a reasonable timeout (e.g., poll every 2 s for the first 30 s, then every 10 s up to 5 minutes).


Step 4 — Read the results

When sessionStatus is COMPLETED, the results field in the session response contains references to the output data blocks.

IBAN Verification — verified session:

{
"sessionId": "0197c55f-5af6-7e3d-af9b-f2359b104be8",
"sessionStatus": "COMPLETED",
"outcome": "accepted",
"results": {
"fundingSource": {
"type": "dataBlock",
"id": "c3a1e2f4-1234-4b6a-a9d8-123456789abc"
},
"verification": {
"type": "dataBlock",
"id": "d4b2f3a1-5678-4c7b-b0e9-abcdef012345"
}
}
}

Proof of Address — verified session:

{
"sessionId": "0197c55f-5af6-7e3d-af9b-f2359b104be9",
"sessionStatus": "COMPLETED",
"outcome": "accepted",
"results": {
"residentialAddress": {
"type": "dataBlock",
"id": "e5c3d4b2-9012-4e8c-c1f0-bcdef0123456"
},
"verification": {
"type": "dataBlock",
"id": "f6d4e5c3-0123-4f9d-d2g1-cdef01234567"
}
}
}

On non-verified routes (not_verified, fraud_detected), only the verification data block is present in results. If analysis could not complete at all, the session ends with sessionStatus: "ERROR" instead of routing to a completed outcome — see Handling non-verified outcomes.


Step 5 — Fetch the data block content

Use the id from results to retrieve the full data block content.

curl https://localhost:3000/api/v1/live/sessions/{sessionId}/datablocks \
--header "Authorization: Bearer YOUR_API_KEY"

FundingSource data block:

{
"datablockId": "c3a1e2f4-1234-4b6a-a9d8-123456789abc",
"type": "fundingSource",
"createdBy": { "stepId": "IBAN_CHECK", "clock": 1 },
"createdAt": "2026-01-15T10:30:00.000Z",
"status": "STORED",
"content": {
"iban": "FR7630006000011234567890189",
"bic": "BNPAFRPP",
"ibanCountry": "FR",
"accountNumber": "1234567890",
"bankCode": "30006",
"branchCode": "00001",
"bankName": "BNP Paribas",
"bankAddressLines": ["75009 Paris"],
"accountKey": "89"
}
}

ResidentialAddress data block:

{
"datablockId": "e5c3d4b2-9012-4e8c-c1f0-bcdef0123456",
"type": "residentialAddress",
"createdBy": { "stepId": "POA_CHECK", "clock": 1 },
"createdAt": "2026-01-15T10:30:00.000Z",
"status": "STORED",
"content": {
"residentAddress": "12 Rue de la Paix, 75002 Paris, France",
"residentStreet": "Rue de la Paix",
"residentHouseNumber": "12",
"residentCity": "Paris",
"residentPostalCode": "75002",
"residentCountry": "FR",
"residentState": null
}
}

For the full attribute reference, see FundingSource and ResidentialAddress.


Handling non-verified outcomes

When the step routes to not_verified or fraud_detected, only the Verification data block is produced. Inspect its status field to determine the reason.

Verification.statusMeaning
verifiedDocument passed all checks
rejectedDocument failed validation or did not match expected data
fraudDetectedSuspicious document or identity — treat as a hard stop

If the underlying analysis could not complete at all (e.g. no verdict could be determined), no route is taken — the session instead terminates with sessionStatus: "ERROR" and no Verification data block is produced. Treat this as a processing fault to retry or escalate, not as a completed outcome to branch on.

{
"datablockId": "d4b2f3a1-5678-4c7b-b0e9-abcdef012345",
"type": "verification",
"content": {
"status": "rejected",
"terminationReason": {
"code": "document_rejected",
"message": "Document could not be validated"
},
"methods": [],
"provider": "IDnow",
"trustFramework": null,
"assuranceLevel": null,
"verifiedAt": "2026-01-15T10:30:01.000Z",
"verificationProcessId": "file-uid-from-provider"
}
}