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 on type and settings).
  • POST …/performPayout — single payout to card only (target.pan) with asynchronous bank submission.
  • POST …/cancelPayout — cancel an API payout by orderNumber before bank processing.
  • POST …/getPayoutStatus — payout status by orderNumber.
  • 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

  1. Log into your account
  2. Go to Settings → API
  3. Click "Create API Token"
  4. Copy the generated token
  5. Store the token securely

Using the Token

HTTP Header
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

POST /api/v1/createPayoutJSON
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"
    }
  }'

Alfa Request Example

POST /api/v1/performPayoutAlfa
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_ALFA_12345",
    "amount": 150000,
    "description": "One-time payout via Alfa",
    "channel": "alfa",
    "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)
payoutUrl String Link to the recipient details page
orderId String Internal order identifier
orderNumber String Order number from the request

Example Success Response

200HTTP 200 - 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

400HTTP 400 - VALIDATION_ERROR
{
  "result": {
    "error": "VALIDATION_ERROR",
    "message": "urls.returnUrl is required; amount must be positive"
  }
}

Payout Lifecycle

  1. Registration - API creates payout with status uploaded
  2. Details Entry - user goes to payoutUrl to enter recipient details
  3. Processing - status changes to created, then processing
  4. Completion - final status success, failed, or cancelled

Perform Single Payout

Endpoint for a single payout to a bank card (target.pan). The API creates the payout and submits it to the bank asynchronously via a worker.

Note: a successful response means the payout was accepted into the queue. Check the final result through getPayoutStatus or webhook. SBP is not supported on this endpoint.

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 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)
channel String No Processing bank channel: vtb by default or alfa. Access is checked separately for the selected channel.

Example Request

POST /api/v1/performPayoutJSON
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",
    "channel": "vtb",
    "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)
orderId String Internal request identifier generated when the payout is registered. It is not the final bank code.
orderNumber String Order identifier
channel String Actual processing bank channel for the request

The immediate performPayout response does not include the final status, amount, creation date, or masked recipient details. Retrieve those fields through getPayoutStatus or webhook after processing.

Example Success Response

200HTTP 200 - Success
{
  "result": { "error": "0", "message": null },
  "orderId": "PAY_2024_001",
  "orderNumber": "ORDER_12345",
  "channel": "vtb"
}

Example Error Response

400HTTP 400 - VALIDATION_ERROR
{
  "result": {
    "error": "VALIDATION_ERROR",
    "message": "orderNumber is required; amount must be greater than 0"
  }
}

Channel Access Error

If the selected channel is disabled for the environment or the company lacks the required feature/integration, performPayout returns HTTP 403 and does not create a new payout.

403HTTP 403 - PERMISSION_DENIED
{
  "result": {
    "error": "PERMISSION_DENIED",
    "message": "Alfa Bank card payouts are not configured for this account"
  }
}
  • Repeated call with the same orderNumber is idempotent (no re-submission to the bank).
  • Requires configured Client ID/Secret and permission for card payouts.

Cancel Payout

This endpoint cancels an API-created payout by orderNumber. Cancellation is available only while the payout is in uploaded or created; after it moves to processing, success, or failed, cancellation is no longer available.

Idempotency: cancelling an already cancelled payout returns a successful response with cancelled 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 36 characters)

Example Request

POST /api/v1/cancelPayoutJSON
curl -X POST 'https://payouts-dev.quantumlabs.ru/api/v1/cancelPayout' \
  -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"
  }'

Example Success Response

200HTTP 200 - Success
{
  "result": { "error": "0", "message": null },
  "orderNumber": "ORDER_12345",
  "orderStatus": "cancelled",
  "orderId": "PAY_2024_001",
  "channel": "vtb"
}

Example Error Response

409HTTP 409 - PAYOUT_NOT_CANCELLABLE
{
  "result": {
    "error": "PAYOUT_NOT_CANCELLABLE",
    "message": "Payout cannot be cancelled in its current state"
  }
}

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

POST /api/v1/getPayoutStatusJSON
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 Internal request identifier or system-stored code (when 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

200HTTP 200 - Success 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" }
}

Test Data

Applies to a specific payout when its valid recipient details match a scenario. Any valid recipient details that do not match the mock service mask are sent to the real banking system.

  • Card success: card PAN ending with 0001 or 1111 leads the payout to final status success.
  • Card failed: card PAN ending with 0002 or 9999 leads the payout to final status failed.
  • SBP success: SBP phone ending with 0001 or 1122 leads the payout to final status success.
  • SBP failed: SBP phone ending with 0002 or 9999 leads the payout to final status failed.
  • Checking the result: after creating the request, check the status in the usual way through getPayoutStatus or webhook. The payout first moves to processing, then to final success or failed.

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/json
  • X-Payouts-Event
  • X-Payouts-Event-Id
  • X-Payouts-Timestamp
  • X-Payouts-Signature (optional): sha256=<hex>

Signature verification (optional)

Signature calculation algorithm

The signature is calculated using HMAC-SHA256 algorithm as follows:

  1. Source data: take the raw HTTP request body exactly as it was sent
  2. JSON normalization: apply JSON minify to the request body (remove spaces, line breaks, tabs)
  3. Secret key: use the secret generated in webhook settings
  4. Hashing algorithm: apply HMAC-SHA256 to the normalized JSON using the secret
  5. Result format: get a hexadecimal string and add the sha256= prefix
  6. Comparison: compare the resulting signature with the X-Payouts-Signature header 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_1234567890abcdef1234567890abcdef12345678

4. 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=8a3f7e2c9b1d4a5e6f0c8b7a9d3e2f1c4b5a6e7d8c9b0a1f2e3d4c5b6a7e8d9f

Code 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 eventId to 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

GET /api/v1/features
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

200HTTP 200 - 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

401HTTP 401 - UNAUTHORIZED
{
  "result": {
    "error": "UNAUTHORIZED",
    "message": "Invalid or missing Bearer token"
  }
}

Integration Examples

Python

Python with requests library
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

Node.js with axios
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
400 INVALID_CONTENT_TYPE Content-Type: application/json; charset=UTF-8 is required
400 INVALID_ACCEPT_HEADER Accept must include application/json; version=1.0
400 LIMIT_EXCEEDED Single payout limits were exceeded
401 UNAUTHORIZED Invalid or missing Bearer token
403 PERMISSION_DENIED Insufficient permissions for operation
404 PAYOUT_NOT_FOUND Payout with this orderNumber was not found
409 PAYOUT_NOT_CANCELLABLE Payout cannot be cancelled in its current status
429 RATE_LIMIT_EXCEEDED API request rate limit was exceeded
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 - for performPayout, don't exceed 30 requests per minute per user/API token. If the limit is exceeded, the API returns HTTP 429 with Retry-After: 60
  • 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