Skip to content

Onramp API Reference

Base URLs: https://onramp.mainnet.rubin.trade (mainnet), https://onramp.testnet.rubin.trade (testnet). All customer endpoints live under the route /v4/sbp; Rubin assigns each customer a payment provider behind it (see Overview).

Auth column: public — no signature; signed — signed request for the customer address.

MethodPathAuthApplies to
GET/v4/sbp/ratespublic / signed if ratesPerCustomerall
GET/v4/sbp/customersignedall
POST/v4/sbp/customersignedidentity: form
POST/v4/sbp/customer/resend-otpsignedidentity: form
POST/v4/sbp/customer/verify-phonesignedidentity: form
POST/v4/sbp/customer/identifysignedidentity: form
POST/v4/sbp/customer/identify/updatesignedidentity: form
POST/v4/sbp/customer/esiasignedidentity: form
POST/v4/sbp/customer/kycsignedidentity: hosted
POST/v4/sbp/customer/contactssignedcontacts: true
GET/v4/sbp/limitssignedall
POST/v4/sbp/orderssignedall
GET/v4/sbp/orderssignedall
GET/v4/sbp/orders/{orderId}signedall

An endpoint the customer's flow does not use answers 404 (after authentication and validation). Amounts are decimal strings; timestamps are ISO 8601.

Errors

Every error has the same shape:

{ "errors": [{ "code": "QUOTE_EXPIRED", "msg": "Quote expired", "retryAfterSeconds": 30 }] }

code and retryAfterSeconds are present only where they apply. Request validation errors (400) are a list of { "value", "msg", "param", "location" } without code. Pauses are reported only in retryAfterSeconds — there is no Retry-After header.

CodeHTTPMeaning
PHONE_ALREADY_REGISTERED409The phone number belongs to another customer
SMS_RATE_LIMIT429Too many SMS or code attempts; wait retryAfterSeconds
OTP_INVALID422Wrong SMS code
OTP_EXPIRED422The SMS code expired; send a new one
IDENTIFY_FAILED422Identity document or eID check failed
NOT_VERIFIED422The customer is below level 3
CONTACTS_REQUIRED422Hosted flow: contacts are not submitted yet
CONTACTS_CONFLICT422Hosted flow: contacts need manual review
QUOTE_EXPIRED422The quote is stale or invalid; request a new one
AMOUNT_BELOW_MIN422Below the minimum order
AMOUNT_ABOVE_MAX422Above the maximum order or the on-chain per-deposit cap
LIMIT_EXCEEDED422The customer's daily, weekly or monthly limit is used up
PROVIDER_LIQUIDITY422Not enough liquidity for this amount (flow.poolLimited)
CUSTOMER_NOT_FOUND404No customer for this address yet
ORDER_NOT_FOUND404No such order
PROVIDER_UNAVAILABLE502 / 429The payment service is down or throttling; on 429 wait retryAfterSeconds
PROVIDER_ERROR502 / 422The payment provider rejected the request

Provider errors carry a fixed msg per code. To investigate a specific failure, send us the address, the time and the response.

Authentication errors are listed on the Authentication page.


Rates

GET /v4/sbp/rates

Current rate for buying USDC with fiat (RUB). Public; sign it when the customer's flow.ratesPerCustomer is true.

{ "userRate": "81.250000", "currency": "RUB", "quoteSignature": "…", "expiresInSeconds": 165 }
FieldTypeDescription
userRatestringRUB per 1 USDC, final for the customer (all fees included)
currencystringFiat currency, RUB
quoteSignaturestringPass to POST /orders
expiresInSecondsnumberSeconds until the quote expires

Errors: NOT_VERIFIED, CUSTOMER_NOT_FOUND (personal rates only), PROVIDER_UNAVAILABLE, PROVIDER_ERROR.

Customer

GET /v4/sbp/customer

Verification state of the signing address and the steps it needs. The first call of an address assigns it a payment provider.

{
  "flow": {
    "identity": "form",
    "phoneOtp": true,
    "contacts": false,
    "ratesPerCustomer": false,
    "settlement": false,
    "poolLimited": false
  },
  "exists": true,
  "verificationLevel": 3,
  "identified": true,
  "phoneSet": true,
  "phoneMasked": "+7***1234",
  "identifyStatus": "success",
  "activeOrderId": null
}
FieldTypeDescription
flowobjectSteps this customer needs — see Integration flow
existsbooleanfalse — verification not started yet (verificationLevel is 0)
verificationLevelnumberSee levels; orders need 3
identifiedbooleanIdentity confirmed
phoneSetbooleanA phone number is attached (only when exists is true)
phoneMaskedstring | nullMasked phone, e.g. +7***1234
identifyStatusstringnone, pending, success, failed
activeOrderIdstring | nullLatest open order (initiated, processing or awaiting_payment)
kycStatusstringHosted flow (and form flow when reported): none, pending, approved, rejected, expired
contactsStatusstringHosted flow: none, pending, ready, action_required, failed, review_required
contactsActionUrlstring | nullHosted flow: page to open when contactsStatus is action_required

POST /v4/sbp/customer

Attaches a phone number and sends an SMS code. Form flow.

Body fieldTypeRequiredDescription
phonestringyes+7 and 10 digits, up to 32 chars

Response: { "verificationLevel": 1, "otpSent": true, "nextStep": "verify_phone" }.

Errors: PHONE_ALREADY_REGISTERED, SMS_RATE_LIMIT, PROVIDER_ERROR (SMS could not be sent).

POST /v4/sbp/customer/resend-otp

Sends the SMS code again. Body: { "phone": "+79991234567" }. Response: { "otpSent": true }. Errors: SMS_RATE_LIMIT.

POST /v4/sbp/customer/verify-phone

