Developer API
API Overview
Quantum Payouts provides REST API to integrate with external systems. It allows creating payouts, tracking their status and getting information about available features.
Main Capabilities
- Create Payouts - register a payout and get a link to the recipient details page
- Status Tracking - get current information about payout status
- System Information - get available features and limitations
- Secure Authorization - use Bearer tokens for authentication
Base URL
https://payouts-dev.quantumlabs.ru/api/v1/
Data Format
- Requests: JSON (Content-Type: application/json; charset=UTF-8)
- Responses: JSON (Accept: application/json; version=1.0)
- Encoding: UTF-8
- Amounts: in kopecks (e.g., 150000 = 1500.00 rubles)
Endpoint reference
Endpoints intended for server integration with a Bearer token (Authorization header, see Authorization):
POST …/createPayout— create a payout and get a link to capture recipient details (card or SBP depending ontypeand settings).POST …/performPayout— immediate single payout to card only (target.pan).POST …/getPayoutStatus— payout status byorderNumber.GET …/features— features available for the token (GET only).
Separate from Bearer API: saving and testing the webhook use POST …/webhook_save and POST …/webhook_test with a logged-in session and CSRF (not the API token). Low-level bank integration endpoints (e.g. Alfa signing, certificate deploy) are not part of the merchant integration contract and are documented for partners only.
Authentication
All API requests require authorization using Bearer tokens.
Getting an API Token
- Log into your account
- Go to Settings → API
- Click "Create API Token"
- Copy the generated token
- Store the token securely
Using the Token
Authorization: Bearer YOUR_API_TOKEN
⚠️ Security: Never pass API tokens in URL parameters or in unprotected form. Use only HTTPS connections.
Create Payout
Endpoint for registering a new payout and getting a link to the recipient details page.
Request Parameters
Required headers:Authorization: Bearer YOUR_API_TOKEN,Accept: application/json; version=1.0,Content-Type: application/json; charset=UTF-8.
| Parameter | Type | Required | Description |
|---|---|---|---|
orderNumber |
String | Yes | Unique payout identifier (up to 36 characters) |
amount |
Integer | Yes | Amount in kopecks (up to 12 digits) |
urls |
Object | Yes | Container for redirect URLs |
urls.returnUrl |
String | Yes | URL for redirect on success |
urls.failUrl |
String | No | URL for redirect on error |
description |
String | No | Payout description |
sessionTimeoutSecs |
Integer | No | Session lifetime in seconds (default 600) |
type |
String | No | Payout destination type: "card" or "sbp" (optional) |
channel |
String | No | Processing bank channel: vtb (default) or alfa when integrated. Works with type: use sbp for SBP, card (or omit) for cards. |
For immediate card payout without a recipient details page, use performPayout (cards only). SBP is not supported on performPayout.
Example Request
curl -X POST 'https://payouts-dev.quantumlabs.ru/api/v1/createPayout' \
-H 'Content-Type: application/json; charset=UTF-8' \
-H 'Accept: application/json; version=1.0' \
-H 'Authorization: Bearer YOUR_API_TOKEN' \
-d '{
"orderNumber": "ORDER_12345",
"amount": 150000,
"description": "Payout for order #12345",
"sessionTimeoutSecs": 1800,
"type": "card",
"urls": {
"returnUrl": "https://your-site.com/success",
"failUrl": "https://your-site.com/fail"
}
}'
Response Parameters
| Parameter | Type | Description |
|---|---|---|
result |
Object | Request status block |
result.error |
String | Error code ("0" = success) |
result.message |
String | Error message (null on success) |
payoutUrl |
String | Link to the recipient details page |
orderId |
String | Internal order identifier |
orderNumber |
String | Order number from the request |
Example Success Response
{
"result": {
"error": "0",
"message": null
},
"payoutUrl": "https://payouts-dev.quantumlabs.ru/static/payout.php?payout_id=123&token=abc123",
"orderId": "PAY_2024_12_19_abc123",
"orderNumber": "ORDER_12345"
}
Example Error Response
{
"result": {
"error": "VALIDATION_ERROR",
"message": "urls.returnUrl is required; amount must be positive"
}
}
Payout Lifecycle
- Registration - API creates payout with status
uploaded - Details Entry - user goes to
payoutUrlto enter recipient details - Processing - status changes to
created, thenprocessing - Completion - final status
success,failed, orcancelled
Perform Single Payout
Endpoint to immediately execute a single payout to a bank card (target.pan). The request registers the payout and submits it to the bank in one call.
Note: SBP is not supported on this endpoint. For SBP use createPayout with type: sbp and channel if needed.
Request Parameters
Required headers:Authorization: Bearer YOUR_API_TOKEN,Accept: application/json; version=1.0,Content-Type: application/json; charset=UTF-8.
| Parameter | Type | Required | Description |
|---|---|---|---|
orderNumber |
String | Yes | Unique payout identifier (up to 32 chars). Idempotency per (user, orderNumber). |
amount |
Integer | Yes | Amount in kopecks (up to 12 digits). Example: 150000 = 1500.00 RUB |
description |
String | Yes | Operation description (up to ~600 chars) |
target |
Object | Yes | Recipient details container |
target.pan |
String | Yes | Recipient card number (13–19 digits) |
Example Request
curl -X POST 'https://payouts-dev.quantumlabs.ru/api/v1/performPayout' \
-H 'Content-Type: application/json; charset=UTF-8' \
-H 'Accept: application/json; version=1.0' \
-H 'Authorization: Bearer YOUR_API_TOKEN' \
-d '{
"orderNumber": "ORDER_12345",
"amount": 150000,
"description": "One-time payout",
"target": { "pan": "4111111111111111" }
}'
Response Parameters
| Parameter | Type | Description | |
|---|---|---|---|
result |
Object | Request status block | |
result.error |
String | Error code ("0" = success) | |
result.message |
String | Error message (null on success) | |
target |
Object | No | Recipient info (when present) |
target.maskedPan |
String | Yes | Masked card number |
orderNumber |
String | Order identifier | |
orderStatus |
String | Internal payout status from DB, e.g. uploaded, created, processing, success, failed, cancelled |
|
amount |
Integer | Amount in kopecks | |
creationDate |
Integer | Creation Unix timestamp | |
orderId |
String | Bank order code (if available) |
Example Success Response
{
"result": { "error": "0", "message": null },
"orderNumber": "ORDER_12345",
"orderStatus": "processing",
"amount": 150000,
"creationDate": 1737097200,
"orderId": "BANK_ORDER_CODE_OPTIONAL",
"target": { "maskedPan": "411111******1111" }
}
Example Error Response
{
"result": {
"error": "VALIDATION_ERROR",
"message": "orderNumber is required; amount must be greater than 0"
}
}
- Repeated call with the same
orderNumberis idempotent (no re-submission to the bank). - Requires configured Client ID/Secret and permission for card payouts.
Get Payout Status
Endpoint for getting current information about payout status.
Request Parameters
Required headers:Authorization: Bearer YOUR_API_TOKEN,Accept: application/json; version=1.0,Content-Type: application/json; charset=UTF-8.
| Parameter | Type | Required | Description |
|---|---|---|---|
orderNumber |
String | Yes | Payout identifier (up to 32 chars) |
Example Request
curl -X POST 'https://payouts-dev.quantumlabs.ru/api/v1/getPayoutStatus' \
-H 'Content-Type: application/json; charset=UTF-8' \
-H 'Accept: application/json; version=1.0' \
-H 'Authorization: Bearer YOUR_API_TOKEN' \
-d '{
"orderNumber": "ORDER_12345"
}'
Response Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
result |
Object | Yes | Request status block |
result.error |
String | Yes | Error code ("0" = success) |
result.message |
String | Yes | Error message (null on success) |
orderNumber |
String | Yes | Order identifier |
orderStatus |
String | Yes | Internal payout status from DB, e.g. uploaded, created, processing, success, failed, cancelled |
destination_type |
String | No | card or sbp when present |
channel |
String | No | e.g. vtb, alfa |
amount |
Integer | Yes | Amount in kopecks |
creationDate |
Integer | Yes | Creation Unix timestamp |
orderId |
String | Yes | Bank order code (if available) |
target |
Object | No | Recipient info (when present) |
target.maskedPan |
String | Yes | Masked card number (for card payouts) |
target.maskedPhone |
String | No | Masked phone number (for SBP payouts) |
Response
{
"result": {
"error": "0",
"message": null
},
"orderNumber": "ORDER_12345",
"orderStatus": "success",
"amount": 150000,
"creationDate": 1737097200,
"description": "Order payout",
"destination_type": "card",
"channel": "vtb",
"target": { "maskedPan": "411111******1111" }
}
Webhook
HTTPS webhook for event notifications (e.g., payout status changes). URL, enable flag, and signing are configured under Dashboard → Settings → Webhook.
Saving webhook settings (portal UI)
POST /api/v1/webhook_save uses a logged-in session and a required csrf_token (not the Bearer API token). JSON or form body: url (HTTPS when enabled), enabled, use_signature, optionally regenerate_secret. Without a linked company you may get NO_COMPANY.
Test delivery: POST /api/v1/webhook_test — also session + CSRF.
Payload format
{
"event": "payout.status_changed",
"eventId": "2f4b1b0a0c2a4d32b2b0c9d311e7e59b",
"occurredAt": "2025-09-15T19:40:21+03:00",
"payout": {
"id": 123456,
"externalId": "ORDER-2025-0001",
"status": "success",
"amount": 150000,
"currency": "RUB"
}
}- event — event type (always "payout.status_changed")
- eventId — unique event identifier (32 hex characters)
- occurredAt — event timestamp in ISO8601 format (Europe/Moscow timezone, UTC+3)
- payout.id — internal payout ID (integer)
- payout.externalId — your orderNumber from API request (string, null for manual/file payouts)
- payout.status — payout status: "uploaded", "created", "processing", "success", "failed", "cancelled"
- payout.amount — amount in kopecks as integer (e.g., 12345 = 123.45 RUB). Same format as API requests
- payout.currency — currency code (always "RUB")
Headers
Content-Type: application/jsonX-Payouts-EventX-Payouts-Event-IdX-Payouts-TimestampX-Payouts-Signature(optional):sha256=<hex>
Signature verification (optional)
Signature calculation algorithm
The signature is calculated using HMAC-SHA256 algorithm as follows:
- Source data: take the raw HTTP request body exactly as it was sent
- JSON normalization: apply JSON minify to the request body (remove spaces, line breaks, tabs)
- Secret key: use the secret generated in webhook settings
- Hashing algorithm: apply HMAC-SHA256 to the normalized JSON using the secret
- Result format: get a hexadecimal string and add the
sha256=prefix - Comparison: compare the resulting signature with the
X-Payouts-Signatureheader using secure string comparison
Step-by-step signature calculation example
Let's consider an example with a test webhook:
1. Original request body (as sent):
{
"event": "payout.status_changed",
"eventId": "2f4b1b0a0c2a4d32b2b0c9d311e7e59b",
"occurredAt": "2025-09-15T19:40:21+03:00",
"payout": {
"id": 123456,
"externalId": "ORDER-2025-0001",
"status": "success",
"amount": 150000,
"currency": "RUB"
}
}Note: amount is 150000 kopecks = 1500.00 RUB
2. Normalized JSON (after minify):
{"event":"payout.status_changed","eventId":"2f4b1b0a0c2a4d32b2b0c9d311e7e59b","occurredAt":"2025-09-15T19:40:21+03:00","payout":{"id":123456,"externalId":"ORDER-2025-0001","status":"success","amount":150000,"currency":"RUB"}}3. Secret key (example):
sk_test_1234567890abcdef1234567890abcdef123456784. HMAC-SHA256 calculation:
HMAC-SHA256(
"sk_test_1234567890abcdef1234567890abcdef12345678",
"{\"event\":\"payout.status_changed\",\"eventId\":\"2f4b1b0a0c2a4d32b2b0c9d311e7e59b\",\"occurredAt\":\"2025-09-15T19:40:21+03:00\",\"payout\":{\"id\":123456,\"externalId\":\"ORDER-2025-0001\",\"status\":\"success\",\"amount\":150000,\"currency\":\"RUB\"}}"
) = "8a3f7e2c9b1d4a5e6f0c8b7a9d3e2f1c4b5a6e7d8c9b0a1f2e3d4c5b6a7e8d9f"Note: This is an example hash. Your actual hash will depend on your secret key.
5. Final signature:
sha256=8a3f7e2c9b1d4a5e6f0c8b7a9d3e2f1c4b5a6e7d8c9b0a1f2e3d4c5b6a7e8d9fCode examples for signature verification
PHP:
$raw = file_get_contents('php://input');
$normalized = json_encode(json_decode($raw), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
$sig = $_SERVER['HTTP_X_PAYOUTS_SIGNATURE'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $normalized, $yourSecret);
if (!hash_equals($expected, $sig)) { http_response_code(401); exit; }Node.js:
const crypto = require('crypto');
const rawBody = req.rawBody; // unmodified body
const normalized = JSON.stringify(JSON.parse(rawBody));
const signature = req.get('X-Payouts-Signature') || '';
const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(normalized).digest('hex');
if (!timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) {
return res.status(401).end();
}Retry policy
0s → 15s → 30s → 60s → 5m → 15m → 30m → 60m → 120m → 180m until the first HTTP 2xx. Test delivery uses a single attempt.
Receiver requirements
- Return HTTP 2xx on success
- Offload heavy work to your queue and respond 200 immediately
- Response timeout ~10 seconds
Idempotency and security
- Use
eventIdto prevent reprocessing - HTTPS is required; signature is recommended
System Features
Endpoint for getting information about available features and system limitations.
Request Parameters
Required headers:Authorization: Bearer YOUR_API_TOKEN,Accept: application/json; version=1.0.
Example Request
curl -X GET 'https://payouts-dev.quantumlabs.ru/api/v1/features' \
-H 'Accept: application/json; version=1.0' \
-H 'Authorization: Bearer YOUR_API_TOKEN'
Response Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
system |
String | Yes | System name ("payouts") |
environment |
String | Yes | Environment (development, uat, production) |
version |
String | Yes | API version ("v1") |
timestamp |
Integer | Yes | Response Unix timestamp |
features.* |
Object | Yes | Set of capability flags |
result.error |
String | Yes | Error code ("0" = success) |
result.message |
String | Yes | Error message (null on success) |
Detailed features object description
| Field | Type | Required | Description |
|---|---|---|---|
single_payout |
Boolean | Yes | Single payout API availability (always true for users with valid token) |
batch_processing |
Boolean | Yes | Batch file processing support (always true) |
bearer_auth |
Boolean | Yes | Bearer token authentication support (always true) |
card_payouts |
Boolean | Yes | Card payouts availability (depends on client settings and presence of client_id/client_secret) |
sbp_payouts |
Boolean | Yes | SBP payouts availability (depends on client settings and presence of sbp_client_id/sbp_client_secret) |
file_download |
Boolean | Yes | File download permission for payout results (depends on user permissions) |
Example Success Response
{
"system": "payouts",
"environment": "production",
"version": "v1",
"timestamp": 1737097200,
"features": {
"single_payout": true,
"batch_processing": true,
"bearer_auth": true,
"card_payouts": false,
"sbp_payouts": false,
"file_download": false
},
"result": { "error": "0", "message": null }
}
Example Error Response
{
"result": {
"error": "UNAUTHORIZED",
"message": "Invalid or missing Bearer token"
}
}
Integration Examples
Python
import requests
class PayoutsAPI:
def __init__(self, api_token, base_url='https://payouts-dev.quantumlabs.ru/api/v1/'):
self.api_token = api_token
self.base_url = base_url
self.headers = {
'Content-Type': 'application/json; charset=UTF-8',
'Accept': 'application/json; version=1.0',
'Authorization': f'Bearer {api_token}'
}
def create_payout(self, order_number, amount, return_url, description=None, payout_type='card'):
data = {
'orderNumber': order_number,
'amount': amount,
'urls': {'returnUrl': return_url},
'type': payout_type
}
if description:
data['description'] = description
response = requests.post(f'{self.base_url}/createPayout', json=data, headers=self.headers)
return response.json()
def get_payout_status(self, order_number):
response = requests.post(
f'{self.base_url}/getPayoutStatus',
json={'orderNumber': order_number},
headers=self.headers
)
return response.json()
# Usage
api = PayoutsAPI('your_token_here')
result = api.create_payout('ORDER_001', 100000, 'https://example.com/success')
print(result)
JavaScript/Node.js
const axios = require('axios');
class PayoutsAPI {
constructor(apiToken, baseUrl = 'https://payouts-dev.quantumlabs.ru/api/v1') {
this.apiToken = apiToken;
this.baseUrl = baseUrl;
this.headers = {
'Content-Type': 'application/json; charset=UTF-8',
'Accept': 'application/json; version=1.0',
'Authorization': `Bearer ${apiToken}`
};
}
async createPayout(orderNumber, amount, returnUrl, description = null, type = 'card') {
const data = { orderNumber, amount, urls: { returnUrl }, type };
if (description) data.description = description;
try {
const response = await axios.post(`${this.baseUrl}/createPayout`, data, { headers: this.headers });
return response.data;
} catch (error) {
throw new Error(`API Error: ${error.response?.data || error.message}`);
}
}
}
// Usage
const api = new PayoutsAPI('your_token_here');
api.createPayout('ORDER_001', 100000, 'https://example.com/success')
.then(result => console.log(result))
.catch(error => console.error(error));
Error Handling
API uses standard HTTP status codes to indicate success or failure of requests.
Error Codes
| HTTP Code | Error Code | Description |
|---|---|---|
| 400 | VALIDATION_ERROR | Input data validation error |
| 401 | UNAUTHORIZED | Invalid or missing Bearer token |
| 403 | PERMISSION_DENIED | Insufficient permissions for operation |
| 500 | INTERNAL_ERROR | Internal server error |
Best Practices
Security
- Use HTTPS - never send API tokens over unprotected connections
- Store tokens securely - use environment variables
- Token rotation - regularly update API tokens
Performance
- Respect limits - don't exceed 30 requests per second
- Use timeouts - set reasonable timeouts for HTTP requests
- Retry logic - implement exponential backoff for retries
Reliability
- Error handling - always check response codes
- Idempotency - use unique orderNumber values
- Logging - log all API requests and responses