REST API
REST over HTTPS, JSON in both directions. Base — https://api.nerezpay.ru. Bearer key from the dashboard, optionally with an HMAC signature; Idempotency-Key on create operations.
Payments
Create a payment
/v1/public/paymentsamountAmount in minor units. For RUB — kopeks. Required.currencyISO-4217 currency code. Defaults to RUB.methodPayment method: sbp, cards, tpay, sberpay, recurrent or any. Defaults to any — customer picks on our hosted page.order_idYour order identifier. If omitted we generate ord_<timestamp>.customer_idYour customer identifier. Saved into metadata and used to group operations.return_urlURL the customer returns to after paying. Requires https://.descriptionPayment purpose. Shown in the customer receipt.webhook_urlPer-payment webhook receiver URL. Overrides shop settings.metadataArbitrary key-value pairs. Up to 20 pairs, values are strings up to 500 characters.curl -X POST https://api.nerezpay.ru/v1/public/payments \
-H 'Authorization: Bearer sk_test_x9k…' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: 7f2a-pay-1042' \
-d '{
"amount": 150000,
"currency": "RUB",
"method": "sbp",
"order_id": "ORDER-1042",
"customer_id": "user-7821",
"return_url": "https://example.com/order/1042/done"
}'{
"payment": {
"id": "5a331a39-32bf-4940-afb1-855e2fc6757f",
"merchant_id": "22222222-2222-2222-2222-222222222222",
"shop_id": "33333333-3333-3333-3333-333333333333",
"order_id": "ORDER-1042",
"amount": 150000,
"currency": "RUB",
"method": "sbp",
"status": "pending",
"expires_at": "2026-05-05T13:42:00Z",
"created_at": "2026-05-05T12:42:00Z"
},
"payment_url": "https://nerezpay.ru/pay/5a331a39-…",
"mode": "live",
"bank": "Sber", // acquiring bank name if it responded sync
"bank_ref": "262512345678" // operation ID at the bank (h2h only)
}idempotent: true, failure_code and failure_message are returned on a repeat request keyed by Idempotency-Key or a synchronous bank failure. In test mode the response also has simulator service fields — described on the Sandbox page.
Check payment status
/v1/public/payments/{id}curl -X GET https://api.nerezpay.ru/v1/public/payments/5a331a39-32bf-4940-afb1-855e2fc6757f \ -H 'Authorization: Bearer sk_test_x9k…' \ -H 'Content-Type: application/json'
{
"id": "5a331a39-32bf-4940-afb1-855e2fc6757f",
"merchant_id": "22222222-2222-2222-2222-222222222222",
"shop_id": "33333333-3333-3333-3333-333333333333",
"shop_name": "Web checkout",
"order_id": "ORDER-1042",
"status": "succeeded",
"amount": 150000,
"amount_refunded": 0,
"currency": "RUB",
"method": "sbp",
"rrn": "262512345678",
"bank_name": "Sber",
"return_url": "https://example.com/order/1042/done",
"created_at": "2026-05-05T12:42:00Z",
"captured_at": "2026-05-05T12:43:11Z",
"finalized_at": "2026-05-05T12:43:11Z",
"expires_at": "2026-05-05T13:42:00Z",
"processing_until":"2026-05-05T13:02:00Z",
"metadata": { "customer_id": "user-7821" },
"customer_card_brand": "visa",
"customer_card_mask": "411111******1111",
"customer_card_holder": "IVAN IVANOV",
"customer_phone_mask": "+7•••••••12-34",
"customer_payer_bank": "Sber",
"refunds": [
{
"id": "9b2c1f8a-1111-2222-3333-444444444444",
"payment_id": "5a331a39-32bf-4940-afb1-855e2fc6757f",
"amount": 50000,
"reason": "partial per request",
"status": "succeeded",
"created_at": "2026-05-05T15:10:00Z",
"completed_at": "2026-05-05T15:10:08Z"
}
]
}Cancel a payment
/v1/public/payments/{id}/cancelcurl -X POST https://api.nerezpay.ru/v1/public/payments/5a331a39-32bf-4940-afb1-855e2fc6757f/cancel \ -H 'Authorization: Bearer sk_test_x9k…' \ -H 'Content-Type: application/json'
{
"status": "cancelled"
}method any (multi-form)
When the merchant creates a payment with method: "any" (or without a method field at all), the response\u2019s payment_url points to our hosted page https://nerezpay.ru/pay/<id>. The customer picks a payment method themselves: card, SBP, T-Pay, SberPay or recurrent.
Lifecycle
- Payment is created with status
pendingand methods=any. No bank session yet,processing_untilis empty. - Customer opens
payment_url, sees method buttons. - Customer clicks "Pay via SBP" (for example). Method is fixed: status stays
pending, themethodfield changes fromanytosbp. No webhook on this step. - Bank session opens: status becomes
processing,processing_untilis filled with the method TTL (20 min for SBP). No webhook on this transition. - Bank returns a final status:
succeededorfailed. Apayment.succeededorpayment.failedwebhook is delivered.
processing_until, the payment moves to expired and the merchant gets a payment.expired webhook. Different semantics than failed: the bank didn\u2019t reject, it just never closed the session.Before a method is picked, the payment cannot be cancelled (cancel only works in pending — and it will here) or refunded (succeeded hasn\u2019t happened). The merchant can check status via GET /payments/{id} or wait for a webhook.
metadata field
metadata is arbitrary key-value pairs you pass when creating a payment. We don\u2019t interpret them, just store and echo them back. Convenient for cart IDs, traffic source, A/B-test tags and so on.
Limits
- All values are strings. Pass numbers and booleans as
"42"/"true"— we don\u2019t touch them. - We recommend no more than 20 pairs per payment and 500 characters per value. There’s no hard cap, but everything lives in a single JSONB column.
- Encoding is UTF-8.
Where you’ll see them
- In the
POST /paymentsresponse —payment.metadata. - In
GET /payments/{id}— same field. - In the webhook
dataobject.
Reserved keys
Alongside your data, metadata may contain system keys that we set. Nothing scary, just don\u2019t be surprised:
| key | when it appears |
|---|---|
mode | always. Value is test or live depending on the key used. |
webhook_url | if you passed webhook_url in the POST /payments body. |
customer_id | if you passed customer_id in the POST /payments body. |
bank_ref | live mode: operation ID at the acquiring bank. |
failure_code | on failed/expired: our unified failure code (see the "Failure codes" section). |
bank_failure_code | on failed: raw bank code. Audit-only — rely on failure_code in code. |
simulator_outcome | test mode: the planned simulator outcome (succeeded, failed, requires_action, pending). |
simulator_failure_code | test mode: the code the simulator passes off as the bank response. |
Don’t use these names for your own keys: on collision we overwrite your value.
Returning the customer to the shop
If you supplied return_url in POST /payments, after the payment reaches a final status we redirect the customer to that URL with two query parameters:
https://example.com/order/1042/done?payment_id=5a331a39-…&status=succeeded
| parameter | value |
|---|---|
payment_id | Payment UUID. |
status | succeeded | failed | cancelled | refunded | expired. |
Existing query parameters in your return_url are kept. If the URL already had ?ref=email, it stays — and our two are appended.
payment.succeeded webhook or an explicit GET /payments/{id}.When the redirect happens
- succeeded: ~2 seconds after the "Payment complete" screen.
- failed / cancelled / refunded / expired: immediately.
- If
return_urlisn\u2019t set we keep the customer on our status page.
Refunds
A refund is a standalone object attached to a payment. Multiple partial refunds are allowed on one payment; once their sum reaches the original amount, the payment\u2019s status becomes refunded. Every successful refund triggers a payment.refunded webhook.
Refund object
| Field | Type | Description |
|---|---|---|
id | UUID | Refund identifier. |
payment_id | UUID | Payment the refund belongs to. |
merchant_id | UUID | Merchant. Determined from the API key. |
amount | int64 | Refund amount in minor units. |
reason | string | Refund reason. Lands in the receipt and audit log. |
status | enum | pending → succeeded or failed. |
created_at | time | When the refund was created. |
completed_at | time | When it reached its final status. |
{
"id": "9b2c1f8a-1111-2222-3333-444444444444",
"payment_id": "5a331a39-32bf-4940-afb1-855e2fc6757f",
"merchant_id": "22222222-2222-2222-2222-222222222222",
"amount": 50000,
"reason": "partial refund per customer request",
"status": "succeeded",
"created_at": "2026-05-05T15:10:00Z",
"completed_at": "2026-05-05T15:10:02Z"
}Refund funds
/v1/public/payments/{id}/refundamountRefund amount in minor units. If omitted or 0 — refunds the entire remainder: amount − amount_refunded.reasonRefund reason. Shown to the customer in the receipt and recorded in the audit log.curl -X POST https://api.nerezpay.ru/v1/public/payments/5a331a39-32bf-4940-afb1-855e2fc6757f/refund \
-H 'Authorization: Bearer sk_test_x9k…' \
-H 'Content-Type: application/json' \
-d '{
"amount": 50000,
"reason": "partial refund per customer request"
}'{
"id": "9b2c1f8a-1111-2222-3333-444444444444",
"payment_id": "5a331a39-32bf-4940-afb1-855e2fc6757f",
"merchant_id": "22222222-2222-2222-2222-222222222222",
"amount": 50000,
"reason": "partial refund per customer request",
"status": "succeeded",
"created_at": "2026-05-05T15:10:00Z"
}There\u2019s no separate "get refund by id" endpoint in the public API. The current list of refunds is always returned in the refunds[] array of GET /v1/public/payments/{id}. A consolidated refund report per shop is available in the dashboard.
Payouts
SBP payout to an individual
/v1/public/payoutsamountPayout amount in minor units (kopeks). Required.methodPayout method. Only payouts_sbp is currently available; used by default.phoneRecipient phone in the +7XXXXXXXXXX format. Required.bank_idRecipient bank’s member_id in the NSPK registry (12 digits). Example: 100000000111 — Sberbank. Required.full_nameRecipient full name in one line. Used by the bank for additional matching.curl -X POST https://api.nerezpay.ru/v1/public/payouts \
-H 'Authorization: Bearer sk_test_x9k…' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: 7f2a-payout-001' \
-d '{
"amount": 250000,
"method": "payouts_sbp",
"phone": "+79001234567",
"bank_id": "100000000111",
"full_name": "Иванов Иван Иванович"
}'{
"payout": {
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"merchant_id": "22222222-2222-2222-2222-222222222222",
"shop_id": "33333333-3333-3333-3333-333333333333",
"amount": 250000,
"currency": "RUB",
"method": "payouts_sbp",
"status": "pending",
"recipient": {
"phone": "+79001234567",
"bank_id": "100000000111",
"full_name": "Иванов Иван Иванович"
},
"created_at": "2026-05-05T16:01:00Z"
},
"mode": "live",
"bank": "Sber",
"bank_ref": "PAY2026050601234"
}failure_reason and idempotent fields are returned on failure or a repeated request keyed by Idempotency-Key. In test mode the response also has simulator service fields — described on the Sandbox page.
Payout status
/v1/public/payouts/{id}curl -X GET https://api.nerezpay.ru/v1/public/payouts/f47ac10b-58cc-4372-a567-0e02b2c3d479 \ -H 'Authorization: Bearer sk_test_x9k…' \ -H 'Content-Type: application/json'
{
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"merchant_id": "22222222-2222-2222-2222-222222222222",
"shop_id": "33333333-3333-3333-3333-333333333333",
"amount": 250000,
"currency": "RUB",
"method": "payouts_sbp",
"status": "succeeded",
"fail_reason": "",
"recipient": {
"phone": "+79001234567",
"bank_id": "100000000111",
"full_name": "Иванов Иван Иванович"
},
"created_at": "2026-05-05T16:01:00Z",
"completed_at": "2026-05-05T16:01:09Z"
}SBP banks
The bank_id field in POST /payouts is an NSPK participant\u2019s member_id (12 digits). The official registry is maintained by NSPK; we mirror it through our Bank 131 integration and return it as a single list.
SBP bank directory
/v1/public/banks/sbpcurl -X GET https://api.nerezpay.ru/v1/public/banks/sbp \ -H 'Authorization: Bearer sk_test_x9k…' \ -H 'Content-Type: application/json'
{
"items": [
{ "member_id": "100000000004", "name": "Тинькофф Банк", "name_en": "T-Bank" },
{ "member_id": "100000000005", "name": "ВТБ", "name_en": "VTB" },
{ "member_id": "100000000007", "name": "Альфа-Банк", "name_en": "Alfa-Bank" },
{ "member_id": "100000000111", "name": "Сбербанк", "name_en": "Sberbank" },
// … the whole NSPK registry (~200 participants), sorted by name
]
}If the bank you need isn’t in the list
The directory is a UI list, not an allow-list. We accept any member_id from the official NSPK registry; there are no restrictions, and validation happens at the receiving bank.
Platform-side validation
bank_idnot 12 digits —400 bad_requestbefore we call the bank.member_idis formally valid but missing from NSPK — the acquiring bank declines, the operation transitions tofailedwithfailure_codedo_not_honororbank_unknown, or the request returns502 router_error.