Integration GuideAPI Reference
Integration Guide

H2H: Payouts to Saved Payee Cards

Bind a payee's card once and receive a paymentInstrumentToken starting with pi_. Use this token in disbursements instead of sending the card number every time. Binding does not create a payout.

📘

Payouts by token are available for DisbursementBankCardRUS and BankCardKZ. H2H tokenization must be enabled for the settlement account by the integration team. Payouts with card details work as before and do not require a token.

To save a customer's card for payments, see Card Tokenization via API. Customer cards and payee cards are kept separately: see Token scope.

Before you start

📘

Ask the integration team to enable H2H tokenization for your settlement account and to configure a payout route for the payment method you use.

Endpoint paths below are relative to the processing base URL for your environment. See Access URLs and API Authentication.

Request headers
HeaderValue
AuthorizationBearer <access_token>
settlement-account-idYour settlement account ID, such as SA204264682781298688
Content-Typeapplication/json for requests with a JSON body

Supported methods

Payment methodBind a payee cardPayout by token
DisbursementBankCardRUSSupportedSupported
BankCardKZSupportedSupported

Other payout methods, including Napas, do not support binding. The examples use BankCardKZ and KZT.

Identifiers and token scope

IdentifierPurpose
paymentInstrumentTokenThe reusable payee card token, such as pi_01J8ZC4M7QK3T5V9XB2NDR6HAF. Supply it in a disbursement instead of the card details
merchantPayeeReferenceYour stable payee identifier. Required when binding a card, paying out by token and deleting the binding. Use a nonblank string of up to 256 characters and keep it unchanged
settlement-account-idThe account within which the card is bound and paid out to
clientReferenceIdYour reference for the Intent, and for each disbursement inside it. Use a new value for every payout; it is separate from the stable payee reference

A payee card token belongs to one settlement account, one merchantPayeeReference and one payment method. A token bound for payee payee-12345 in account A cannot be used for another payee, another account or another payment method. Sandbox and production tokens are separate.

Customer cards saved during payments are not used for payouts, and a payee card token is not accepted in a payment.

flowchart LR
  bind["Bind the payee card"] --> token["pi_ token"]
  token -->|"Payout by token, any number of times"| token
  token -->|"Delete the binding"| deleted["Token unavailable"]
  deleted -->|"Bind the card again"| replacement["New pi_ token"]

Bind a payee card

API reference: Bind a payout card.

Endpoint: POST /api/v1/payment-instruments

{
  "paymentMethodName": "BankCardKZ",
  "merchantPayeeReference": "payee-12345",
  "incomingDetails": {
    "number": "4111111111111111"
  }
}
Request body
FieldTypeRequiredDescription
paymentMethodNameStringYesDisbursementBankCardRUS or BankCardKZ
merchantPayeeReferenceStringYesYour payee identifier, nonblank, up to 256 characters
incomingDetailsObjectYesThe payee's card; only the card number is accepted, see the object below
incomingDetails
FieldTypeRequiredDescription
numberStringYesCard number of 13 to 19 digits with a valid checksum

Do not send the expiry date, holder name or CVV: a request with any field other than number is rejected with HTTP 400. Payouts to a bound card need the card number only.

Binding the same card to the same payee with the same payment method again returns the same token. The same card bound to a different payee gets a different token.

⚠️

Store the token together with merchantPayeeReference and the masked number from this response. Payee card tokens cannot be listed or retrieved later: GET /api/v1/payment-instruments/{paymentInstrumentToken} answers 404 for them.


Pay out by token

Pass the token in payeePaymentInstrument and the payee reference in payee instead of the card details. Both ways of creating a disbursement accept a token:

EndpointUse
POST /api/v1/intentsCreate a new Intent with disbursements
POST /api/v1/intents/{intentId}/transactionsAdd disbursements to an existing Intent

Endpoint: POST /api/v1/intents

{
  "clientReferenceId": "payout-20261020-0001",
  "useCheckoutForm": false,
  "disbursements": [
    {
      "payee": {
        "merchantPayeeReference": "payee-12345"
      },
      "payeePaymentInstrument": {
        "paymentMethodName": "BankCardKZ",
        "paymentInstrumentToken": "pi_01J8ZC4M7QK3T5V9XB2NDR6HAF"
      },
      "submittedAmount": {
        "value": 12300.00,
        "currency": "KZT"
      },
      "authAmount": {
        "currency": "KZT"
      },
      "clientReferenceId": "payout-20261020-0001-1",
      "webhookUrl": "https://merchant.example.com/webhooks/disbursements"
    }
  ]
}

