Bulk Payment Initiation#

These endpoints allow a TPP to initiate several payments from one payment account of a Holvi customer with a single request, approved with a single Strong Customer Authentication (SCA).

The bulk is accepted only if the available balance of the account covers its total amount. After that the customer must approve it on their phone; the payments are created and sent only once the customer approves. You can poll the bulk status to follow the process and, once processed, read the state of every payment.

The endpoint supports SEPA, SEPA Instant and SWIFT (international) payments, up to 100 per bulk, and they are executed immediately: bulk payments cannot be scheduled. Verification of Payee is not available per payment inside a bulk; to check the payee names before initiating the bulk, see the bulk Verification of Payee endpoint.

Note

All these endpoints require authentication, see the authentication documentation for the full list of headers required for these endpoints.

POST /api/v2/payment-initiation/bulk/#

Example request:

POST /api/v2/payment-initiation/bulk/ HTTP/1.1
Accept: application/json
Authorization: Bearer testJWTAccessToken
Idempotency-Key: 5b1f2f0e-2b6a-4c1d-9c3a-8d1e6f7a9b0c
{
    "payment_account": "b06d5478-0954-451a-a1e0-f1eeb25336be",
    "payments": [
        {
            "amount": "20.00",
            "unstructured_reference": "Invoice 1001",
            "counterparty": {
                "name": "Jean Sibelius",
                "account_identifier": "FI7979977991294922",
                "account_identifier_type": "iban"
            }
        },
        {
            "amount": "15.50",
            "unstructured_reference": "Invoice 1002",
            "instant": true,
            "counterparty": {
                "name": "Aino Ackté",
                "account_identifier": "DE89370400440532013000",
                "account_identifier_type": "iban"
            }
        }
    ]
}

Example response:

HTTP/1.1 202 ACCEPTED
Vary: Accept
Content-Type: application/json
{
    "bulk_id": "3f0c1a2e-7b4d-4c8e-9a1f-2d3e4f5a6b7c",
    "status_url": "https://api.psd2.holvi.com/api/v2/payment-initiation/bulk/3f0c1a2e-7b4d-4c8e-9a1f-2d3e4f5a6b7c/status/"
}

Parameters:

  • <string> payment_account: Payment account UUID. All the payments of the bulk are debited from this account.

  • <list> payments: The payments to make, from 1 to 100. Each payment accepts the same fields as a single payment initiation, except payment_account (taken from the bulk), due_date (bulk payments cannot be scheduled) and execute_verification_of_payee (not available in a bulk).

    • <string> amount: Payment amount

    • <string> currency: (optional) ISO 4217 currency code. Defaults to the currency of the given payment_account. See list of supported currencies here.

    • <json> counterparty: Payment receiver, see payment initiation.

    • <string> unstructured_reference: (optional) Max length of 140 characters. Exactly one of structured_reference or unstructured_reference must be provided; sending both is rejected.

    • <string> structured_reference: (optional) Max length of 35 characters. Exactly one of structured_reference or unstructured_reference must be provided; sending both is rejected.

    • <string> end_to_end_id: (optional) Max length of 36 characters

    • <string> method: (optional) Allowed methods are sepa and international. Defaults to sepa.

    • <boolean> instant: (optional) Set to true to request a SEPA Instant payment. Defaults to false. It can only be used when method is set to sepa.

Request Headers:

  • 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

  • Idempotency-Key: Unique key chosen by the TPP for this bulk, max length of 255 characters. See idempotency below.

Status Codes:

  • 202: The bulk has been accepted and the customer has been asked to approve it. Poll status_url to follow it.

  • 400: The request is invalid, the available balance does not cover the total amount of the bulk, the Idempotency-Key header is missing, or the key was already used with a different request body. Errors of a single payment are reported under its position in payments, see below.

  • 401: Unsuccessful authentication

  • 403: Forbidden. The user does not have permission to perform this action.

    • Permissions granted to the user do not include payment initiation.

    • The user is not verified.

  • 409: A request with the same Idempotency-Key is still being processed. Retry shortly.

  • 503: The payment service is unavailable. Retry with the same Idempotency-Key.

Example error responses:

Errors detected while validating the request body are reported in a list with one entry per payment, empty for the payments that are valid:

HTTP/1.1 400 BAD REQUEST
Vary: Accept
Content-Type: application/json
{
    "payments": [
        {},
        {
            "counterparty": {
                "account_identifier": ["Checksum does not validate"]
            }
        }
    ]
}

Errors detected while accepting the bulk are reported in an object keyed by the index of the payment, whose value is either a message or an object with the field concerned:

{
    "payments": {
        "1": "The IBAN number is not part of the SEPA area. Please contact support@holvi.com for more details."
    }
}

Errors about the bulk as a whole are reported as a list of messages, under payments when they concern the list itself and under non_field_errors otherwise:

{
    "payments": ["Maximum 100 payments per bulk request."]
}
{
    "non_field_errors": ["Amount can't be greater than the account balance!"]
}

Idempotency#

The Idempotency-Key header is required. It protects the customer from being asked to approve, and pay, the same bulk twice when a request is retried, for example after a network timeout. Keys are scoped to the TPP and the customer: a key used by another TPP or for another customer is unrelated.

  • Retrying with the same key and the same request body returns the bulk created by the first request, with the same bulk_id. No second bulk is created and the customer is not asked again.

  • Reusing a key with a different request body is rejected with a 400.

  • While the first request is still being processed, a retry receives a 409.

