payout / developers
Guide

Identity verification

On this page

Note

This API is under active development and may change in future releases.

PayoutID also offers identity verification and AML checks. You create an invitation and send the user to the page it returns; PayoutID collects the user's data and documents and then sends the user back to you. Because some checks finish asynchronously, the results arrive as webhooks.

Payout ID verification diagram

  1. Get an access token. Use the client credentials grant at Retrieve token with the scopes you need, see Scopes.
  2. Create an invitation. Call Create invitation with your callback_url, notify_url and, for AML checks, aml_notify_url. The response contains the invitation id and a redirect_url.
  3. Redirect the user. Send the user's browser to redirect_url. When the user has finished, PayoutID sends them to your callback_url.
  4. Wait for the webhooks. The results arrive at notify_url and aml_notify_url, see Webhooks.

Scopes

Scope Required
verify Always
account_info When the invitation requests the user's bank account details (bank_account_requested: true)
aml When the invitation requests AML checks (aml_requested: true)

Without a required scope, the invitation is refused with HTTP 422.

Webhooks

There are two webhooks, both sent with POST:

  1. Identity verification webhook, sent to notify_url.
  2. AML check webhook, sent to aml_notify_url.

Both are signed. Verify the signature of every webhook, see Signature, to reject forged calls.

Identity verification webhook

Contains the result of the identity verification and, if requested, the user's bank account details. Every attribute in data is always present; a value that is not available is null.

Path Type Example Description
type string IDENTITY_CHECK Webhook type. Not present when data.identity_verification_failed is true
data object The verification data
data.id string edf29e60-45a5-463b-b120-033e835df3ce ID of the invitation, as returned when you created it
data.provided_email string [email protected] E-mail address provided by the user
data.provided_name string John First name provided by the user
data.provided_surname string Doe Last name provided by the user
data.provided_is_sanctioned boolean false The user's answer to whether they are on a sanctions list
data.provided_is_pep boolean false The user's answer to whether they are a politically exposed person
data.identity_verification_failed boolean false true when the identity verification failed
data.bank_account_requested boolean false The invitation requested the owner of the user's bank account
data.bank_account_unsupported_integration boolean false The user said their bank is not among the supported banks
data.bank_account_owner_name string or null John Doe Account owner name retrieved from the bank; several owners are separated by , . null if it could not be retrieved or was not requested
data.bank_account_iban string or null SK3112000000198742637541 IBAN retrieved from the bank. null if the user did not give access or it was not requested
data.overall string APPROVED Final result of the verification
data.finish_time string 2026-03-12T13:16:44Z When the verification finished
data.document_type string passport Document the user verified with: identity-card or passport
data.document_number string AB1234567 Number of the document
data.document_valid_until string 2030-03-09 Expiry date of the document
data.document_first_name string JOHN First name read from the document
data.document_last_name string DOE Last name read from the document
data.document_sex string M Sex read from the document
data.document_nationality string SVK Nationality read from the document (ISO 3166-1 alpha-3)
data.document_issuing_country string SVK Issuing country of the document (ISO 3166-1 alpha-3)
data.document_birth_place string BRATISLAVA Place of birth read from the document
data.document_personal_code string 800101/1234 Personal identification number read from the document
data.document_date_of_birth string 1980-01-01 Date of birth read from the document
data.document_front_url string Download URL of the front of the document. Expires after one hour
data.document_back_url string Download URL of the back of the document. Expires after one hour
data.photo_face_url string Download URL of the user's face photo. Expires after one hour
data.platform, data.start_time, data.client_ip, data.client_ip_country, data.client_location, data.suspicion_reasons, data.mismatch_tags, data.fraud_tags, data.auto_document, data.auto_face, data.manual_document, data.manual_face, data.aditional_steps Currently not filled: always null. They are part of the signature
signature string Signature of the webhook, see Signature
nonce string Value used in the signature

When a webhook is retried, the three download URLs are null.

When identity_verification_failed is true, data contains only id, the provided_* attributes, the bank_account_* attributes and identity_verification_failed.

AML check webhook

Contains the results of the AML check. It is sent to aml_notify_url. As the monitoring continues, one invitation can receive several AML check webhooks.

Path Type Example Description
type string AML_CHECK Webhook type
data object The check data
data.id string edf29e60-45a5-463b-b120-033e835df3ce ID of the invitation, as returned when you created it
data.status_service_suspected boolean true At least one hit is not a false positive
data.status_service_used boolean true The screening service was used
data.status_service_found boolean true The screening returned one or more hits
data.status_check_successful boolean true The check completed
data.status_overall string SUSPECTED Overall result: NOT_SUSPECTED, SUSPECTED or PENDING
data.error_message string or null Error description, if any
data.uid string FAFQLS95W7Z0JI9HL13VZBMX6 ID of the check
data.aml_items array of AML items The hits. Not part of the signature, so an item may gain attributes without changing how the signature is verified
signature string Signature of the webhook, see Signature
nonce string Value used in the signature

