Skip to content

Bulk payout via SBP

This page describes two methods: creating a registry of SBP payouts and getting its result. A registry is processed asynchronously: creating it returns only a task identifier, and the result for each payout is requested separately.

Integration flow

  1. Get the recipient's bank_id for every payout from the bank list.
  2. Prepare the list of payouts. Generate a separate unique transaction for every payout.
  3. Sign the exact request body and send the registry.
  4. Save id from the response — this is the task identifier (task_id) used to request the result.
  5. Poll the bulk payout result until the task reaches the SUCCESS or FAILURE state.
  6. Get the final status of each payout by its transaction via the transaction status method or a callback.

A 200 response doesn't confirm that payouts were created

A 200 response only means that the registry was saved and the task was queued. No payout and no transaction is created at this step. Payouts are created in the background — one by one, sequentially, in the order of the items elements.

Limits and errors of individual payouts

Shop limits are checked for every payout separately at the moment it is created. An error in one payout doesn't stop the registry: the remaining payouts are processed, and the failed element is returned in the task result with an error code and description.

Shops with OTP payout confirmation

A registry doesn't support one-time code confirmation: there is no confirmation step for a bulk payout. If OTP is enabled for the shop, use a single SBP payout and payout confirmation.

Creating a registry

URL

POST https://api.pay.kvell.group/v1/orders/payout/sbp/bulk
POST https://api.pay.stage.kvell.group/v1/orders/payout/sbp/bulk

Headers

Name Type Required Description
X-Api-Key string Yes Shop identifier.
X-Signature string Yes Signature of the request body.

Building the signature

The request must be signed with an RSA/SHA-256 electronic signature. Pass the result in the X-Signature header.

The step-by-step algorithm, key requirements, examples for Python, PHP and OpenSSL, and troubleshooting for error 20002 are given in the general section «Building a signature for payouts».

Request body

Parameter Type Required Description
name string Yes Registry name in the merchant's system. Used to look the registry up.
items array Yes List of payouts, at least one element. The element structure is below.

items array element

Parameter Type Required Description
phone string Yes Recipient's phone number: 11 digits, without +, spaces or separators. Example: 79991234567.
fio string Yes Recipient's full name. For banks that don't accept Ё/ё, use the agreed variant with Е/е.
bank_id string Yes Bank identifier from the bank list.
amount integer Yes Amount in kopecks. For example, 15000 is 150 RUB.
transaction string Yes Unique operation identifier in the merchant's system.
description string Yes Purpose of the payout, no more than 110 characters.
fio_check boolean No true — check that the given full name matches the one received from NSPK.
bank_bic string Conditional BIC from the bank list response. Whether it is required depends on the configured acquiring bank.
nspk_id string Conditional Payment link obtained during the payout feasibility check. Valid for no more than 180 seconds after the check.
request_id string Conditional Identifier of the payout feasibility check request. Whether it is required depends on the configured acquiring bank.
customer string No Customer's email or phone number in the merchant's system.
extra_data object No Additional merchant data.
fiscal_data object No Data for receipt fiscalization under 54-FZ.

Conditional parameters

Whether request_id and bank_bic are required depends on the acquiring bank that handles the shop's payouts. If the integration scheme is unknown, confirm it with your KVELL manager before going to Production.

Example

{
  "name": "SBP payouts 10.08.2026",
  "items": [
    {
      "phone": "79991234567",
      "fio": "Иванов Иван Иванович",
      "bank_id": "100000000008",
      "amount": 15000,
      "transaction": "payout-sbp-20260810-0001",
      "description": "Payout under contract 42"
    },
    {
      "phone": "79997654321",
      "fio": "Петров Пётр Петрович",
      "bank_id": "100000000008",
      "amount": 27000,
      "transaction": "payout-sbp-20260810-0002",
      "description": "Payout under contract 43"
    }
  ]
}
curl --request POST \
  --url 'https://api.pay.stage.kvell.group/v1/orders/payout/sbp/bulk' \
  --header 'X-Api-Key: <api-key>' \
  --header 'X-Signature: <signature>' \
  --header 'Content-Type: application/json' \
  --data '{"name":"SBP payouts 10.08.2026","items":[{"phone":"79991234567","fio":"Иванов Иван Иванович","bank_id":"100000000008","amount":15000,"transaction":"payout-sbp-20260810-0001","description":"Payout under contract 42"}]}'

Response

Select an HTTP code to see the example, response parameters and recommended actions.

If no HTTP response is received

Handle a timeout or a dropped connection the same way as a 5XX response: it is unknown whether the task was queued. Don't resend the registry until you make sure that no payouts were created for its transaction values.

