Verification of Payee (VOP)#
Verification of Payee (VOP) is an optional security feature that allows TPPs to verify that the payee name matches the account holder name before initiating a payment. This helps prevent payment fraud and misdirected payments.
When VOP is enabled for a payment initiation request, the system will check the provided payee name against the actual account holder name for the given IBAN. Based on the verification result, the payment may proceed automatically (for exact matches) or require explicit user confirmation (for fuzzy matches or no matches).
Note
VOP functionality requires authentication and the same headers as regular payment initiation. See the authentication documentation for the full list of required headers.
How VOP Works#
Payment Request with VOP: Include
"execute_verification_of_payee": truein your payment initiation requestName Verification: The system checks the provided payee name against the account holder name
Match Results:
Perfect Match: Payment proceeds automatically
Fuzzy Match/No Match: Payment requires user confirmation via a separate endpoint
POST /api/v2/payment-initiation/ with VOP#
To enable VOP verification, include the execute_verification_of_payee parameter in your payment request.
Example request with VOP enabled:
POST /api/v2/payment-initiation/ HTTP/1.1
Accept: application/json
Authorization: Bearer testJWTAccessToken
{
"payment_account": "b06d5478-0954-451a-a1e0-f1eeb25336be",
"amount": "100.00",
"unstructured_reference": "Concert ticket",
"execute_verification_of_payee": true,
"counterparty": {
"name": "John Doe",
"account_identifier": "FI7979977991294922",
"bic": "HOLVFIHH"
}
}
Perfect Match Response#
When the payee name perfectly matches the account holder name, the payment proceeds normally:
HTTP/1.1 201 CREATED
Vary: Accept
Content-Type: application/json
{
"uuid": "b923d102-127e-449a-a216-960836a229c5",
"amount": "100.00",
"currency": "EUR",
"method": "sepa",
"booking_date": null,
"due_date": null,
"execution_at": null,
"state": "unverified",
"direction": "out",
"is_credit": null,
"structured_reference": "",
"unstructured_reference": "Concert ticket",
"end_to_end_id": "",
"counterparty": {
"name": "John Doe",
"bic": "HOLVFIHH",
"account_identifier": "FI7979977991294922",
"account_identifier_type": "iban",
"street": "",
"building": "",
"city": "",
"postcode": "",
"region": "",
"additional_info": "",
"country": ""
}
}
Fuzzy Match/No Match Response#
When the payee name doesn’t perfectly match, you’ll receive a different response requiring confirmation:
HTTP/1.1 202 ACCEPTED
Vary: Accept
Content-Type: application/json
{
"payment_initialization_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"vault_vop_result": {
"request_uuid": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"match_result": "close-match",
"match_name": "Jon Doe"
},
"requires_confirmation": true
}
VOP Result Fields:
request_uuid: Unique identifier for the VOP verification requestmatch_result: One of"match","close-match","no-match", or"not-applicable"(the payee’s bank does not support Verification of Payee)match_name: (optional) The actual name found for the account holder
POST /api/v2/payment-initiation/{payment_initialization_id}/confirm-vop#
When VOP returns a fuzzy match or no match, use this endpoint to confirm and proceed with the payment after user review.
Example request:
POST /api/v2/payment-initiation/a1b2c3d4-e5f6-7890-abcd-ef1234567890/confirm-vop HTTP/1.1
Accept: application/json
Authorization: Bearer testJWTAccessToken
Example response:
HTTP/1.1 201 CREATED
Vary: Accept
Content-Type: application/json
{
"uuid": "b923d102-127e-449a-a216-960836a229c5",
"amount": "100.00",
"currency": "EUR",
"method": "sepa",
"booking_date": null,
"due_date": null,
"execution_at": null,
"state": "unverified",
"direction": "out",
"is_credit": null,
"structured_reference": "",
"unstructured_reference": "Concert ticket",
"end_to_end_id": "",
"counterparty": {
"name": "John Doe",
"bic": "HOLVFIHH",
"account_identifier": "FI7979977991294922",
"account_identifier_type": "iban",
"street": "",
"building": "",
"city": "",
"postcode": "",
"region": "",
"additional_info": "",
"country": ""
}
}
Bulk Verification of Payee#
Up to 100 payees can be verified in one request, for example all the counterparties of an upcoming
bulk payment initiation. The verification runs asynchronously: poll
status_url until status becomes completed and review the match_result of every payee before
initiating the bulk. Verification of Payee cannot be requested per payment inside a bulk.
POST /api/v2/payee-verification/{payment_account_uuid}/bulk/#
Example request:
POST /api/v2/payee-verification/b06d5478-0954-451a-a1e0-f1eeb25336be/bulk/ HTTP/1.1
Accept: application/json
Authorization: Bearer testJWTAccessToken
{
"verifications": [
{"name": "Acme Oy", "iban": "FI7979977997799742"},
{"name": "Jane Doe", "iban": "DE89370400440532013000"}
]
}
Example response:
HTTP/1.1 202 ACCEPTED
Vary: Accept
Content-Type: application/json
{
"bulk_request_uuid": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"status": "processing",
"status_url": "https://api.psd2.holvi.com/api/v2/payee-verification/b06d5478-0954-451a-a1e0-f1eeb25336be/bulk/f47ac10b-58cc-4372-a567-0e02b2c3d479/status/"
}
Parameters:
<list>verifications: The payees to verify, from 1 to 100.<string>name: Payee name to verify. Max length of 140 characters.<string>iban: IBAN of the payee account.
Status Codes:
202: The verification has been accepted. Pollstatus_urlfor the results.400: The request is invalid. Errors of a single payee are reported under its position inverifications; more than 100 payees is reported as{"verifications": ["Maximum 100 verifications per bulk request."]}.401: Unsuccessful authentication403: Permissions granted to the user do not include payment initiation, or the account is not accessible.503: The verification service is unavailable.
GET /api/v2/payee-verification/{payment_account_uuid}/bulk/{bulk_request_uuid}/status/#
Example response while the verification is running:
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"bulk_request_uuid": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"status": "processing",
"total_count": 2,
"completed_count": 1
}
Example response once completed:
{
"bulk_request_uuid": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"status": "completed",
"completed_at": "2026-06-12T09:15:42.123456Z",
"results": [
{
"name": "Acme Oy",
"iban": "FI7979977997799742",
"request_uuid": "0a1b2c3d-4e5f-6071-8293-a4b5c6d7e8f9",
"match_result": "match"
},
{
"name": "Jane Doe",
"iban": "DE89370400440532013000",
"request_uuid": "1b2c3d4e-5f60-7182-93a4-b5c6d7e8f90a",
"match_result": "close-match",
"match_name": "Jane A. Doe"
},
{
"name": "Acme GmbH",
"iban": "DE00000000000000000000",
"request_uuid": "2c3d4e5f-6071-8293-a4b5-c6d7e8f90a1b",
"match_result": "error",
"error": "BAD_IBAN",
"error_type": "BadIban"
}
]
}
Each entry of results carries the name and iban that were submitted, so the result can be matched
with its payee, followed by the same fields as a single VOP result, see above. Two more values of
match_result are possible:
not-applicable: The payee’s bank does not support Verification of Payee, so no result is available.error: The payee could not be verified. Anerror_typeofBadIbanmeans the IBAN must be corrected; any other value is a temporary failure and the payee can be verified again.
Status Codes:
200: No error401: Unsuccessful authentication403: Permissions granted to the user do not include payment initiation, or the verification belongs to another user.404: The verification does not exist.503: The verification service is unavailable.
Parameters#
Payment Initiation with VOP#
All standard payment initiation parameters apply, plus:
<boolean>execute_verification_of_payee: (optional) Set totrueto enable VOP verification. Defaults tofalse.
VOP Confirmation#
payment_initialization_id: The ID returned from the initial VOP payment request
Request Headers#
Same as standard payment initiation, for the bulk endpoints too (Digest is not needed on the GET status request):
Host: Must beapi.psd2.holvi.comDate: Must follow the format described in section 7.1.1.1 of RFC 7231Digest: See authentication documentation for more detailsSignature: See authentication documentation for more detailsX-Holvi-Client-Id: Client ID provided by HolviX-Holvi-Client-Secret: Client secret provided by HolviAuthorization: JWT token to authenticate
Status Codes#
Payment Initiation with VOP#
201: Payment created successfully (perfect match or VOP disabled)202: VOP verification requires user confirmation400: VOP verification failed or invalid request401: Unsuccessful authentication403: Insufficient permissions503: Service unavailable
VOP Confirmation#
201: Payment confirmed and created successfully401: Unsuccessful authentication403: Insufficient permissions404: Payment initialization not found or not in confirmable state503: Service unavailable
Error Handling#
VOP Service Errors#
When the VOP service is unavailable or returns an error:
HTTP/1.1 400 BAD REQUEST
Vary: Accept
Content-Type: application/json
{
"error": "VOP verification failed",
"details": "VOP service unavailable: Connection timeout"
}
Missing Required Fields#
VOP requires both payee name and IBAN. If either is missing:
HTTP/1.1 400 BAD REQUEST
Vary: Accept
Content-Type: application/json
{
"error": "VOP verification failed",
"details": "Missing payee name or IBAN for VOP"
}
Implementation Notes#
Performance: VOP adds an additional verification step, which may increase payment initiation time by 1-2 seconds
User Experience: For fuzzy matches, implement a clear UI showing the expected vs. actual payee names
Audit Trail: All VOP requests are logged with unique request UUIDs for audit purposes