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#

  1. Payment Request with VOP: Include "execute_verification_of_payee": true in your payment initiation request

  2. Name Verification: The system checks the provided payee name against the account holder name

  3. 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 request

  • match_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. Poll status_url for the results.

  • 400: The request is invalid. Errors of a single payee are reported under its position in verifications; more than 100 payees is reported as {"verifications": ["Maximum 100 verifications per bulk request."]}.

  • 401: Unsuccessful authentication

  • 403: 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. An error_type of BadIban means the IBAN must be corrected; any other value is a temporary failure and the payee can be verified again.

Status Codes:

  • 200: No error

  • 401: Unsuccessful authentication

  • 403: 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 to true to enable VOP verification. Defaults to false.

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 be api.psd2.holvi.com

  • Date: Must follow the format described in section 7.1.1.1 of RFC 7231

  • Digest: See authentication documentation for more details

  • Signature: See authentication documentation for more details

  • X-Holvi-Client-Id: Client ID provided by Holvi

  • X-Holvi-Client-Secret: Client secret provided by Holvi

  • Authorization: JWT token to authenticate

Status Codes#

Payment Initiation with VOP#

  • 201: Payment created successfully (perfect match or VOP disabled)

  • 202: VOP verification requires user confirmation

  • 400: VOP verification failed or invalid request

  • 401: Unsuccessful authentication

  • 403: Insufficient permissions

  • 503: Service unavailable

VOP Confirmation#

  • 201: Payment confirmed and created successfully

  • 401: Unsuccessful authentication

  • 403: Insufficient permissions

  • 404: Payment initialization not found or not in confirmable state

  • 503: 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#

  1. Performance: VOP adds an additional verification step, which may increase payment initiation time by 1-2 seconds

  2. User Experience: For fuzzy matches, implement a clear UI showing the expected vs. actual payee names

  3. Audit Trail: All VOP requests are logged with unique request UUIDs for audit purposes