For POST /api/v1/intents/{intentId}/transactions, send the same disbursement objects in the disbursements array. Each element must carry its own clientReferenceId; see the API reference.

Token-specific fields of disbursements[]
FieldRequiredDescription
payee.merchantPayeeReferenceYesThe same payee reference used when the card was bound
payeePaymentInstrument.paymentMethodNameYesThe same payment method used when the card was bound
payeePaymentInstrument.paymentInstrumentTokenYesThe payee card token
payeePaymentInstrument.incomingDetailsNoOmit it. Card details sent together with a token are rejected with HTTP 400
payeePaymentInstrument.tokenPaymentReasonNoDo not send it. CIT and MIT apply to payments only; with a disbursement it is rejected

Other payee fields, including currentAddress, are optional. Amounts, webhookUrl and the other disbursement fields follow the usual rules: see Amount Fields: submittedAmount and authAmount, KZ: Bank Card - Disbursements and RU: Bank Card - Disbursements.

The token is checked before anything is created. If any disbursement in the request has an invalid token, the whole request is rejected with an HTTP error, and no Intent or disbursement is created.

A token has no expiry date and no limit on the number of payouts. Your payout limits and duplicate-payout checks apply as for payouts with card details. If the payee's card is reissued with the same number, the token keeps working; a card with a new number must be bound again.

The disbursement possibility check does not use the token: to check a payout, send the card details in that request, as before.


Delete a payee card

Endpoint: DELETE /api/v1/payment-instruments/{paymentInstrumentToken}

Example: DELETE /api/v1/payment-instruments/pi_01J8ZC4M7QK3T5V9XB2NDR6HAF?merchantPayeeReference=payee-12345

Parameters
ParameterInRequiredDescription
paymentInstrumentTokenPathYesThe payee card token
merchantPayeeReferenceQueryYesThe same payee reference used when binding

Do not combine merchantPayeeReference with payerId or merchantPayerReference: such a request is rejected with HTTP 400. Those parameters select customer cards; see Manage Saved Cards.

After deletion:

  • New payouts by this token are rejected with HTTP 404 and INSTRUMENT_NOT_FOUND.
  • Binding the same card again creates a new token.
  • Payouts already created are not affected: deletion does not cancel a payout in progress.

Errors

HTTP statuserrorCodeRequestMeaning
400400BindingUnsupported payment method, invalid card number, a field other than number in incomingDetails, or a blank or too long merchantPayeeReference
400400PayoutThe payment method does not support tokens, card details are sent together with the token, payee.merchantPayeeReference is missing or tokenPaymentReason is sent
400400DeletionmerchantPayeeReference is combined with payerId or merchantPayerReference
403CAPABILITY_DISABLEDBinding, payout, deletionH2H tokenization is not enabled for the settlement account
404INSTRUMENT_NOT_FOUNDPayout, deletionThe token is unknown or deleted, or it belongs to another settlement account or another payee. For a payout, also: another payment method, or a customer's saved card
502UPSTREAM_UNAVAILABLEBindingCard tokenization is temporarily unavailable. Nothing was bound; retry later

For a payout, 404 is returned in the same form in every case listed above, so the response does not reveal whether a token exists outside your scope. Check the token, the payee reference and the payment method.

Error response object
FieldTypeDescription
errorCodeNumber or stringNumeric 400 for an invalid request, or the named error code shown above
failureMessageStringExplanation of the error
timestampNumberTime the error response was generated, as a Unix timestamp in seconds; it can include a fractional part

Example HTTP 404 response excerpt:

{
  "errorCode": "INSTRUMENT_NOT_FOUND",
  "failureMessage": "Saved payment instrument not found"
}

Errors that occur after the disbursement is created, such as a decline by the bank, are reported as for any payout: see Creation Errors and Processing Errors.


Test the integration

Use a sandbox settlement account with H2H tokenization enabled and a payout route for the payment method. Test card numbers and status triggers are the same as for payouts with card details: see the test data in KZ: Bank Card - Disbursements and RU: Bank Card - Disbursements.

Cover the complete journey: bind a card, bind it again and check that the token is the same, pay out by the token through both endpoints, make several payouts by one token, try the token with another payee reference and receive 404, delete the binding, and check that a payout by the deleted token is rejected.


Did this page help you?