AML item

Any attribute except is_active and false_positive can be null.

Path Type Example Description
name string JOHN First name on the list
surname string DOE Last name on the list
reason string BELARUS PROGRAM Reason for the listing
nationality string BELARUS Nationality on the list
dob string 31 AUG 1954 Date of birth on the list, in the list's format
suspicion string Sanctions Kind of hit, such as PEP, Sanctions or Adverse Media. Several kinds are separated by ;
list_number string Number of the list entry
list_name string OFAC Name of the list
score integer 100 Match score
last_update string 2022-03-16 Date of the last update of the entry
is_person boolean true The entry is a person
is_active boolean true The entry is still in force
false_positive boolean false The hit was dismissed as not a real match
linked_document string URL of the list entry
other_information string Additional information
checked_at string When the hit was checked

Examples

All examples on this page are signed with the client secret c57f41ac-3bfb-4bb5-b18f-00cca093d97b, so you can use them to test your signature check.

Successful identity verification:

JSON
{
  "type": "IDENTITY_CHECK",
  "data": {
    "platform": null,
    "start_time": null,
    "finish_time": "2026-03-12T13:16:44Z",
    "client_ip": null,
    "client_ip_country": null,
    "client_location": null,
    "overall": "APPROVED",
    "suspicion_reasons": null,
    "mismatch_tags": null,
    "fraud_tags": null,
    "auto_document": null,
    "auto_face": null,
    "manual_document": null,
    "manual_face": null,
    "aditional_steps": null,
    "document_valid_until": "2030-03-09",
    "document_type": "passport",
    "document_number": "AB1234567",
    "document_front_url": "https://storage.example.com/identity_verifications/13b0f350/document_front.png?X-Amz-Expires=3600&X-Amz-Signature=…",
    "document_back_url": "https://storage.example.com/identity_verifications/13b0f350/document_back.png?X-Amz-Expires=3600&X-Amz-Signature=…",
    "document_first_name": "JOHN",
    "document_last_name": "DOE",
    "document_sex": "M",
    "document_nationality": "SVK",
    "document_issuing_country": "SVK",
    "document_birth_place": "BRATISLAVA",
    "document_personal_code": "800101/1234",
    "document_date_of_birth": "1980-01-01",
    "photo_face_url": "https://storage.example.com/identity_verifications/13b0f350/photo_face.png?X-Amz-Expires=3600&X-Amz-Signature=…",
    "id": "13b0f350-e208-4440-8f7c-cdae9d597f6d",
    "provided_email": "[email protected]",
    "provided_name": "John",
    "provided_surname": "Doe",
    "provided_is_pep": false,
    "provided_is_sanctioned": false,
    "bank_account_requested": false,
    "bank_account_unsupported_integration": false,
    "bank_account_owner_name": null,
    "bank_account_iban": null,
    "identity_verification_failed": false
  },
  "nonce": "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXowMTIzNDU2Nzg5",
  "signature": "9f0a148105490db92a8e6755e7c79bd59dcc08c7a5311577b9ff2b7fcc83547c"
}

Failed identity verification:

JSON
{
  "data": {
    "id": "ac286ff1-4e3c-41c7-ab00-127605583d36",
    "provided_email": "[email protected]",
    "provided_name": "John",
    "provided_surname": "Doe",
    "provided_is_pep": false,
    "provided_is_sanctioned": false,
    "bank_account_requested": false,
    "bank_account_unsupported_integration": false,
    "bank_account_owner_name": null,
    "bank_account_iban": null,
    "identity_verification_failed": true
  },
  "nonce": "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXowMTIzNDU2Nzg5",
  "signature": "5f94a97a92ff99f2f20dd25d3d867187c03768316cd12fe859ea7908111e5bf2"
}

With bank account details (bank_account_requested: true), the same webhook also carries the owner and the IBAN:

JSON
{
  "bank_account_requested": true,
  "bank_account_unsupported_integration": false,
  "bank_account_owner_name": "John Doe",
  "bank_account_iban": "SK3112000000198742637541"
}

When the user says their bank is not supported, bank_account_unsupported_integration is true and both bank account attributes are null:

JSON
{
  "bank_account_requested": true,
  "bank_account_unsupported_integration": true,
  "bank_account_owner_name": null,
  "bank_account_iban": null
}

AML check:

