Identity verification
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.
- Get an access token. Use the client credentials grant at Retrieve token with the scopes you need, see Scopes.
- Create an invitation. Call Create invitation with your
callback_url,notify_urland, for AML checks,aml_notify_url. The response contains the invitationidand aredirect_url. - Redirect the user. Send the user's browser to
redirect_url. When the user has finished, PayoutID sends them to yourcallback_url. - Wait for the webhooks. The results arrive at
notify_urlandaml_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:
- Identity verification webhook, sent to
notify_url. - 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/ |
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:
{
"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:
{
"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:
{
"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:
{
"bank_account_requested": true,
"bank_account_unsupported_integration": true,
"bank_account_owner_name": null,
"bank_account_iban": null
}
AML check:
{
"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;
trueandfalsebecometrueandfalse;nulland an empty array[]become an empty string;- an array is concatenated without a separator:
["test", "value"]becomestestvalue.
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:
{
"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:
John|Doe||true||guitarskiing|YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXowMTIzNDU2Nzg5|c57f41ac-3bfb-4bb5-b18f-00cca093d97b
Its SHA-256 as lowercase hex is the signature:
bb165278ad8e9504649ac4cde2b3293f099523682d7e1dee38464b12e57a42e7
Order of the identity verification webhook
platformstart_timefinish_timeclient_ipclient_ip_countryclient_locationoverallsuspicion_reasonsmismatch_tagsfraud_tagsauto_documentauto_facemanual_documentmanual_faceaditional_stepsdocument_valid_untildocument_typedocument_numberdocument_front_urldocument_back_urldocument_first_namedocument_last_namedocument_sexdocument_nationalitydocument_issuing_countrydocument_birth_placedocument_personal_codedocument_date_of_birthphoto_face_urlidprovided_emailprovided_nameprovided_surnameprovided_is_pepprovided_is_sanctionedbank_account_requestedbank_account_unsupported_integrationbank_account_owner_namebank_account_ibanidentity_verification_failed
When identity_verification_failed is true, the document attributes are left out and only these are signed:
idprovided_emailprovided_nameprovided_surnameprovided_is_pepprovided_is_sanctionedbank_account_requestedbank_account_unsupported_integrationbank_account_owner_namebank_account_ibanidentity_verification_failed
Order of the AML check webhook
idstatus_service_suspectedstatus_service_usedstatus_service_foundstatus_check_successfulstatus_overallerror_messageuid
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.
- Need help? Contact support.
- Questions? Contact sales.
- Service status? status.payout.one.
- LLM? Read llms.txt.