Example of a 200 (OK) response
{
  "id": "1d5b2f5a-8f0a-4c27-9c53-2f7a8a1b4d10",
  "status": "PENDING"
}

Response parameters

Parameter Type Description
id string Task identifier (task_id). Pass it to the result method.
status string Task state at the moment it was queued. Values are listed in «Task states».

What the response means

The registry is saved and the task is queued. No payouts have been created yet and there are no transactions in KVELL.

What to do next

  1. Store id on your side: without it the registry result can't be retrieved.
  2. Poll the bulk payout result until the SUCCESS or FAILURE state.
Example of a 400 (Bad Request) response
{
  "errors": [
    {
      "code": 20003,
      "message": "Payout профиль не привязан к магазину"
    }
  ]
}

Response parameters

Parameter Type Description
errors array List of errors. Codes and handling recommendations are given in «HTTP response errors».
∟ code integer Error code.
∟ message string Reason why the request was rejected.
Example of a 404 (Not Found) response
{
  "errors": [
    {
      "code": 20006,
      "message": "Магазин не найден"
    }
  ]
}

Response parameters

Parameter Type Description
errors array List of errors. Codes and handling recommendations are given in «HTTP response errors».
∟ code integer Error code.
∟ message string Reason why the resource wasn't found.
Example of a 422 (Unprocessable Entity) response
{
  "errors": [
    {
      "code": 20098,
      "message": "items.0.phone: Field required"
    }
  ]
}

Response parameters

Parameter Type Description
errors array List of validation errors. Codes and handling recommendations are given in «HTTP response errors».
∟ code integer Error code.
∟ message string Reason for the validation error, with the field name.

What the response means

The registry failed format validation: a single invalid items element is enough for the whole request to be rejected. The registry wasn't saved and no payouts were created.

What to do next

  1. Fix the fields listed in errors[].message and make sure items contains at least one element.
  2. Build a new signature for the corrected body and resend the registry with the same transaction values.
Example of a 5XX (Internal Server Error) response
{
  "errors": [
    {
      "code": 20000,
      "message": "Неизвестная ошибка"
    }
  ]
}

Response parameters

Parameter Type Description
errors array List of errors. Codes and handling recommendations are given in «HTTP response errors».
∟ code integer Error code.
∟ message string Description of the technical error.

5XX, timeout and dropped connection

The result of the request is considered undefined: the task could have been queued even if the client received no response. Resending the same registry without a check leads to duplicate payout attempts.

Unified handling algorithm

  1. Don't treat the registry as accepted or rejected based on a technical error alone.
  2. If you didn't receive the task id, check transaction status for the transaction values of the registry.
  3. If no transaction is found, resend the registry with the same transaction values.
  4. If transactions are found, don't resend the registry: wait for their final statuses.
  5. If requests keep failing, contact support and provide the registry name and the request time.

Don't create duplicate payouts

Don't change transaction when resending a registry. A payout with an already used transaction is rejected with code 20007, and this protects you from paying twice.

Getting the result

The method returns the state of the registry processing task and, once it is finished, the result for each payout. It doesn't change the state of the operations.

Polling interval

The recommended interval between requests is at least 1 minute. A registry is processed sequentially, so the processing time grows with the number of payouts.

The SUCCESS state refers to the task, not to the payouts

SUCCESS means that the registry was processed to the end. Individual payouts inside it may have failed or may still be processed by the bank.

URL

GET https://api.pay.kvell.group/v1/orders/payout/sbp/bulk/{task_id}
GET https://api.pay.stage.kvell.group/v1/orders/payout/sbp/bulk/{task_id}

Path parameters

Parameter Type Required Description
task_id string Yes Task identifier — the id field from the bulk payout response.

Headers

Name Type Required Description
X-Api-Key string Yes Shop identifier.
X-Signature string Yes Signature of the request.

Building the signature

Concatenate X-Api-Key, task_id and secret_key without separators, compute SHA-256 of the resulting UTF-8 string and pass the lowercase hex result in the X-Signature header.

SHA256(X-Api-Key + task_id + secret_key)

secret_key is available in the shop settings. The order of the values can't be changed. Request the result with the same X-Api-Key that was used to create the registry.

Example

curl --request GET \
  --url 'https://api.pay.stage.kvell.group/v1/orders/payout/sbp/bulk/1d5b2f5a-8f0a-4c27-9c53-2f7a8a1b4d10' \
  --header 'X-Api-Key: <api-key>' \
  --header 'X-Signature: <signature>'

Response

Select an HTTP code to see the example, response parameters and recommended actions.

If no HTTP response is received