JSON
{
  "type": "AML_CHECK",
  "data": {
    "id": "b93e7497-1726-4216-8516-df678d86ca03",
    "status_service_suspected": true,
    "status_service_used": true,
    "status_service_found": true,
    "status_check_successful": true,
    "status_overall": "SUSPECTED",
    "error_message": null,
    "uid": "FAFQLS95W7Z0JI9HL13VZBMX6",
    "aml_items": [
      {
        "name": "JOHN",
        "surname": "DOE",
        "reason": "BELARUS PROGRAM",
        "nationality": "BELARUS",
        "dob": "31 AUG 1954",
        "suspicion": "Sanctions",
        "list_number": null,
        "list_name": "OFAC",
        "score": 100,
        "last_update": "2022-03-16",
        "is_person": true,
        "is_active": true,
        "false_positive": false,
        "linked_document": "https://sanctionssearch.ofac.treas.gov/Details.aspx?id=9760",
        "other_information": null,
        "checked_at": null
      },
      {
        "name": "JOHN",
        "surname": "DOE",
        "reason": "EU.5971.83",
        "nationality": "BELARUS",
        "dob": "1954",
        "suspicion": "Sanctions",
        "list_number": null,
        "list_name": "EU",
        "score": 87,
        "last_update": "2022-07-08",
        "is_person": true,
        "is_active": true,
        "false_positive": true,
        "linked_document": null,
        "other_information": null,
        "checked_at": null
      }
    ]
  },
  "nonce": "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXowMTIzNDU2Nzg5",
  "signature": "c3801f133b53ac713f081fa1e98e6690406405f8a7d63185c8c80f5ca0b2de36"
}

Signature

Build the string to sign from the data values in the order given below, then the nonce, then your client secret, separated by |:

  • a string or number is used as it is;
  • true and false become true and false;
  • null and an empty array [] become an empty string;
  • an array is concatenated without a separator: ["test", "value"] becomes testvalue.

Hash the string with SHA-256 and encode the hash as lowercase hex. The result must equal signature. type is not signed.

For example, take this imaginary webhook with the attributes first_name, last_name, children, married, age and hobbies:

JSON
{
  "data": {"first_name": "John", "last_name": "Doe", "children": [], "married": true, "age": null, "hobbies": ["guitar", "skiing"]},
  "nonce": "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXowMTIzNDU2Nzg5",
  "signature": "bb165278ad8e9504649ac4cde2b3293f099523682d7e1dee38464b12e57a42e7"
}

With the client secret c57f41ac-3bfb-4bb5-b18f-00cca093d97b, the string to sign is $first_name|$last_name|$children|$married|$age|$hobbies|$nonce|$client_secret:

TEXT
John|Doe||true||guitarskiing|YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXowMTIzNDU2Nzg5|c57f41ac-3bfb-4bb5-b18f-00cca093d97b

Its SHA-256 as lowercase hex is the signature:

TEXT
bb165278ad8e9504649ac4cde2b3293f099523682d7e1dee38464b12e57a42e7

Order of the identity verification webhook

  1. platform
  2. start_time
  3. finish_time
  4. client_ip
  5. client_ip_country
  6. client_location
  7. overall
  8. suspicion_reasons
  9. mismatch_tags
  10. fraud_tags
  11. auto_document
  12. auto_face
  13. manual_document
  14. manual_face
  15. aditional_steps
  16. document_valid_until
  17. document_type
  18. document_number
  19. document_front_url
  20. document_back_url
  21. document_first_name
  22. document_last_name
  23. document_sex
  24. document_nationality
  25. document_issuing_country
  26. document_birth_place
  27. document_personal_code
  28. document_date_of_birth
  29. photo_face_url
  30. id
  31. provided_email
  32. provided_name
  33. provided_surname
  34. provided_is_pep
  35. provided_is_sanctioned
  36. bank_account_requested
  37. bank_account_unsupported_integration
  38. bank_account_owner_name
  39. bank_account_iban
  40. identity_verification_failed

When identity_verification_failed is true, the document attributes are left out and only these are signed:

  1. id
  2. provided_email
  3. provided_name
  4. provided_surname
  5. provided_is_pep
  6. provided_is_sanctioned
  7. bank_account_requested
  8. bank_account_unsupported_integration
  9. bank_account_owner_name
  10. bank_account_iban
  11. identity_verification_failed

Order of the AML check webhook

  1. id
  2. status_service_suspected
  3. status_service_used
  4. status_service_found
  5. status_check_successful
  6. status_overall
  7. error_message
  8. uid

Testing

The sandbox cannot process real banking data, so it uses a mock bank. The account owner name is always John Doe, and the IBAN depends on the bank the user selects:

Bank IBAN
UniCredit SK5111110000000002314003
Prima banka Two accounts to choose from: SK0431000000002333363431 and SK1531000000003150217305
Any other bank SK0431000000002333363431

If the invitation or the user specifies an IBAN that is not one of these, no owner name is returned.

Was this page helpful?