Available via API | Available via SDK | Needs configuration on Veriff’s side | Needs a separate integration | Needs a separate webhook |
|---|---|---|---|---|
✅ | ✅ | ✅ | ❌ | ❌ |
The Proof of Address (PoA) solution automates the verification of end-user addresses by capturing and extracting data from documents such as bank statements and utility bills, matching name data, and by checking the integrity of the document.
The core solution validates document type and issue date, extracts name and address information. It is possible to improve the solution further by enabling address validation and address matching, name matching, and/or fraud validation to check document integrity.
All the extracted and checked data is provided via API or Veriff Customer Portal.
The automatic Proof of Address solution is available via API, web flow, and native SDKs. The flow is set up via Workflows in the Veriff Customer Portal.
Contact your solutions engineer for info and configuration.
Prerequisites
You have an integration set up with Veriff
The Proof of Address check is configured by your Solutions Engineer
You have configured the decision webhook to get responses from Veriff (see the how-to in webhooks’ Set up webhooks section)
If using the API, you are ready to collect and send Veriff your end-user’s data and document images
Veriff strongly recommends that you create and send us the endUserId or vendorData
Define acceptable document types (optional)
Not available by default.
Contact your solutions engineer for info and configuration.
To add more flexibility to the solution, you have the option to define which document types are accepted to perform the verification. Note that this option is not available by default.
When enabled for your solution, you can pass the document types when creating a session with POST /sessions, using the proofOfAddress.acceptableTypes array.
Example request to start a session:
{
"verification": {
"proofOfAddress": {
"acceptableTypes": [
{"name": "MOBILE_BILL"},
{"name": "PHONE_BILL"}
]
}
}
}→ See the Supported document types article for a list of document types
Flow overview
The flow is a bit different, depending on the Proof of Address related solutions you are using, and wether you are using Veriff through SDK or via API.
Available solutions:
Proof of Address Extraction (via SDK and API)
Address Validation (via SDK and API)
Address Matching (via SDK and API)
Proof of Address Extraction
This solution is used to extract data from the end-user’s document.
Create session and send end-user data
Generate a session using the API keys and the BaseURL of your PoA integration (see the API Documentation and API Reference[↗] how to find these)
You can use the
proofOfAddress.acceptableTypesarray to define which types of PoA documents the integration accepts (note available by default, needs configuration on Veriff’s side)
Capture end-user’s PoA document images with Veriff’s SDKs
Send the end-user through the verification flow to capture the image (using the preferred Veriff SDK)
You need the session URL generated in step 1 to use the SDKs (found in response payload as verification.url)
Session will be submitted automatically once the end-user takes and submits necessary images of their document
Receive the results from Veriff via decision webhook
1. Create a session using the POST /sessions call
1.1 Include the API URL and mandatory headers
POST {baseURL}/v1/sessions
Type: object
Headers:
X-AUTH-CLIENT: string (required) = API key
Content-Type: application/json1.2 Add the following data (minimum payload)
verification:objectVerification object*. You can send an empty object, or:define the acceptable document types** with:
proofOfAddress.acceptableTypesarray, defining them in thenamestring(s)
include the following to improve the results:
vendorData:stringendUserId:string
include the
person.firstNameandperson.lastNameto do the name match
*Mandatory parameter
**Option not available by default, must be enabled on Veriff’s side
Request payload sample
curl -X POST \
--url '/v1/sessions/' \
-H 'Content-Type: application/json' \
-H 'X-AUTH-CLIENT: API-PUBLIC-KEY' \
-d '{
"verification": {
"person": {
"firstName": "John",
"lastName": "Smith",
},
"proofOfAddress": {
"acceptableTypes": [
{"name": "UTILITY_BILL"},
{"name": "TAX_DOCUMENT"},
{"name": "PHONE_BILL"},
]
},
"endUserId": "a1b2c35d-e8f7-6d5e-3cd2-a1b2c35db3d4"
}
}'→ See the POST /sessions[↗] endpoint documentation for further details about how to start a session
2. Make a POST /sessions/{sessionId}/media request
Send only one image per verification session. Make sure that the value of image.context parameter of the image is document-front.
2.1 Include the API URL and mandatory headers
POST {baseURL}/v1/sessions/{sessionId}/media
Type: object
Headers:
X-AUTH-CLIENT: string (required) = API key
X-HMAC-SIGNATURE: string (required) = sessionId signed with the shared secret key
Content-Type: application/json2.2 Add the following data into the minimum payload
image:object*context:string* In this case, it must bedocument-frontcontent:string* Base64 encoded image (.jpg, .jpeg, .png, .heif, .heic, .webp and .pdf formats are supported)
*Required parameter
Request payload sample
curl -X POST \
--url '/v1/sessions/aea9ba6d-1b47-47fc-a4fc-f72b6d3584a7/media' \
-H 'Content-Type: application/json' \
-H 'X-AUTH-CLIENT: API-PUBLIC-KEY' \
-H 'X-HMAC-SIGNATURE: 034c6da2bb31fd9e6892516c6d7b90ebe10f79b47cfb3d155d77b4d9b66e1d53' \
-d '{
"image": {
"context": "document-front",
"content": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA+.../9fgAEAKcxisFjVfn0AAAAASUVORK5CYII="
}
}'→ See the POST /sessions/{sessionId}/media[↗] endpoint documentation for further details about how to send media files
3. Update the session status to submitted using the PATCH /sessions/{sessionId} call
submitted using the PATCH /sessions/{sessionId} call3.1 Include the API URL and mandatory headers
PATCH {baseURL}/v1/sessions/{sessionId}
Type: object
Headers:
X-AUTH-CLIENT: string (required) = API key
X-HMAC-SIGNATURE: string (required) = sessionId signed with the shared secret key
Content-Type: application/json3.2 Add the following data (minimum payload)
verification:object*status:string* Status of the verification session you are patching the session into, alwayssubmitted
*Required parameter
Request payload sample
curl -X PATCH \
--url '/v1/sessions/fd5c1563-1d23-4b1a-ae46-7ba429927ed8' \
-H 'Content-Type: application/json' \
-H 'X-AUTH-CLIENT: API-PUBLIC-KEY' \
-H 'X-HMAC-SIGNATURE: dd994f70b1150ae012f9c1d6d20adf7ed69780044835d39de20b00ffae0660a0' \
-d '{
"verification": {
"status": "submitted"
}
}'Once done, an event webhook with submitted status will be sent.
Wait for the session to be approved and decision webhook to send response (see the webhook payload subsection below)
Now you can use the following API calls to query the document info:
GET /sessions/{sessionId}/media[↗] to get a list of media objects and address media with the sessionId
GET /attempts/{attemptId}/media[↗] to get a list of media objects and address media with the attempt ID
GET /media/{mediaId}[↗] to get a media file with the mediaId
Find session and/or decision related data
You can get the data from three sources:
Receive the decision webhook
Query the results via GET sessions/{sessionId}/decision
View the session in Veriff Customer Portal (see below for more info)
Click to open decision webhook example
Below are a sample and an explanation of a decision webhook payload for a minimal Proof of Address Verification.
Depending on your solution, the payload may contain additional parameters. To find more info about it, see the decision webhook documentation.
Sample request
{
"status": "success",
"verification": {
"id": "ab1c2345-678d-4e3b-98a3-a75b00b715kj",
"attemptId": "c6cab5b0-c506-47a6-922d-6ae3b72ca172",
"code": 9001,
"person": {
"gender": null,
"idNumber": null,
"lastName": null,
"addresses": [
{
"fullAddress": "Jaan Koorti 777,
99999 Tallinn, Estonia"
}
],
"firstName": "Jane Doe",
"citizenship": null,
"dateOfBirth": null,
"nationality": null,
"yearOfBirth": null,
"placeOfBirth": null,
"pepSanctionMatch": null
},
"reason": null,
"status": "approved",
"comments": [],
"document": {
"type": null,
"number": null,
"country": null,
"validFrom": "2023-11-25",
"validUntil": null
},
"reasonCode": null,
"vendorData": null,
"endUserId": "a1b2c35d-e8f7-6d5e-3cd2-a1b2c35db3d4",
"decisionTime": "2024-01-23T10:31:05.293000Z",
"acceptanceTime": "2024-01-23T10:25:40.026Z",
"additionalVerifiedData": {
"proofOfAddress": {
"documentType": "UTILITY_BILL",
"nameMatch": true,
"nameMatcPercentage": 100.00,
"fraud": {
"riskLevel": "HIGH_RISK",
"reason": "PDF_PROCESSED_BY_EDITOR",
"reasonDescription": "Document was processed using editing software",
"indicators": []
}
},
},
}
"technicalData": {
"ip": null
}Request properties explained
*Required field
status:string* Status of the responseverification:object* Verification request decision object.nullif decision is not available yetid:string* UUID v4 which identifies the verification sessionattemptId:string* UUID v4 of the attempt which received a status (as shown inverification.statusfield)vendorData:string | null* The unique identifier that you created for your end-user.nullif not specifiedendUserId:string | null* TheUUIDthat you created for your end-user.nullif not specifiedstatus:string* Verification status, one ofapproved,declined,resubmission_requested,review,expired,abandonedcode:integer* Verification session decision code, one of9001,9102,9103,9104,9121. For more info, see the verification session decision codesreason:string | nullReason why the verification failed, only sent when the verification failedreasonCode:integer | nullReason code of the failed verification, only sent when the verification failed. For more info, see the possible codes for a failed verificationdecisionTime:string | null* Timestamp of the decision, represented asUTC YYYY-MM-DDTHH:MM:SS.SSS+Timezone Offset(ISO 8601)acceptanceTime:string* Timestamp of the session generation, represented asUTC YYYY-MM-DDTHH:MM:SS.SSS+Timezone Offset(ISO 8601)person:object* Data about the verified persongender:string | null* Person’s gender, represented as M or F, ornullif not presentidNumber:string | null* National identification numberlastName:string | null*null, unless prefilled by the customeraddresses:array* Array of person's addressesfullAddress:stringAddress as a single string
firstName:string | null* Person's first name as written on the document, if availabledateOfBirth:string* Person’s date of birth, represented asYYYY-MM-DDnationality:string | null* Person's nationalityyearOfBirth:string | null* Person’s year of birth, represented asYYYYplaceOfBirth:string | null* Person's place of birthpepSanctionMatch:string | null* Legacy field, may return incorrect result, should be ignoredcitizenship:string | null* Deprecated, always returns null
document:object* Verified documenttype:string | nullDocument typenumber:string | nullDocument number,[a-zA-Z0-9]characters onlycountry:string | nullDocument issuing country, represented as ISO 3166alpha-2codevalidUntil:string | nullDocument is valid until date, represented asYYYY-MM-DD. Optional, must be configured for your integration by the Solutions EngineervalidFrom:string | null* Document is valid from date, represented asYYYY-MM-DD
additionalVerifiedData:object* Data which has been optionally verified for sessionproofOfAddress:object* Proof of address data. Optional, available only if the name match check has been enabled for the integrationdocumentType:string* Indicates the type of the proof of address document.nullif the check could not be completed.nameMatch:boolean | nullIndicates if the name on the proof of address document matches the name from the initial request data.nullif the check could not be completed. Sent only when the name match check was enabled.nameMatchPercentage:float| nullIndicates the level of similarity the matched names have, in the range of 0.00-100.00.nullif the check could not be completed. Sent only when the name match check was enabled.fraud:objectReturns data about document integrity. Available only if the fraud validation check has been enabled for your integration.riskLevel:string | nullIndicates the risk level, possible valuesLOW_RISK,MEDIUM_RISK,HIGH_RISK.nullif the check was not executed or failed.reason:string | nullShort description indicating the reason behind the risk level.nullif the check was not executed or failed.reasonDescription:string | nullHuman readable explanation of the data in thereasonfield.nullif the check was not executed or failed.indicators:arrayArray strings listing the factors that influenced the risk assessment. Empty if the check was not executed or failed.
comments:array* (Deprecated)
technicalData:object* Technical dataip:string | nullIP of the device from which the verification was made
Click to open API GET sessions/sessionId/decision endpoint example
Below are a sample and an explanation of a API call payload for a minimal Proof of Address Verification.
Depending on your solution, the payload may contain additional parameters. To find more info about it, see the GET sessions/{sessionId}/decision[↗] documentation.
Sample response
{
"status": "success",
"verification": {
"id": "ab1c2345-678d-4e3b-98a3-a75b00b715kj",
"attemptId": "c6cab5b0-c506-47a6-922d-6ae3b72ca172",
"code": 9001,
"person": {
"gender": null,
"idNumber": null,
"lastName": null,
"addresses": [
{
"fullAddress": "Jaan Koorti 777,
99999 Tallinn, Estonia"
}
],
"firstName": "Jane Doe",
"citizenship": null,
"dateOfBirth": null,
"nationality": null,
"yearOfBirth": null,
"placeOfBirth": null,
"pepSanctionMatch": null
},
"reason": null,
"status": "approved",
"comments": [],
"document": {
"type": null,
"number": null,
"country": null,
"validFrom": "2023-11-25",
"validUntil": null
},
"reasonCode": null,
"vendorData": null,
"endUserId": "a1b2c35d-e8f7-6d5e-3cd2-a1b2c35db3d4",
"decisionTime": "2024-01-23T10:31:05.293000Z",
"acceptanceTime": "2024-01-23T10:25:40.026Z",
"additionalVerifiedData": {
"proofOfAddress": {
"documentType": "UTILITY_BILL",
"nameMatch": true,
"nameMatcPercentage": 100.00,
"fraud": {
"riskLevel": "HIGH_RISK",
"reason": "PDF_PROCESSED_BY_EDITOR",
"reasonDescription": "Document was processed using editing software",
"indicators": []
}
},
},
}
"technicalData": {
"ip": null
}Response properties explained
status:stringStatus of the responseverification:objectVerification request decision object.nullif decision is not available yetid:stringUUID v4 which identifies the verification sessionattemptId:stringUUID v4 of the attempt which received a status (as shown inverification.statusfield)vendorData:string | nullThe unique identifier that you created for your end-user.nullif not specifiedendUserId:string | nullTheUUIDthat you created for your end-user.nullif not specifiedstatus:stringVerification status, one ofapproved,declined,resubmission_requested,review,expired,abandonedcode:integerVerification session decision code, one of9001,9102,9103,9104,9121. For more info, see the verification session decision codesreason:string | nullReason why the verification failed, only sent when the verification failedreasonCode:integer | nullReason code of the failed verification, only sent when the verification failed. For more info, see the possible codes for a failed verificationdecisionTime:stringTimestamp of the decision, represented asUTC YYYY-MM-DDTHH:MM:SS.SSS+Timezone Offset(ISO 8601)acceptanceTime:stringTimestamp of the session generation, represented asUTC YYYY-MM-DDTHH:MM:SS.SSS+Timezone Offset(ISO 8601)person:objectData about the verified persongender:string | nullPerson’s gender, represented as M or F, ornullif not presentidNumber:string | nullNational identification numberlastName:string | nullnull, unless prefilled by the customeraddresses:arrayArray of person's addressesfullAddress:stringAddress as a single string
firstName:string | nullPerson's first name as written on the document, if availabledateOfBirth:stringPerson’s date of birth, represented asYYYY-MM-DDnationality:string | nullPerson's nationalityyearOfBirth:string | nullPerson’s year of birth, represented asYYYYplaceOfBirth:string | nullPerson's place of birthpepSanctionMatch:string | nullLegacy field, may return incorrect result, should be ignoredcitizenship:string | nullDeprecated, always returns null
document:objectVerified documenttype:string | nullDocument typenumber:string | nullDocument number,[a-zA-Z0-9]characters onlycountry:string | nullDocument issuing country, represented as ISO 3166alpha-2codevalidUntil:string | nullDocument is valid until date, represented asYYYY-MM-DD. Optional, must be configured for your integration by the Solutions EngineervalidFrom:string | nullDocument is valid from date, represented asYYYY-MM-DD
additionalVerifiedData:objectData which has been optionally verified for sessionproofOfAddress:objectProof of address data. Optional, available only if the name match check has been enabled for the integrationdocumentType:stringIndicates the type of the proof of address document.nullif the check could not be completed.nameMatch:boolean | nullIndicates if the name on the proof of address document matches the name from the initial request data.nullif the check could not be completed. Sent only when the name match check was enabled.nameMatchPercentage:float| nullIndicates the level of similarity the matched names have, in the range of 0.00-100.00.nullif the check could not be completed. Sent only when the name match check was enabled.fraud:objectReturns data about document integrity. Available only if the fraud validation check has been enabled for your integration.riskLevel:string | nullIndicates the risk level, possible valuesLOW_RISK,MEDIUM_RISK,HIGH_RISK.nullif the check was not executed or failed.reason:string | nullShort description indicating the reason behind the risk level.nullif the check was not executed or failed.reasonDescription:string | nullHuman readable explanation of the data in thereasonfield.nullif the check was not executed or failed.indicators:arrayArray strings listing the factors that influenced the risk assessment. Empty if the check was not executed or failed.
comments:array(Deprecated)
technicalData:objectTechnical dataip:string | nullIP of the device from which the verification was made
Address Validation
Address Validation for Proof of Address is a step used to validate the address extracted from the end-user’s document. It has three components:
Address Search (mandatory),
Address Validation (optional) and
Required Country (optional).
Basis of the whole address validation solution.
Address extracted from the document is matched against the info in a third-party provider (e.g., Google maps). Initially extracted data is not changed in any way. If any data is found, it is added to the webhook, to addressValidationResult object. Address Search functionality is required to use Address Validation and Required Country features.
Address Search feature also attempts to find correct country. This result is mandatory for the Required Country feature.
Required fields for the feature: a value is present for either street, unit or houseNumber.
The Address Validation functionality adds additional accuracy to address search results and can be used to validate if the present address is a PO box or not.
Address Search returns a list of possible addresses, which can be from different countries. The Address Validation check tries to determine if any of those exist. It also fills the addressType string, trying to indicate if the address belongs to a residential, business or PO box entity.
Required fields for the feature: one of street, unit or houseNumber have been confirmed as valid.
Address Search is a required component to enable this feature.
With this feature you can set the session to automatically fail if no valid country is found.
Relevant parameters are addressValidationResult.components.country and countryExists.result . countryExists.result parameter indicates wether the components.country is filled.
Address Search is a required component to enable this feature.
Create a session and send end-user data
Generate a session using the API keys and the BaseURL of your PoA integration (see the API Documentation and API Reference[↗] how to find these)
You can use the
proofOfAddress.acceptableTypesarray to define which types of PoA documents the integration accepts (note available by default, needs configuration on Veriff’s side)
Capture end-user’s PoA document images with Veriff’s SDKs
Send the end-user through the verification flow to capture the image (using the preferred Veriff SDK)
You need the session URL generated in step 1 to use the SDKs (found in response payload as
verification.url)
Session will be submitted automatically once the end-user takes and submits necessary images of their document
Receive the results from Veriff via decision webhook
1. Create a session using the POST /sessions call
1.1 Include the API URL and mandatory headers
POST {baseURL}/v1/sessions
Type: object
Headers:
X-AUTH-CLIENT: string (required) = API key
Content-Type: application/json1.2 Add the following data (minimum payload)
verification:objectVerification object*. You can send an empty object, or:define the acceptable document types** with:
proofOfAddress.acceptableTypesarray, defining them in thenamestring(s)
include the following to improve the results:
vendorData:stringendUserId:string
include the
person.firstNameandperson.lastNameto do the name match
*Mandatory parameter
**Option not available by default, must be enabled on Veriff’s side
Request payload sample
curl -X POST \
--url '/v1/sessions/' \
-H 'Content-Type: application/json' \
-H 'X-AUTH-CLIENT: API-PUBLIC-KEY' \
-d '{
"verification": {
"person": {
"firstName": "John",
"lastName": "Smith",
},
"proofOfAddress": {
"acceptableTypes": [
{"name": "UTILITY_BILL"},
{"name": "TAX_DOCUMENT"},
{"name": "PHONE_BILL"},
]
},
"endUserId": "a1b2c35d-e8f7-6d5e-3cd2-a1b2c35db3d4"
}
}'→ See the POST /sessions[↗] endpoint documentation for further details about how to start a session
2. Make a POST /sessions/{sessionId}/media request
Send only one image per verification session. Make sure that the value of image.context parameter of the image is document-front.
2.1 Include the API URL and mandatory headers
POST {baseURL}/v1/sessions/{sessionId}/media
Type: object
Headers:
X-AUTH-CLIENT: string (required) = API key
X-HMAC-SIGNATURE: string (required) = sessionId signed with the shared secret key
Content-Type: application/json2.2 Add the following data into the minimum payload
image:object*context:string* In this case, it must bedocument-frontcontent:string* Base64 encoded image (.jpg, .jpeg, .png, .heif, .heic, .webp and .pdf formats are supported)
*Required parameter
Request payload sample
curl -X POST \
--url '/v1/sessions/aea9ba6d-1b47-47fc-a4fc-f72b6d3584a7/media' \
-H 'Content-Type: application/json' \
-H 'X-AUTH-CLIENT: API-PUBLIC-KEY' \
-H 'X-HMAC-SIGNATURE: 034c6da2bb31fd9e6892516c6d7b90ebe10f79b47cfb3d155d77b4d9b66e1d53' \
-d '{
"image": {
"context": "document-front",
"content": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA+.../9fgAEAKcxisFjVfn0AAAAASUVORK5CYII="
}
}'→ See the POST /sessions/{sessionId}/media[↗] endpoint documentation for further details about how to send media files
3. Update the session status to submitted using the PATCH /sessions/{sessionId} call
submitted using the PATCH /sessions/{sessionId} call3.1 Include the API URL and mandatory headers
PATCH {baseURL}/v1/sessions/{sessionId}
Type: object
Headers:
X-AUTH-CLIENT: string (required) = API key
X-HMAC-SIGNATURE: string (required) = sessionId signed with the shared secret key
Content-Type: application/json3.2 Add the following data (minimum payload)
verification:object*status:string* Status of the verification session you are patching the session into, alwayssubmitted
*Required parameter
Request payload sample
curl -X PATCH \
--url '/v1/sessions/fd5c1563-1d23-4b1a-ae46-7ba429927ed8' \
-H 'Content-Type: application/json' \
-H 'X-AUTH-CLIENT: API-PUBLIC-KEY' \
-H 'X-HMAC-SIGNATURE: dd994f70b1150ae012f9c1d6d20adf7ed69780044835d39de20b00ffae0660a0' \
-d '{
"verification": {
"status": "submitted"
}
}'Once done, an event webhook with submitted status will be sent.
Wait for the session to be approved and decision webhook to send response (see the webhook payload subsection below)
Now you can use the following API calls to query the document info:
GET /sessions/{sessionId}/media[↗] to get a list of media objects and address media with the
sessionIdGET /attempts/{attemptId}/media[↗] to get a list of media objects and address media with the attempt ID
GET /media/{mediaId}[↗] to get a media file with the
mediaId
Find session and/or decision related data
You can get the data from three sources:
Receive the decision webhook
Poll results via GET sessions/{sessionId}/decision
View the session in Veriff Customer Portal (see below for more info)
Click to open decision webhook example
Sample response
Below are an example and explanation of the Address Validation related payload. Depending on your solution, the payload may contain additional parameters. To find more info about it, see the decision webhook documentation.
...
"additionalVerifiedData": {
"proofOfAddress": {
"documentType": "ELECTRICITY_BILL",
"nameMatch": null,
"nameMatchPercentage": null,
"addressValidationResult": {
"components": {
"plausibleFullAddress": "23 Willowbrook Lane, Ashford, Kent CT19 8NP, UK",
"city": "Ashford",
"unit": null,
"state": null,
"street": "Willowbrook Lane",
"country": "UK",
"postcode": "CT19 8NP",
"houseNumber": "23"
},
"addressType": "residential"
"validations": {
"countryExists": {
"result": "skipped"
}
}
}
}
}
...Request properties explained
additionalVerifiedData:objectData that has been optionally verified for the sessionproofOfAddress:objectProof of address datadocumentType:string | nullIndicates the type of the proof of address document.nullif the check could not be completed.nameMatch:boolean | nullIndicates if the name on the proof of address document matches the name from the initial request data.nullif the check could not be completed.nameMatchPercentage:float | nullIndicates the level of similarity the matched names have, in the range of 0.00-100.00.nullif the check could not be completed.addressValidationResult:objectContains data that has been found about the extracted addresscomponents:objectplausibleFullAddress:string | nullFull address as a string, containing extracted and found datacity:string | nullAny human settlement, including cities, towns, villages, hamlets, localities, etc.unit:string | nullAn apartment, unit, office, lot, or other secondary unit designatorstate:string | nullA first-level administrative divisionstreet:string | nullStreet namecountry:string | nullSovereign nations and their dependent territories (ISO 3166)postcode:string | nullPostal codes used for mail sortinghouseNumber:string | nullExternal (street-facing) building number
addressType:string | nullIndicates if the address is of a residential or business premise, or it is a PO box. One ofresidential,business,poBox.nullif the address validation feature was has not been enabled, or if no data was sent.validations:objectContains country validation result datacountryExists:objectIndicates if the validation was able to verify the countryresult:string | nullIndicates the result of country validation, or if it was skipped. One ofsuccess,failure,skipped
Click to open API GET sessions/sessionId/decision endpoint example
Sample response
Below are an example and explanation of the Address Validation related payload. Depending on your solution, the payload may contain additional parameters. To find more info about it, see the GET sessions/{sessionId}/decision[↗] documentation.
...
"additionalVerifiedData": {
"proofOfAddress": {
"documentType": "ELECTRICITY_BILL",
"nameMatch": null,
"nameMatchPercentage": null,
"addressValidationResult": {
"components": {
"plausibleFullAddress": "23 Willowbrook Lane, Ashford, Kent CT19 8NP, UK",
"city": "Ashford",
"unit": null,
"state": null,
"street": "Willowbrook Lane",
"country": "UK",
"postcode": "CT19 8NP",
"houseNumber": "23"
},
"addressType": "residential"
"validations": {
"countryExists": {
"result": "skipped"
}
}
}
}
}
...Request properties explained
additionalVerifiedData:objectData that has been optionally verified for the sessionproofOfAddress:objectProof of address datadocumentType:string | nullIndicates the type of the proof of address document.nullif the check could not be completed.nameMatch:boolean | nullIndicates if the name on the proof of address document matches the name from the initial request data.nullif the check could not be completed.nameMatchPercentage:float | nullIndicates the level of similarity the matched names have, in the range of 0.00-100.00.nullif the check could not be completed.addressValidationResult:objectContains data that has been found about the extracted addresscomponents:objectplausibleFullAddress:string | nullFull address as a string, containing extracted and found datacity:string | nullAny human settlement, including cities, towns, villages, hamlets, localities, etc.unit:string | nullAn apartment, unit, office, lot, or other secondary unit designatorstate:string | nullA first-level administrative divisionstreet:string | nullStreet namecountry:string | nullSovereign nations and their dependent territories (ISO 3166)postcode:string | nullPostal codes used for mail sortinghouseNumber:string | nullExternal (street-facing) building number
addressType:string | nullIndicates if the address is of a residential or business premise, or it is a PO box. One ofresidential,business,poBox.nullif the address validation feature was has not been enabled, or if no data was sent.validations:objectContains country validation result datacountryExists:objectIndicates if the validation was able to verify the countryresult:string | nullIndicates the result of country validation, or if it was skipped. One ofsuccess,failure,skipped
Address Matching
Address Matching for PoA is a step that compares addresses from two different sources (Proof of Address documents and customer-provided initial data) to verify they belong to the same location. The solution enables automated address verification for compliance and fraud prevention, reduces manual review overhead, improves KYC accuracy by cross-referencing address data from multiple sources, helps detect address discrepancies during onboarding flows.
Address Matching cannot performed for you sessions if you are located in restricted countries: China, Vietnam, Crimea, Cuba, Donetsk People's Republic, Iran, Luhansk People's Republic, North Korea, Syria.
Create a session and send end-user data
Generate a session using the API keys and the BaseURL of your PoA integration (see the API Documentation and API Reference[↗] how to find these)
You must provide address data in session initData in
person.addressfield
Capture end-user’s PoA document images with Veriff’s SDKs
End-user’s document must include extractable address information
Send the end-user through the verification flow to capture the image (using the preferred Veriff SDK)
You need the session URL generated in step 1 to use the SDKs (found in response payload as
verification.url)
Session will be submitted automatically once the end-user takes and submits necessary images of their document
Receive the results from Veriff via decision webhook
1. Create a session using the POST /sessions call
1.1 Include the API URL and mandatory headers
POST {baseURL}/v1/sessions
Type: object
Headers:
X-AUTH-CLIENT: string (required) = API key
Content-Type: application/json1.2 Add the following data (minimum payload)
verification:objectVerification object*. You can send an empty object, or:include the address data in
person.addressdefine the PoA document info with
document.typeanddocument.countryinclude the following to improve the results:
vendorData:stringendUserId:string
include the
person.firstNameandperson.lastNameto do the name match
*Mandatory parameter
Request payload sample
curl -X POST \
--url '/v1/sessions/' \
-H 'Content-Type: application/json' \
-H 'X-AUTH-CLIENT: API-PUBLIC-KEY' \
-d '{
"verification": {
"person": {
"firstName": "John",
"lastName": "Smith",
"address": {
"fullAddress": "123 Main Street, New York, NY 10001, USA"
}
},
"document": {
"type": "PROOF_OF_ADDRESS",
"country": "US"
},
"endUserId": "a1b2c35d-e8f7-6d5e-3cd2-a1b2c35db3d4"
}
}'→ See the POST /sessions[↗] endpoint documentation for further details about how to start a session
2. Make a POST /sessions/{sessionId}/media request
Send only one image per verification session. Make sure that the value of image.context parameter of the image is document.
2.1 Include the API URL and mandatory headers
POST {baseURL}/v1/sessions/{sessionId}/media
Type: object
Headers:
X-AUTH-CLIENT: string (required) = API key
X-HMAC-SIGNATURE: string (required) = sessionId signed with the shared secret key
Content-Type: application/json2.2 Add the following data into the minimum payload
image:object*context:string* In this case, it must bedocumentcontent:string* Base64 encoded image (.jpg, .jpeg, .png, .heif, .heic, .webp and .pdf formats are supported)
*Required parameter
Request payload sample
curl -X POST \
--url '/v1/sessions/aea9ba6d-1b47-47fc-a4fc-f72b6d3584a7/media' \
-H 'Content-Type: application/json' \
-H 'X-AUTH-CLIENT: API-PUBLIC-KEY' \
-H 'X-HMAC-SIGNATURE: 034c6da2bb31fd9e6892516c6d7b90ebe10f79b47cfb3d155d77b4d9b66e1d53' \
-d '{
"image": {
"context": "document",
"content": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA+.../9fgAEAKcxisFjVfn0AAAAASUVORK5CYII="
}
}'→ See the POST /sessions/{sessionId}/media[↗] endpoint documentation for further details about how to send media files
3. Update the session status to submitted using the PATCH /sessions/{sessionId} call
submitted using the PATCH /sessions/{sessionId} call3.1 Include the API URL and mandatory headers
PATCH {baseURL}/v1/sessions/{sessionId}
Type: object
Headers:
X-AUTH-CLIENT: string (required) = API key
X-HMAC-SIGNATURE: string (required) = sessionId signed with the shared secret key
Content-Type: application/json3.2 Add the following data (minimum payload)
verification:object*status:string* Status of the verification session you are patching the session into, alwayssubmitted
*Required parameter
Request payload sample
curl -X PATCH \
--url '/v1/sessions/fd5c1563-1d23-4b1a-ae46-7ba429927ed8' \
-H 'Content-Type: application/json' \
-H 'X-AUTH-CLIENT: API-PUBLIC-KEY' \
-H 'X-HMAC-SIGNATURE: dd994f70b1150ae012f9c1d6d20adf7ed69780044835d39de20b00ffae0660a0' \
-d '{
"verification": {
"status": "submitted"
}
}'Once done, an event webhook with submitted status will be sent.
Wait for the session to be approved and decision webhook to send response (see the webhook payload subsection below)
Now you can use the following API calls to query the document info:
GET /sessions/{sessionId}/media[↗] to get a list of media objects and address media with the
sessionIdGET /attempts/{attemptId}/media[↗] to get a list of media objects and address media with the attempt ID
GET /media/{mediaId}[↗] to get a media file with the
mediaId
Find session and/or decision related data
You can get the data from three sources:
Receive the decision webhook
Poll results via GET sessions/{sessionId}/decision
View the session in Veriff Customer Portal (see below for more info)
Click to open decision webhook example
Sample response
Below are an example and explanation of the Address Matching related payload. Depending on your solution, the payload may contain additional parameters. To find more info about it, see the decision webhook documentation.
...,
"additionalVerifiedData": {
"proofOfAddress": {
"documentType": "UTILITY_BILL",
"fullName": "John Smith",
"fullAddress": "123 Main Street, New York, NY 10001, USA",
"issueDate": "2025-09-01",
"nameMatch": null,
"nameMatchPercentage": null
},
"addressMatching": {
"addresses": [
{
"fullAddress": "123 Main Street, New York, NY 10001, USA",
"parsedAddress": {
"city": "New York",
"unit": null,
"state": "NY",
"street": "Main Street",
"country": "USA",
"postcode": "10001",
"houseNumber": "123"
}
},
{
"fullAddress": "123 Main St, New York, New York 10001, United States",
"parsedAddress": {
"city": "New York",
"unit": null,
"state": "New York",
"street": "Main St",
"country": "United States",
"postcode": "10001",
"houseNumber": "123"
}
}
],
"result": true,
"matchThreshold": 80,
"matchPercentage": 90
}
},
...Request properties explained
additionalVerifiedData:objectData that has been optionally verified for the sessionproofOfAddress:objectProof of address datadocumentType:string | nullIndicates the type of the proof of address document.nullif the check could not be completed.fullName:stringPerson’s name as a single stringfullAddress:stringFull address as a single stringissueDate:stringPoA document issue date, inYYYY-MM-DDformatnameMatch:boolean | nullIndicates if the name on the proof of address document matches the name from the initial request data.nullif the check could not be completed.nameMatchPercentage:float | nullIndicates the level of similarity the matched names have, in the range of 0.00-100.00.nullif the check could not be completed.
addressMatching:objectContains all address matching validation resultsaddresses:arrayArray of exactly 2 address objects - first from initData, second from PoA documentfullAddress:stringOriginal full address string as provided/extractedparsedAddress:objectGoogle Maps API parsed address componentscity:string | nullParsed city nameunit:string | nullParsed partment/unit numberstate:string | nullParsed state/provincestreet:string | nullParsed street name (premise)country:string | nullParsed country namepostcode:string | nullParsed postal/ZIP codehouseNumber:string | nullParsed house/building number
result:booleanIndicates whether addresses match based on thresholdmatchThreshold:integerConfigured matching percentage thresholdmatchPercentage:integerIndicates matching percentage
Click to open API GET sessions/sessionId/decision endpoint example
Sample response
Below are an example and explanation of the Address Matching related payload. Depending on your solution, the payload may contain additional parameters. To find more info about it, see the GET sessions/{sessionId}/decision[↗] documentation.
...,
"additionalVerifiedData": {
"proofOfAddress": {
"documentType": "UTILITY_BILL",
"fullName": "John Smith",
"fullAddress": "123 Main Street, New York, NY 10001, USA",
"issueDate": "2025-09-01",
"nameMatch": null,
"nameMatchPercentage": null
},
"addressMatching": {
"addresses": [
{
"fullAddress": "123 Main Street, New York, NY 10001, USA",
"parsedAddress": {
"city": "New York",
"unit": null,
"state": "NY",
"street": "Main Street",
"country": "USA",
"postcode": "10001",
"houseNumber": "123"
}
},
{
"fullAddress": "123 Main St, New York, New York 10001, United States",
"parsedAddress": {
"city": "New York",
"unit": null,
"state": "New York",
"street": "Main St",
"country": "United States",
"postcode": "10001",
"houseNumber": "123"
}
}
],
"result": true,
"matchThreshold": 80,
"matchPercentage": 90
}
},
...Request properties explained
additionalVerifiedData:objectData that has been optionally verified for the sessionproofOfAddress:objectProof of address datadocumentType:string | nullIndicates the type of the proof of address document.nullif the check could not be completed.fullName:stringPerson’s name as a single stringfullAddress:stringFull address as a single stringissueDate:stringPoA document issue date, inYYYY-MM-DDformatnameMatch:boolean | nullIndicates if the name on the proof of address document matches the name from the initial request data.nullif the check could not be completed.nameMatchPercentage:float | nullIndicates the level of similarity the matched names have, in the range of 0.00-100.00.nullif the check could not be completed.
addressMatching:objectContains all address matching validation resultsaddresses:arrayArray of exactly 2 address objects - first from initData, second from PoA documentfullAddress:stringOriginal full address string as provided/extractedparsedAddress:objectGoogle Maps API parsed address componentscity:string | nullParsed city nameunit:string | nullParsed partment/unit numberstate:string | nullParsed state/provincestreet:string | nullParsed street name (premise)country:string | nullParsed country namepostcode:string | nullParsed postal/ZIP codehouseNumber:string | nullParsed house/building number
result:booleanIndicates whether addresses match based on thresholdmatchThreshold:integerConfigured matching percentage thresholdmatchPercentage:integerIndicates matching percentage
Note about the fraud validation
If the fraud validation has been enabled for your solution, then you will see the additionalVerifiedData.proofOfAddress.fraud object in the payload.
"additionalVerifiedData": {
"proofOfAddress": {
...
"fraud": {
"riskLevel": "HIGH_RISK",
"reason": "PDF_PROCESSED_BY_EDITOR",
"reasonDescription": "Document was processed using editing software",
"indicators": []
}
},The fraud object shows the document's risk level and gives additional details about the factors that influenced this risk assessment. Veriff passes the data unaltered in webhooks and API payloads.
Veriff recommends you do not build any logic on top of the
proofOfAddress.fraud.indicatorsarray, as possible values may be altered without prior notice to provide better insights. However, structure of entries will stay backwards compatible.
The decision webhook and API examples below contain limited info about the possible values of the keys in the fraud object.
Find decision and/or session related info in Veriff Customer Portal
You can find the verification session related info, including the decision, in the Veriff Customer Portal, under the All Verifications tab.
→ See Review verification in Veriff Customer Portal about how to view the session info in the Veriff Customer portal
Proof of Address Capture
This is a separate solution which is used to just capture end-user’s document. No further analysis is done.
Refer to the Proof of Address Capture documentation.
Non-document Proof of Address
It is possible to perform Proof of Address verification without sending document data. This, however, is not part of the classic Proof of Address solution. Instead, it can be done by passing the end-user’s name and address data when creating a session with Veriff, and configuring the integration to include a registry check. Veriff passes the data to the database verification service provider, who verifies the name and address data, and returns relevant info.
Currently, the solution is available only for US Database Verification check.
Status and reason codes
For an approved session, see:
verification.codeabout verification session decision code, one of9001,9102,9103,9104,9121verification.statusabout verification status, one ofapproved,declined,resubmission_requested,review,expired,abandoned
If the session was declined or resubmission_requested, you can find additional information by checking:
verification.reasonfor the reason why the verification failed
verification.reasonCode for reason code of the failed verification and cross-reference it with Granular reason codes (table)
Most common decline reasons for the Proof of Address solution
|
|
| What does it mean? |
|---|---|---|---|
| 510 | Presented document type is not supported | The uploaded document type is not on the list of supported document types for the integration. Try altering the list of supported documents. |
| 527 | Unable to collect proof of address data | System was unable to collect pieces of document data from the presented document. This can occur when the document itself or its image is of low quality. |
| 528 | Proof of address document issue date too old | The presented PoA document is older than 90 days. |
| 566 | System processing failure | There was an error when processing the request due to service error, incorrect configuration, etc. Check with your Solutions Engineer or Veriff Customer Support. |
| 568 | Invalid input | The full address is missing in either the document, initData, or both. |
| 573 | Address on document does not match with address in session initialisation data | Session was declined because the addresses do not match |
| 574 | Address provided is missing mandatory fields | Ensure that all the required fields are part of the initData |
| 580 | Address matching failed due to regional restrictions | You seem to be operating from a restricted country |
The table above aims to list the most common reasons why you may see the decline reason for a PoA session. However, the session may be declined due to a reason that is not inherently proof of address verification related (e.g. uploaded document’s quality is low). In that case, to find info about the codes you are seeing, refer to:
Changelog
Date | Description |
|---|---|
Nov 7, 2025 |
|
Jul 22, 2025 | Updated the Define acceptable document types section with a note that this solution is not available by default. Also added an example of session creation payload to the section. |
Jul 18, 2025 |
|
Jun 6, 2025 | Heading “Article versioning” changed to “Changelog” |
May 28, 2025 | Corrected the format of acceptable document types array of objects in Flow overview > API > 1.2 Add the following data (minimum payload) example |
May 19, 2025 | Article updated with info about the Proof of Address fraud check |
May 2, 2025 | Documentation published |