Payment Docs
Recurring payments

Recurring payments overview

Save the payer's payment source once and charge it later without the payer

Recurring payments let you charge a payer again without their participation: subscriptions, auto top-ups, installments. The payer confirms a binding once — a payment source saved at the payment provider. After that you create charges by the binding ID; the payer does not open a payment page.

Before you start

Authorization

How to sign your requests with the API token.

Recurring payments are enabled per payment method. Ask your manager to enable them; today they are available for SBP payments in RUB. Orders without the recurring object work exactly as before.

How it works

Save the binding

Create the first payment with recurring.saveBinding: true, or create a binding without a payment. The binding starts as PENDING; store its id.

The payer confirms

The payer pays the first order (and agrees to the subscription in their banking app) or confirms the binding on the provider page. The binding becomes ACTIVE and you receive a RECURRING_BINDING_UPDATE webhook.

Charge the binding

Create an order with recurring.bindingId. It is charged right away, without a payment link; the result comes in the usual order webhook.

Delete the binding

When the subscription ends, delete the binding: further charges are rejected.

Save a binding with the first payment

A regular order creation request with the recurring object. Everything else — customer fields, payment link, H2H QR link, webhooks — is the same as for the payment method.

POST /v1/orders
ParameterTypeRequiredDescription
externalUserIdstring✅ YesPayer ID in your system, up to 64 characters. The binding belongs to this payer
recurring.saveBindingboolean✅ Yestrue — save the payment source after a successful payment
recurring.purposestring❌ NoSubscription description shown in the banking app, up to 255 characters. Default — purpose
recurring.externalIdstring❌ NoBinding ID in your system, unique per merchant, up to 255 characters
curl -X POST "https://api.1capital.capital/v1/orders" \
  -H "Content-Type: application/json" \
  -H "X-Api-Token: YOUR_API_TOKEN" \
  -d '{
    "amount": "990",
    "currency": "RUB",
    "serviceId": 12,
    "purpose": "Subscription, October",
    "externalId": "order_1234",
    "externalUserId": "user_987",
    "customer": { "phone": "+79991234567", "ip": "198.51.100.24" },
    "recurring": {
      "saveBinding": true,
      "purpose": "Monthly subscription",
      "externalId": "sub-42"
    }
  }'

The response is the order object with the binding ID in recurringBindingId. The order is routed only to the payment methods of the service that support recurring payments.

For SBP the payer gets a subscription QR link (https://sub.nspk.ru/...) instead of a one-time one: by paying it they also agree to future charges. When the order is COMPLETED, the binding becomes ACTIVE. If the order ends as FAILED, EXPIRED or CANCELED, the binding becomes FAILED.

Create a binding without a payment

Saves the payment source without charging the payer, for example for a free trial.

POST /v1/recurring/bindings
ParameterTypeRequiredDescription
externalUserIdstring✅ YesPayer ID in your system, up to 64 characters
serviceIdinteger / null❌ NoService whose payment method saves the binding. Default — your default service
purposestring / null❌ NoSubscription description shown in the banking app
externalIdstring / null❌ NoBinding ID in your system, unique per merchant
callbackUrlstring / null❌ NoWebhook URL for this binding
curl -X POST "https://api.1capital.capital/v1/recurring/bindings" \
  -H "Content-Type: application/json" \
  -H "X-Api-Token: YOUR_API_TOKEN" \
  -d '{
    "externalUserId": "user_987",
    "serviceId": 12,
    "purpose": "Monthly subscription",
    "externalId": "sub-43"
  }'

The response is the binding object with confirmationUrl: redirect the payer there to confirm the binding in their bank. The binding stays PENDING until it is confirmed and becomes FAILED if it is not confirmed within 24 hours.

Charge a binding

POST /v1/orders
ParameterTypeRequiredDescription
amountstring✅ YesCharge amount
recurring.bindingIdstring (UUID)✅ YesID of an ACTIVE binding
serviceIdinteger / null❌ NoDefault — the service the binding was created through. The service must include the binding's method
externalUserIdstring / null❌ NoIf passed, must match the binding payer. Default — the binding payer
externalIdstring / null❌ NoCharge ID in your system. Recommended: protects from double charges

purpose, currency, callbackUrl and the other order parameters work as usual.

curl -X POST "https://api.1capital.capital/v1/orders" \
  -H "Content-Type: application/json" \
  -H "X-Api-Token: YOUR_API_TOKEN" \
  -d '{
    "amount": "990",
    "currency": "RUB",
    "purpose": "Subscription, November",
    "externalId": "order_1235",
    "recurring": { "bindingId": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b" }
  }'

The charge goes to the provider while the request is processed. The response is the order object:

  • PENDING — the charge is accepted, the result comes in an ORDER_UPDATE webhook;
  • COMPLETED — the provider confirmed the charge at once;
  • FAILED — the provider refused, for example for insufficient funds. Create a new charge later.

A charge has no payment page: the payment link and the H2H endpoints answer 400 for it. Fees, limits, the balance credit and the webhooks are the same as for any order.

Errors

HTTP statusWhen
400Invalid parameters: saveBinding together with bindingId, no externalUserId for saveBinding, externalUserId of another payer
404The binding is not found or belongs to another merchant; no payment method of the service supports recurring payments; the amount is outside the payment method limits
409The binding is not ACTIVE; the binding's payment method is disabled or not in the service; the externalId is taken

