Loading...
Integration guides

Verified payments

Add account-ownership verification and an approval gate to an Open Banking payment


Beta

Verified payments are currently in beta. Request/response shapes might be subject to change. Please reach out to your Volt contact before integrating.

Overview

A verified payment is a standard Open Banking payment with an Account Ownership Verification step placed in front of settlement. Before the payment is sent to the bank, the payer authenticates at their bank — confirming they have access to the account they are paying from — and Volt matches their name against the account holder name on the bank record. Volt sends you the account holder data and the name-match result, and the payment pauses for your approval decision — only once you approve does Volt initiate the transfer.

It runs as a single payment on the Global API and works with any integration channel, , or the . Adding the X-Volt-Verified-Payment: true header to your existing POST /payments call is the only change required — the request body and the rest of your integration stay exactly the same. The verification and approval gate are surfaced through the usual paymentInitiationFlow, and you do not manage a separate account-access request; Volt handles the verification internally.

Availability

Verified payments are available for Open Banking (EU, UK) payments at institutions that support account verification. Talk to your Volt account manager to have verified payments enabled for your application.


How it works

A verified payment moves through two distinct ADDITIONAL_AUTHORIZATION_REQUIRED checkpoints:

  1. Account authorisation — the payer authorises at their bank, and Volt uses that authorisation to verify account ownership and read the account holder name.
  2. Approval gate — once verification and the name match complete, Volt sends you the account holder data and the name-match result, and the payment waits for your approve/refuse decision.

Steps

Authenticate with Volt

In order to use Volt's API you need to be authenticated. Use this to handle authentication with Volt's API.

Create the verified payment

Call POST /payments with your usual X-Volt-Initiation-Channel (here api) and add the X-Volt-Verified-Payment: true header. Everything else is the standard Open Banking payment body.

POST /payments
curl --request POST \
  --url https://gateway.volt.io/payments \
  --header 'authorization: Bearer <your access token>' \
  --header 'content-type: application/json' \
  --header 'idempotency-key: <your idempotency key>' \
  --header 'x-volt-api-version: 1' \
  --header 'x-volt-initiation-channel: api' \
  --header 'x-volt-verified-payment: true' \
  --data '{
    "currency": "EUR",
    "amount": 1000,
    "paymentReference": "ABCDE12345F6GH78X9",
    "internalReference": "your-internal-reference",
    "payer": {
      "email": "johndoe@example.com",
      "firstName": "John",
      "lastName": "Doe",
      "reference": "JDOE-101"
    },
    "device": {
      "ip": "192.168.0.1",
      "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X x.y; rv:42.0) Gecko/20100101 Firefox/42.0",
      "fingerprint": "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"
    },
    "paymentSystem": "OPEN_BANKING_EU",
    "openBankingEU": {
      "type": "SERVICES",
      "institutionId": "<institution id>",
      "accountIdentifiers": {
        "iban": "DE75512108001245126199"
      }
    },
    "communication": {
      "return": {
        "urls": {
          "unified": { "url": "https://mywebsite.com/payment" }
        }
      },
      "notifications": {
        "url": "https://mywebsite.com/webhooks"
      }
    },
    "termsAndConditionsAccepted": true
  }'

The payer name powers the match

Volt matches payer.firstName and payer.lastName against the account holder name returned by the bank, so supply them as accurately as you can. Volt sends you the match result so you can decide whether to approve the payment (see step 5).

The payment is created and immediately enters account-ownership verification, returning ADDITIONAL_AUTHORIZATION_REQUIRED. The paymentInitiationFlow guides the payer through bank authorisation — for most banks, a redirect:

POST /payments response
{
  "id": "fbd0a297-9c96-48c9-a85e-ef5d3d986e96",
  "status": "ADDITIONAL_AUTHORIZATION_REQUIRED", 
  "paymentInitiationFlow": {
    "status": "PROCESSING",
    "details": {
      "reason": "AWAITING_USER_REDIRECT",
      "redirect": {
        "url": "https://vo.lt/short",
        "directUrl": "https://myBank.com/authorisation?code=xyz"
      }
    }
  }
  // ...
}

Payer authorises at their bank

The payer completes authentication and authorisation within the selected bank interface. This first authorisation is what lets Volt verify the account. The payment stays at ADDITIONAL_AUTHORIZATION_REQUIRED until it completes.

Select the account, if prompted

If the verified account holder has more than one account, the payment pauses at status ACCOUNT_SELECTION and the paymentInitiationFlow returns an ACCOUNT_ID input. Its allowedValues list the candidate accounts, and its propertyPath tells you where to put your choice. Submit the chosen account with PATCH /payments/{id}, the same way you resolve any required input. With a single account, this step is skipped automatically.

Volt matches the name, calculates the score and notifies you

Once the payer has authorised, Volt confirms the payer has access to the account and matches the payer name you supplied against the account holder name. Volt then sends the match score and the account holder data to your notification URL so you can decide whether to proceed. The notification contains a paymentInitiationFlow object, which now asks for your approval decision:

You can configure payment notifications in the Fuzebox or pass it directly in the POST /payments request.