The method only reads the task state and doesn't change the state of the payouts. On 5XX, a timeout or a dropped connection it is safe to repeat the same request with the same task_id.

Example of a 200 (OK) response
{
  "state": "SUCCESS",
  "response": [
    {
      "id": "3b3c19f4-c680-4f5d-bf2e-c0835439cf12",
      "status": "processing",
      "transaction": "payout-sbp-20260810-0001",
      "amount": 15000,
      "commission": 150,
      "description": "Payout under contract 42",
      "additional_data": null,
      "error_code": null,
      "error_message": null,
      "created_at": "2026-08-10T09:15:27.231000+00:00"
    },
    {
      "transaction": "payout-sbp-20260810-0002",
      "code": "20019",
      "message": "Превышен лимит выплат по магазину"
    }
  ]
}

Response parameters

Parameter Type Description
state string Task state. Possible values are listed in «Task states».
response array | null Payout results. Filled in only when state is SUCCESS, otherwise — null.
∟ id string Payout identifier in KVELL. Absent in an element with an error.
∟ status string Payout status. Possible values are listed in «Payout statuses».
∟ transaction string Operation identifier in the merchant's system. Returned both in a successful element and in an error element.
∟ amount integer Payout amount in kopecks.
∟ commission integer Commission in kopecks.
∟ description string | null Purpose of the payout. May include the prefix configured for the shop.
∟ additional_data object | null Additional operation data, if it was produced during processing.
∟ error_code string | null Cancellation reason code. Possible values are listed in «Transaction error codes». For an unfinished payout — null.
∟ error_message string | null Cancellation reason description. For an unfinished payout — null.
∟ created_at string Payout creation date and time in ISO 8601 format.
∟ code string Payout error code. Present only in an error element. The codes are listed in the «HTTP response errors» reference.
∟ message string Payout error description. Present only in an error element.

What the response means

A 200 response is returned in any task state, including while the registry is still being processed. The order of response elements matches the order of items in the registry request. Match elements by transaction, not by index.

What to do next

  1. If state is neither SUCCESS nor FAILURE, repeat the request later.
  2. On SUCCESS parse response: elements with id are created payouts, elements with code and message are payouts that couldn't be created.
  3. Payouts from error elements weren't created. Fix the cause and send them in a new registry with the same transaction values.
  4. For created payouts wait for the final status completed or canceled.
Example of a 400 (Bad Request) response
{
  "errors": [
    {
      "code": 20002,
      "message": "Неверная подпись"
    }
  ]
}

Response parameters

Parameter Type Description
errors array List of errors. Codes and handling recommendations are given in «HTTP response errors».
∟ code integer Error code.
∟ message string Reason why the request was rejected.
Example of a 404 (Not Found) response
{
  "errors": [
    {
      "code": 20006,
      "message": "Магазин не найден"
    }
  ]
}

Response parameters

Parameter Type Description
errors array List of errors. Codes and handling recommendations are given in «HTTP response errors».
∟ code integer Error code.
∟ message string Reason why the resource wasn't found.

What the response means

No shop with the given X-Api-Key was found. An unknown or expired task_id is not returned as 404 — see «Task states».

Example of a 5XX (Internal Server Error) response
{
  "errors": [
    {
      "code": 20000,
      "message": "Неизвестная ошибка"
    }
  ]
}

Response parameters

Parameter Type Description
errors array List of errors. Codes and handling recommendations are given in «HTTP response errors».
∟ code integer Error code.
∟ message string Description of the technical error.

What the response means

A technical error of the result request itself. It doesn't change the task state and doesn't determine the result of the registry payouts.

What to do next

  1. Safely repeat the same request with the same task_id after the recommended interval.
  2. If the error repeats, get the payout statuses by their transaction via transaction status.
  3. If those requests fail too, contact support and provide the task_id.

Task states

State Final What to do
PENDING No The registry hasn't been processed yet. Keep polling. The same state is returned for an unknown task_id.
SUCCESS Yes The registry was processed. Parse response.
FAILURE Yes Registry processing was interrupted by an error. response is returned as null.

PENDING for an unknown task_id

A task is stored for a limited time, and an unknown identifier is indistinguishable from a task that hasn't started: both return PENDING. If the state doesn't change longer than the expected processing time of the registry, check the task_id and get the payout statuses by their transaction.

Other states

Besides the listed ones, internal task queue states may be returned. Treat any state other than SUCCESS and FAILURE as intermediate and keep polling.

What to do on FAILURE

Some payouts of the registry may have been created before processing was interrupted. Don't resend the registry until you check every transaction of it via transaction status. Resend only the payouts whose transactions weren't found, keeping their original transaction values.