Binding object

{
  "id": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
  "status": "ACTIVE",
  "isTest": false,
  "merchantId": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5e",
  "externalId": "sub-42",
  "externalUserId": "user_987",
  "purpose": "Monthly subscription",
  "initialOrderId": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5c",
  "confirmationUrl": null,
  "bankName": "Sberbank",
  "bankLogoUrl": null,
  "callbackUrl": null,
  "createdAt": "2026-10-21T12:34:56.000Z",
  "updatedAt": "2026-10-21T12:36:10.512Z",
  "activatedAt": "2026-10-21T12:36:10.512Z",
  "closedAt": null
}
FieldTypeDescription
idstring (UUID)Binding ID. Pass it as recurring.bindingId to charge the binding
statusstringCurrent status, see Binding statuses
isTestbooleanTest binding: created on a test terminal, no real payment source is saved
merchantIdstring (UUID)Your merchant ID
externalIdstring / nullBinding ID in your system (the value you passed)
externalUserIdstringPayer ID in your system: the binding belongs to this payer only
purposestring / nullSubscription description shown to the payer in the banking app
initialOrderIdstring / nullOrder whose payment created the binding; null for a binding created without a payment
confirmationUrlstring / nullPage where the payer confirms a binding created without a payment. Redirect the payer to it
bankNamestring / nullPayer's bank, if the provider reported it
bankLogoUrlstring / nullPayer's bank logo, if the provider reported it
callbackUrlstring / nullWebhook URL for this binding
createdAtstringCreation time (ISO 8601)
updatedAtstringLast update time (ISO 8601)
activatedAtstring / nullWhen the binding first became ACTIVE
closedAtstring / nullWhen the binding became DELETED or FAILED

Binding statuses

StatusFinalDescription
PENDINGNoWaiting for the payer: the first payment is not completed yet, or the payer has not confirmed the binding
ACTIVENoThe binding is confirmed: you can charge it
SUSPENDEDNoSuspended by the provider or the payer's bank. Charges are rejected; the binding may become ACTIVE again
DELETEDYesDeleted by you, by the payer in the banking app or by the provider. Charges are rejected
FAILEDYesThe binding was not created: the first payment failed or expired, or the payer did not confirm it in 24 hours

A FAILED binding still becomes ACTIVE if the provider reports a late successful first payment. Every status change is reported with a RECURRING_BINDING_UPDATE webhook.

Manage bindings

RequestDescription
GET /v1/recurring/bindings/{bindingId}Binding by ID
GET /v1/recurring/bindings/external/{externalId}Binding by your ID
POST /v1/recurring/bindings/findSearch with filters and pagination, e.g. all bindings of a payer
POST /v1/recurring/bindings/{bindingId}/sync-statusRequest the current state from the provider and apply it
DELETE /v1/recurring/bindings/{bindingId}Delete the binding at the provider; it becomes DELETED

Find a payer's bindings

curl -X POST "https://api.1capital.capital/v1/recurring/bindings/find" \
  -H "Content-Type: application/json" \
  -H "X-Api-Token: YOUR_API_TOKEN" \
  -d '{
    "pagination": { "page": 1, "perPage": 20 },
    "filter": { "externalUserId": "user_987", "status": ["ACTIVE"] }
  }'

Filters: externalUserId, externalId, status (any of), fromDate, toDate (creation time), q (binding ID, your ID, payer ID or first order ID). Sorting: sort: [{ "field": "createdAt", "order": "desc" }] by status, createdAt, updatedAt, activatedAt. The response is { "data": [binding objects], "total": number }.

Delete a binding

curl -X DELETE "https://api.1capital.capital/v1/recurring/bindings/0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b" \
  -H "X-Api-Token: YOUR_API_TOKEN"

Returns the binding with the DELETED status. A binding that is already DELETED or FAILED answers 409; if the provider does not answer — 503, the binding is unchanged.

Sync with the provider

The payer can suspend or delete the subscription in their banking app. PENDING bindings are synced in the background; for an ACTIVE one, call sync-status when a charge is refused to find out whether the binding is still valid. The response is { "providerState", "previousStatus", "binding" }.

Test mode

On a test payment method no real payment source is saved:

  • a binding from the first payment becomes ACTIVE when its test order is COMPLETED (amount 1000) and FAILED when the order is not paid;
  • a binding without a payment becomes ACTIVE within a minute, confirmationUrl is null;
  • charges are test orders, their status follows the amount: 1000 — COMPLETED, 1001 — CANCELED, 1002 — FAILED, 1003 — COMPLETED, then REFUND, any other amount — EXPIRED in 5 minutes;
  • sync-status answers 400, deletion works without the provider.

Recommendations

  • Pass recurring.externalId for bindings and externalId for charges: a repeated request with the same value is rejected with 409, which protects you from duplicate bindings and double charges.
  • Charge only ACTIVE bindings; wait for the RECURRING_BINDING_UPDATE webhook after the first payment.
  • Treat a PENDING charge as in progress: do not repeat it until it gets a final status.
  • Delete bindings of canceled subscriptions — the payer sees the subscription in the banking app until then.

Next steps

On this page