Name-match score notification
{
  "id": "fbd0a297-9c96-48c9-a85e-ef5d3d986e96",
  "status": "ADDITIONAL_AUTHORIZATION_REQUIRED",
  "score": 0.89,
  "accountData": {
    "accounts": [
      {
        "owner": "JANE DOE",
        "balances": [
          {
            "type": "AVAILABLE",
            "amount": {
              "value": 20,
              "currency": "EUR"
            }
          }
        ],
        "identifiers": {
          "iban": "DE69500105176334166465"
        }
      }
    ]
  },
  "paymentInitiationFlow": {
    "status": "WAITING_FOR_INPUT",
    "requiredInput": [
      {
        "name": "CUSTOMER_APPROVAL",
        "propertyPath": "customer.approval.status",
        "allowedValues": [
          {
            "name": "APPROVE",
            "value": "APPROVED",
            "description": null
          },
          {
            "name": "REFUSE",
            "value": "REFUSED",
            "description": null
          }
        ]
      }
    ]
  }
}

Account holder data is delivered once

The account holder data (accountData) is included only in this notification — it is not returned by GET /payments/{id}. Persist it from the webhook if you need it for your approval logic or your records.

The notification carries:

  • score — name-match score as a floating-point value (for example, 0.89); a higher value indicates a closer match between the payer name you supplied and the account holder name.
  • accountData.accounts[].owner — account holder name returned by the bank.
  • accountData.accounts[].balances — account balances, when available.
  • accountData.accounts[].identifiers — account identifiers, such as iban.
  • paymentInitiationFlow — asks for your approval decision through the CUSTOMER_APPROVAL required input.

When account holder data is unavailable

If Volt can't retrieve the account holder data, the accountData fields are omitted and score is 0.0. The approval gate still opens, so it remains your decision whether to approve or refuse the payment.

While you're weighing the approval decision, the payer is still on your checkout — if you're polling GET /payments/{id} on their behalf (for example, in a hosted checkout screen), the paymentInitiationFlow reports a different, purely informational shape:

GET /payments/{id} response — payer view
{
  "id": "fbd0a297-9c96-48c9-a85e-ef5d3d986e96",
  "status": "ADDITIONAL_AUTHORIZATION_REQUIRED",
  "paymentInitiationFlow": {
    "status": "PROCESSING",
    "details": {
      "reason": "AWAITING_CUSTOMER_APPROVAL"
    }
  }
  // ...
}

Two shapes, two audiences

The WAITING_FOR_INPUT/CUSTOMER_APPROVAL shape from the notification above is addressed to you — it's what you get back if you call GET/PATCH /payments/{id} directly. PROCESSING/AWAITING_CUSTOMER_APPROVAL is addressed to the payer — it's what a payer-facing checkout polling the same endpoint sees while your decision is still pending.

Submit your approval decision

Resolve the CUSTOMER_APPROVAL input by sending the decision to PATCH /payments/{id}. The value must be either APPROVED or REFUSED.

PATCH /payments/{id}
curl --request PATCH \
  --url https://gateway.volt.io/payments/fbd0a297-9c96-48c9-a85e-ef5d3d986e96 \
  --header 'authorization: Bearer <your access token>' \
  --header 'content-type: application/json' \
  --data '{
    "customer": {
      "approval": {
        "status": "APPROVED"
      }
    }
  }'
  • APPROVED → the payment advances to APPROVED_BY_RISK and Volt initiates the transfer.
  • REFUSED → the payment ends at REFUSED_BY_RISK and is not sent to the bank.

The approval gate stays open for 30 seconds. If no decision is submitted within that window, the payment is automatically refused (REFUSED_BY_RISK), so resolve the input promptly.

Payer authorises the payment

After approval, Volt initiates the payment, and the payer completes the process within the selected bank interface to authorise the transfer — using whichever authorisation flow the bank supports.

Receive the result

Poll the GET /payments/{id} endpoint until the payment reaches a final state, or rely on the payment notification (webhook) Volt sends when the status changes. On success, the payment reaches COMPLETED.

You can configure payment notifications in the Fuzebox or pass it directly in the POST /payments request.

Payment statuses

A verified payment uses the standard Volt payment statuses. The sequence specific to this flow is below; see the for the full reference.

StatusMeaning in the verified flowNotification
NEW_PAYMENTPayment created; account-ownership verification is starting.No
ADDITIONAL_AUTHORIZATION_REQUIREDEither the payer must authorise at their bank (verification), or — after verification — the payment is awaiting your approval decision.No
ACCOUNT_SELECTIONThe account holder has more than one account; one is being selected.No
APPROVED_BY_RISKYou approved the payment; Volt is initiating the transfer.No
REFUSED_BY_RISKYou refused the payment, or the approval gate timed out. The payment is not sent to the bank.Yes
BANK_REDIRECTThe payer is being redirected to the bank to authorise the transfer.Yes
COMPLETEDThe payment was instructed and accepted by the payer's bank.Yes
FAILEDThe verification authorisation could not be completed.Yes
CANCELLED_BY_USERThe payer declined the authorisation at their bank.Yes
ABANDONED_BY_USERThe payer abandoned the payment.Yes

Next steps

How is this guide?

Last updated on

On this page