Proof of Address Verification

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.

The system validates document type and issue date, extracts name and address information, and provides this data via API or Veriff Customer Portal.

It 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 more info


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.

  1. Generate a  using the API keys and the BaseURL of your PoA integration  (see the API Documentation and API Reference[↗] how to find these)

    1. You can use the proofOfAddress.acceptableTypes array to define which types of PoA documents the integration accepts

  2. Capture end-user’s PoA document images with Veriff’s SDKs

    1. Send the end-user through the verification flow to capture the image (using the preferred Veriff SDK)

    2. You need the session URL generated in step 1 to use the SDKs (found in response payload as verification.url)

  3. Session will be submitted automatically once the end-user takes and submits necessary images of their document

  4. 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 the name string(s)

    • include the following to improve the results:

      • vendorData: string

      • endUserId: string

    • include the person.firstName and person.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 be document-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

3.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, always submitted

*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.

  1. Wait for the session to be approved and decision webhook to send response (see the webhook payload subsection below)

  2. Now you can use the following API calls to query the document info:


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

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
            },
         },
    }
    "technicalData": {
        "ip": null
}

Request properties explained

*Required field

  • status: string* Status of the response

  • verification: object* Verification request decision object. null if decision is not available yet

    • id: string* UUID v4 which identifies the verification session

    • attemptId: string* UUID v4 of the attempt which received a status (as shown in verification.status field)

    • vendorData: string | null* The unique identifier that you created for your end-user. null if not specified

    • endUserId: string | null* The UUID that you created for your end-user. null if not specified

    • status: string* Verification status, one of approved, declined, resubmission_requested, review, expired, abandoned

    • code: integer* Verification session decision code, one of 9001, 9102, 9103, 9104, 9121. For more info, see the verification session decision codes

    • reason: string | null Reason why the verification failed, only sent when the verification failed

    • reasonCode: integer | null Reason code of the failed verification, only sent when the verification failed. For more info, see the possible codes for a failed verification

    • decisionTime: string* Timestamp of the decision, represented as UTC YYYY-MM-DDTHH:MM:SS.SSS+Timezone Offset (ISO 8601)

    • acceptanceTime: string* Timestamp of the session generation, represented as UTC YYYY-MM-DDTHH:MM:SS.SSS+Timezone Offset (ISO 8601)

    • person: object* Data about the verified person

      • gender: string | null* Person’s gender, represented as M or F, or null if not present

      • idNumber: string | null* National identification number

      • lastName: string | null* null, unless prefilled by the customer

      • addresses: array* Array of person's addresses

        • fullAddress: string Address as a single string

      • firstName: string | null* Person's first name as written on the document, if available

      • dateOfBirth: string* Person’s date of birth, represented as YYYY-MM-DD

      • nationality: string | null* Person's nationality

      • yearOfBirth: string | null* Person’s year of birth, represented as YYYY

      • placeOfBirth: string | null* Person's place of birth

      • pepSanctionMatch: string | null* Legacy field, may return incorrect result, should be ignored

      • citizenship: string | null* Deprecated, always returns null

    • document: object* Verified document

      • type: string | null Document type

      • number: string | null Document number, [a-zA-Z0-9] characters only

      • country: string | null Document issuing country, represented as ISO 3166 alpha-2 code

      • validUntil: string | null Document is valid until date, represented as YYYY-MM-DD. Optional, must be configured for your integration by the Solutions Engineer

      • validFrom: string | null* Document is valid from date, represented as YYYY-MM-DD

    • additionalVerifiedData: object* Data which has been optionally verified for session

      • proofOfAddress: object* Proof of address data. Optional, available only if the name match check has been enabled for the integration

        • documentType : 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.

    • comments: array* (Deprecated)

  • technicalData: object* Technical data

    • ip: 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
            },
         },
    }
    "technicalData": {
        "ip": null
}

Response properties explained

  • status: string Status of the response

  • verification: object Verification request decision object. null if decision is not available yet

    • id: string UUID v4 which identifies the verification session

    • attemptId: string UUID v4 of the attempt which received a status (as shown in verification.status field)

    • vendorData: string | null The unique identifier that you created for your end-user. null if not specified

    • endUserId: string | null The UUID that you created for your end-user. null if not specified

    • status: string Verification status, one of approved, declined, resubmission_requested, review, expired, abandoned

    • code: integer Verification session decision code, one of 9001, 9102, 9103, 9104, 9121. For more info, see the verification session decision codes

    • reason: string | null Reason why the verification failed, only sent when the verification failed

    • reasonCode: integer | null Reason code of the failed verification, only sent when the verification failed. For more info, see the possible codes for a failed verification

    • decisionTime: string Timestamp of the decision, represented as UTC YYYY-MM-DDTHH:MM:SS.SSS+Timezone Offset (ISO 8601)

    • acceptanceTime: string Timestamp of the session generation, represented as UTC YYYY-MM-DDTHH:MM:SS.SSS+Timezone Offset (ISO 8601)

    • person: object Data about the verified person

      • gender: string | null Person’s gender, represented as M or F, or null if not present

      • idNumber: string | null National identification number

      • lastName: string | null null, unless prefilled by the customer

      • addresses: array Array of person's addresses

        • fullAddress: string Address as a single string

      • firstName: string | null Person's first name as written on the document, if available

      • dateOfBirth: string Person’s date of birth, represented as YYYY-MM-DD

      • nationality: string | null Person's nationality

      • yearOfBirth: string | null Person’s year of birth, represented as YYYY

      • placeOfBirth: string | null Person's place of birth

      • pepSanctionMatch: string | null Legacy field, may return incorrect result, should be ignored

      • citizenship: string | null Deprecated, always returns null

    • document: object Verified document

      • type: string | null Document type

      • number: string | null Document number, [a-zA-Z0-9] characters only

      • country: string | null Document issuing country, represented as ISO 3166 alpha-2 code

      • validUntil: string | null Document is valid until date, represented as YYYY-MM-DD. Optional, must be configured for your integration by the Solutions Engineer

      • validFrom: string | null Document is valid from date, represented as YYYY-MM-DD

    • additionalVerifiedData: object Data which has been optionally verified for session

      • proofOfAddress: object Proof of address data. Optional, available only if the name match check has been enabled for the integration

        • documentType : 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.

    • comments: array* (Deprecated)

  • technicalData: object* Technical data

    • ip: 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.


Status and reason codes

For an approved session, see:

  • verification.code about verification session decision code, one of 9001, 9102, 9103, 9104, 9121

  • verification.status about verification status, one of approved, 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

verification.status

verification.reasonCode

verification.reason

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:


Article Versioning

Date

Description

May 2, 2025

Documentation published