Body fieldTypeRequiredDescription
otpCodestringyesCode from the SMS, 4 digits

Response: { "verificationLevel": 2, "identified": false, "phoneSet": true }. A 200 with verificationLevel below 2 means the code was not accepted.

Errors: OTP_INVALID, OTP_EXPIRED, SMS_RATE_LIMIT, CUSTOMER_NOT_FOUND.

POST /v4/sbp/customer/identify

Identity-document check. POST …/customer/identify/update takes the same body to correct the details of a customer already at level 3.

Body fieldTypeRequiredDescription
lastNamestringyesUp to 128 chars
firstNamestringyesUp to 128 chars
patronymicstringnoUp to 128 chars
birthDatestringyesYYYY-MM-DD
persondocNumberstringyesDocument series and number, 10 digits

Response: { "verificationLevel": 3, "identified": true, "identifyStatus": "success" }. With identifyStatus: "pending" poll GET /customer.

Errors: IDENTIFY_FAILED, CUSTOMER_NOT_FOUND.

POST /v4/sbp/customer/esia

Starts a government eID (ESIA) login to raise the customer to level 4.

Body fieldTypeRequiredDescription
redirectUrlstringnoWhere to return the user afterwards; HTTPS, allow-listed origin

Response: { "redirectUrl": "https://…" } — send the user there. After they return, poll GET /customer until verificationLevel is 4.

Errors: 422 "ESIA is not enabled", 422 "redirectUrl is missing or not allowed", IDENTIFY_FAILED.

POST /v4/sbp/customer/kyc

Returns the hosted KYC page. Hosted flow.

Body fieldTypeRequiredDescription
returnUrlstringnoHTTPS page to return to; ignored if not allowed

Response: { "url": "https://…", "status": "pending" }. url is null while the page is being prepared or when KYC is already approved.

POST /v4/sbp/customer/contacts

Hosted flow with flow.contacts.

Body fieldTypeRequiredDescription
phonestringyesE.164, up to 32 chars
emailstringyesUp to 254 chars

Response: { "status": "action_required", "actionUrl": "https://…" }. Open actionUrl on action_required; poll GET /customer on pending.

Errors: CONTACTS_CONFLICT.

Limits

GET /v4/sbp/limits

FieldTypeDescription
currencystringRUB
remainingRublesstringWhat the customer may still buy now
remainingDailyRublesstring | nullLeft for today
remainingWeeklyRublesstring | nullLeft for this week
remainingMonthlyRublesstring | nullLeft for this month
dailyLimitRublesstring | nullDaily ceiling
weeklyLimitRublesstring | nullWeekly ceiling
monthlyLimitRublesstring | nullMonthly ceiling
minOrderRublesstring | nullMinimum order
maxOrderRublesstring | nullMaximum order
availableUsdcstring | nullAvailable liquidity in USDC (when flow.poolLimited)
verificationLevelnumberCurrent level

null means that limit is not reported. Errors: CUSTOMER_NOT_FOUND.

Orders

POST /v4/sbp/orders

Creates an order and requests a payment link.

Body fieldTypeRequiredDescription
amountCryptostringyesUSDC to credit, up to 6 decimals, e.g. "125.50"
quoteSignaturestringyesFrom GET /rates
subaccountNumbernumberno0–128000; stored with the order. Funds are always credited to the main account

Response: an order.

Errors: NOT_VERIFIED, QUOTE_EXPIRED, AMOUNT_BELOW_MIN, AMOUNT_ABOVE_MAX, LIMIT_EXCEEDED, PROVIDER_LIQUIDITY, CONTACTS_REQUIRED, CONTACTS_CONFLICT, CUSTOMER_NOT_FOUND, PROVIDER_UNAVAILABLE, PROVIDER_ERROR; 422 "amountCrypto must be positive".

GET /v4/sbp/orders

The customer's orders, newest first.

QueryTypeRequiredDescription
limitnumberno1–100, default 20

Response: { "orders": [ …order objects… ] }.

GET /v4/sbp/orders/{orderId}

One order of the signing customer. An open order is refreshed from the payment provider when it has not been updated recently, so this is the endpoint to poll. Response: an order; another customer's or unknown order — 404 "Order not found".

Order object

{
  "orderId": "4f0c7c7e-3a8d-4c55-9b0e-2a51c1f0a6d2",
  "provider": "sbp",
  "status": "awaiting_payment",
  "creditStatus": "none",
  "amountUser": "125.500000",
  "amountGrossFiat": "10196.88",
  "fiatCurrency": "RUB",
  "paymentLink": "https://…",
  "creditTxHash": null,
  "createdAt": "2026-10-12T09:15:02.000Z"
}
FieldTypeDescription
orderIdstring (UUID)Order id
providerstringRoute name, always sbp
statusstringPayment status — see below
creditStatusstringCrediting status — see below
amountUserstringUSDC to be credited to the customer
amountGrossFiatstring | nullFiat amount charged to the customer (RUB)
fiatCurrencystring | nullRUB
paymentLinkstring | nullSBP payment link; null until it is issued
creditTxHashstring | nullRubin transaction that credited the USDC
createdAtstringCreation time
status
ValueMeaning
initiatedCreated, waiting for the payment provider
processingAccepted by the payment provider
awaiting_paymentWaiting for the customer to pay
successPaid
failedRejected or failed
expiredNot paid in time
refundedPayment returned to the customer
creditStatus
ValueMeaning
noneNot paid yet
pendingPaid, queued for crediting
submittedCrediting transaction sent, awaiting confirmation
creditedUSDC is on the customer's main account
failedAttempt failed; retried automatically
manual_reviewNeeds support — contact us with the orderId

The order is complete when status is success and creditStatus is credited.

Service

GET /health

{ "ok": true, "service": "onramp" }. Public.