API Reference

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

The shop is determined by the API key — one key maps to one shop. For multiple shops issue separate keys in the dashboard.

Create a payment

POST/v1/public/payments
Creates a payment session. The response contains the payment object and a payment_url to redirect the customer to.
Request body
amountAmount 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.
Request
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"
  }'
Response
{
  "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

GET/v1/public/payments/{id}
Returns the full payment object with current status, finalized_at and the list of refunds[]. Use for synchronous status checks and after receiving a webhook.
Request
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'
Response
{
  "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

POST/v1/public/payments/{id}/cancel
Only works for status pending. Other statuses return 409 not_pending. A successful cancel triggers a payment.cancelled webhook.
Request
curl -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'
Response
{
  "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

  1. Payment is created with status pending and methods=any. No bank session yet, processing_until is empty.
  2. Customer opens payment_url, sees method buttons.
  3. Customer clicks "Pay via SBP" (for example). Method is fixed: status stays pending, the method field changes from any to sbp. No webhook on this step.
  4. Bank session opens: status becomes processing, processing_until is filled with the method TTL (20 min for SBP). No webhook on this transition.
  5. Bank returns a final status: succeeded or failed. A payment.succeeded or payment.failed webhook is delivered.
If the bank doesn\u2019t return a status before 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 /payments response — payment.metadata.
  • In GET /payments/{id} — same field.
  • In the webhook data object.

Reserved keys

Alongside your data, metadata may contain system keys that we set. Nothing scary, just don\u2019t be surprised:

keywhen it appears
modealways. Value is test or live depending on the key used.
webhook_urlif you passed webhook_url in the POST /payments body.
customer_idif you passed customer_id in the POST /payments body.
bank_reflive mode: operation ID at the acquiring bank.
failure_codeon failed/expired: our unified failure code (see the "Failure codes" section).
bank_failure_codeon failed: raw bank code. Audit-only — rely on failure_code in code.
simulator_outcometest mode: the planned simulator outcome (succeeded, failed, requires_action, pending).
simulator_failure_codetest 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
parametervalue
payment_idPayment UUID.
statussucceeded | 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.

Don\u2019t trust these parameters as proof of payment. Customers can rewrite the URL by hand. Before celebrating a purchase, confirm the status server-side — via the 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_url isn\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

FieldTypeDescription
idUUIDRefund identifier.
payment_idUUIDPayment the refund belongs to.
merchant_idUUIDMerchant. Determined from the API key.
amountint64Refund amount in minor units.
reasonstringRefund reason. Lands in the receipt and audit log.
statusenumpending → succeeded or failed.
created_attimeWhen the refund was created.
completed_attimeWhen 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

POST/v1/public/payments/{id}/refund
Full or partial refund of a succeeded payment. Cumulative refunds cannot exceed the original amount. Every successful refund triggers a payment.refunded webhook.
Request body
amountRefund 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.
Request
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"
}'
Response
{
  "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

POST/v1/public/payouts
Creates a payout to a phone number and the bank’s member_id. The shop is determined by the API key. The final status (succeeded or failed) is delivered by the payout.* webhook.
Request body
amountPayout 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.
Request
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": "Иванов Иван Иванович"
  }'
Response
{
  "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

GET/v1/public/payouts/{id}
Returns the current payout state. Status walks pending → succeeded or failed. The final status triggers a payout.succeeded or payout.failed webhook.
Request
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'
Response
{
  "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

GET/v1/public/banks/sbp
Full registry of SBP participants with member_id and names in Russian and English. Synced with the NSPK registry via bank131. Suitable for rendering a dropdown in a payout form.
Request
curl -X GET https://api.nerezpay.ru/v1/public/banks/sbp \
  -H 'Authorization: Bearer sk_test_x9k…' \
  -H 'Content-Type: application/json'
Response
{
  "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_id not 12 digits — 400 bad_request before we call the bank.
  • member_id is formally valid but missing from NSPK — the acquiring bank declines, the operation transitions to failed with failure_code do_not_honor or bank_unknown, or the request returns 502 router_error.