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 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 for that integration 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
To add more flexibility to the solution, you can define which document types are accepted to perform the verification.
You can pass them when creating a session with POST /sessions, using the proofOfAddress.acceptableTypes
array.
→ See the Supported document types article for a list of document types
Flow overview
The flow is a bit different, depending on the method you are using to verify your end-users with Veriff.
Proof of Address Extraction
In this case, data is extracted from the end-user’s document.
Generate a 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.acceptableTypes
array to define which types of PoA documents the integration accepts
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/json
1.2 Add the following data (minimum payload)
verification
:object
Verification object*. You can send an empty object, or:define the acceptable document types with:
proofOfAddress.acceptableTypes
array, defining them in thename
string(s)
include the following to improve the results:
vendorData
:string
endUserId:
string
include the
person.firstName
andperson.lastName
to 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",
},
"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/json
2.2 Add the following data into the minimum payload
image
:object
*context
:string
* In this case, it must bedocument-front
content
: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/json
3.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 decision and/or session related info
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
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.indicators
array, 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.
Webhook payload
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.null
if 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.status
field)vendorData
:string | null
* The unique identifier that you created for your end-user.null
if not specifiedendUserId
:string | null
* TheUUID
that you created for your end-user.null
if not specifiedstatus
:string
* Verification status, one ofapproved
,declined
,resubmission_requested
,review
,expired
,abandoned
code
:integer
* Verification session decision code, one of9001
,9102
,9103
,9104
,9121
. For more info, see the verification session decision codesreason
:string | null
Reason why the verification failed, only sent when the verification failedreasonCode
:integer | null
Reason code of the failed verification, only sent when the verification failed. For more info, see the possible codes for a failed verificationdecisionTime
:string
* 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, ornull
if not presentidNumber
:string | null
* National identification numberlastName
:string | null
*null
, unless prefilled by the customeraddresses
:array
* Array of person's addressesfullAddress
:string
Address 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-DD
nationality
:string | null
* Person's nationalityyearOfBirth
:string | null
* Person’s year of birth, represented asYYYY
placeOfBirth
: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 | null
Document typenumber
:string | null
Document number,[a-zA-Z0-9]
characters onlycountry
:string | null
Document issuing country, represented as ISO 3166alpha-2
codevalidUntil
:string | null
Document 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.null
if the check could not be completed.nameMatch
:boolean | null
Indicates if the name on the proof of address document matches the name from the initial request data.null
if the check could not be completed. Sent only when the name match check was enabled.nameMatchPercentage
:float| null
Indicates the level of similarity the matched names have, in the range of 0.00-100.00.null
if the check could not be completed. Sent only when the name match check was enabled.fraud
:object
Returns data about document integrity. Available only if the fraud validation check has been enabled for your integration.riskLevel
:string | null
Indicates the risk level, possible valuesLOW_RISK
,MEDIUM_RISK
,HIGH_RISK
.null
if the check was not executed or failed.reason
:string | null
Short description indicating the reason behind the risk level.null
if the check was not executed or failed.reasonDescription
:string | null
Human readable explanation of the data in thereason
field.null
if the check was not executed or failed.indicators
:array
Array 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 | null
IP of the device from which the verification was made
API call
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
:string
Status of the responseverification
:object
Verification request decision object.null
if 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.status
field)vendorData
:string | null
The unique identifier that you created for your end-user.null
if not specifiedendUserId
:string | null
TheUUID
that you created for your end-user.null
if not specifiedstatus
:string
Verification status, one ofapproved
,declined
,resubmission_requested
,review
,expired
,abandoned
code
:integer
Verification session decision code, one of9001
,9102
,9103
,9104
,9121
. For more info, see the verification session decision codesreason
:string | null
Reason why the verification failed, only sent when the verification failedreasonCode
:integer | null
Reason code of the failed verification, only sent when the verification failed. For more info, see the possible codes for a failed verificationdecisionTime
:string
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, ornull
if not presentidNumber
:string | null
National identification numberlastName
:string | null
null
, unless prefilled by the customeraddresses
:array
Array of person's addressesfullAddress
:string
Address 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-DD
nationality
:string | null
Person's nationalityyearOfBirth
:string | null
Person’s year of birth, represented asYYYY
placeOfBirth
: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 | null
Document typenumber
:string | null
Document number,[a-zA-Z0-9]
characters onlycountry
:string | null
Document issuing country, represented as ISO 3166alpha-2
codevalidUntil
:string | null
Document 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.null
if the check could not be completed.nameMatch
:boolean | null
Indicates if the name on the proof of address document matches the name from the initial request data.null
if the check could not be completed. Sent only when the name match check was enabled.nameMatchPercentage
:float| null
Indicates the level of similarity the matched names have, in the range of 0.00-100.00.null
if the check could not be completed. Sent only when the name match check was enabled.fraud
:object
Returns data about document integrity. Available only if the fraud validation check has been enabled for your integration.riskLevel
:string | null
Indicates the risk level, possible valuesLOW_RISK
,MEDIUM_RISK
,HIGH_RISK
.null
if the check was not executed or failed.reason
:string | null
Short description indicating the reason behind the risk level.null
if the check was not executed or failed.reasonDescription
:string | null
Human readable explanation of the data in thereason
field.null
if the check was not executed or failed.indicators
:array
Array 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 | null
IP of the device from which the verification was made
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.code
about verification session decision code, one of9001
,9102
,9103
,9104
,9121
verification.status
about 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.reason
for 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? |
---|---|---|---|
declined | 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. |
declined | 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. |
declined | 528 | Proof of address document issue date too old | The presented PoA document is older than 90 days. |
declined | 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. |
For more info about the codes you are seeing, refer to:
Changelog
Date | Description |
---|---|
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 |