HTTP/1.1 409 CONFLICT
Vary: Accept
Content-Type: application/json
{
    "error": "request_in_progress",
    "error_description": "A request with this Idempotency-Key is already being processed. Retry shortly."
}

Use a new key for every new bulk; a UUID is a good choice.

GET /api/v2/payment-initiation/bulk/{bulk_id}/status/#

Returns the current state of a bulk and, once it has been processed, the payments it contains, on top of the bulk_id and status_url returned by the bulk creation.

Example request:

GET /api/v2/payment-initiation/bulk/3f0c1a2e-7b4d-4c8e-9a1f-2d3e4f5a6b7c/status/ HTTP/1.1
Accept: application/json
Authorization: Bearer testJWTAccessToken

Example response while the customer has not answered yet:

HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
    "bulk_id": "3f0c1a2e-7b4d-4c8e-9a1f-2d3e4f5a6b7c",
    "status_url": "https://api.psd2.holvi.com/api/v2/payment-initiation/bulk/3f0c1a2e-7b4d-4c8e-9a1f-2d3e4f5a6b7c/status/",
    "status": "pending_approval",
    "processed": false,
    "total_count": 2,
    "total_amount": "35.50",
    "not_sent_count": null,
    "payments": []
}

Example response while the bulk is being processed:

{
    "bulk_id": "3f0c1a2e-7b4d-4c8e-9a1f-2d3e4f5a6b7c",
    "status_url": "https://api.psd2.holvi.com/api/v2/payment-initiation/bulk/3f0c1a2e-7b4d-4c8e-9a1f-2d3e4f5a6b7c/status/",
    "status": "processing",
    "processed": false,
    "total_count": 2,
    "total_amount": "35.50",
    "not_sent_count": null,
    "payments": []
}

Example response once the bulk has been approved and processed:

{
    "bulk_id": "3f0c1a2e-7b4d-4c8e-9a1f-2d3e4f5a6b7c",
    "status_url": "https://api.psd2.holvi.com/api/v2/payment-initiation/bulk/3f0c1a2e-7b4d-4c8e-9a1f-2d3e4f5a6b7c/status/",
    "status": "approved",
    "processed": true,
    "total_count": 2,
    "total_amount": "35.50",
    "not_sent_count": 1,
    "payments": [
        {
            "uuid": "9c6f2b0e-1d2a-4f3b-8c4d-5e6f7a8b9c0d",
            "amount": "20.00",
            "currency": "EUR",
            "method": "sepa",
            "booking_date": "2026-08-25",
            "due_date": null,
            "execution_at": "2026-08-25T10:15:03Z",
            "state": "paid",
            "direction": "out",
            "is_credit": false,
            "structured_reference": "",
            "unstructured_reference": "Invoice 1001",
            "end_to_end_id": "",
            "counterparty": {
                "name": "Jean Sibelius",
                "bic": "",
                "account_identifier": "FI7979977991294922",
                "account_identifier_type": "iban",
                "street": "",
                "building": "",
                "city": "",
                "postcode": "",
                "region": "",
                "additional_info": "",
                "country": ""
            }
        },
        {
            "uuid": "0d7a3c1f-2e3b-4a4c-9d5e-6f7a8b9c0d1e",
            "amount": "15.50",
            "currency": "EUR",
            "method": "sepa",
            "booking_date": null,
            "due_date": null,
            "execution_at": null,
            "state": "notenoughbalance",
            "direction": "out",
            "is_credit": false,
            "structured_reference": "",
            "unstructured_reference": "Invoice 1002",
            "end_to_end_id": "",
            "counterparty": {
                "name": "Aino Ackté",
                "bic": "",
                "account_identifier": "DE89370400440532013000",
                "account_identifier_type": "iban",
                "street": "",
                "building": "",
                "city": "",
                "postcode": "",
                "region": "",
                "additional_info": "",
                "country": ""
            }
        }
    ]
}

Example response when the customer declined, or did not answer within one hour:

{
    "bulk_id": "3f0c1a2e-7b4d-4c8e-9a1f-2d3e4f5a6b7c",
    "status_url": "https://api.psd2.holvi.com/api/v2/payment-initiation/bulk/3f0c1a2e-7b4d-4c8e-9a1f-2d3e4f5a6b7c/status/",
    "status": "rejected",
    "processed": true,
    "total_count": 2,
    "total_amount": "35.50",
    "not_sent_count": 2,
    "payments": []
}

Response body:

  • bulk_id: UUID of the bulk, use it to poll its status.

  • status_url: URL of this endpoint for the bulk.

  • status: One of the bulk payment states.

  • processed: true once the bulk has reached its final state, approved or rejected.

  • total_count: Number of payments in the bulk.

  • total_amount: Sum of the amounts of all the payments in the bulk, as given in the request and without currency conversion, so it is only meaningful when all the payments use the currency of the payment account, which they should.

  • not_sent_count: Number of payments that were not sent, null until the bulk is processed. If the balance drops between the approval and the sending, a payment it no longer covers is not sent and ends in the notenoughbalance state; the other payments of the bulk are still sent.

  • payments: The payments of the bulk, in the same shape as a single payment initiation. Empty until the bulk is processed.

Request Headers:

  • Host: Must be api.psd2.holvi.com

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

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

  • 200: No error

  • 401: Unsuccessful authentication

  • 403: Forbidden. Permissions granted to the user do not include payment initiation.

  • 404: The bulk does not exist for this TPP and customer.

  • 503: The payment service is unavailable.