Merchant Integration API
Overview
This document describes the API for merchant integration with the Route payment platform. The API allows merchants to create payment requests (pay-in), receive status updates via webhooks, check payment statuses, and manage their account.
Base URL: https://route.black
Authorization
Obtaining Credentials
To begin integration, you must:
- Request access to the merchant dashboard from your account manager.
- A merchant account will be created for you. Each merchant can have one or more shops.
- Credentials are issued per shop, not per merchant. Each shop has its own API Key and Secret Key.
- Obtain your API Key (
sk_live_...) and Secret Key (64-character hex string) from the shop settings page. - Ensure the shop has a traffic group configured — this determines which trader pool handles your payments.
- Provide your account manager with the static public IP address(es) of the server(s) that will call the API and host your callback endpoint, so we can add them to the access allowlist (see IP Allowlisting).
Authentication Headers
All requests to the Merchant API must include the following headers:
| Header | Description |
|---|---|
X-API-Key |
Your merchant API key (format: sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx) |
X-Signature |
HMAC-SHA256 signature of the request (see below) |
Content-Type |
application/json for JSON requests |
Signature Generation
The X-Signature header is a Base64-encoded HMAC-SHA256 signature computed from the concatenation of:
- HTTP method (GET, POST, etc.)
- Full request URL (including query parameters)
- Request body (only for
application/jsonrequests; empty string for GET requests)
Signature formula:
X-Signature = Base64(HMAC-SHA256(secret_key, METHOD + URL + BODY))
Code Examples
PHP:
<?php
$method = 'POST';
$url = 'https://route.black/api/v1/payments';
$body = json_encode(['currency' => 'RUB', 'amount' => 5000]);
$secretKey = 'your_secret_key';
$stringToSign = $method . $url . $body;
$signature = base64_encode(hash_hmac('sha256', $stringToSign, $secretKey, true));
// Set header: X-Signature: $signature
JavaScript (Node.js):
const crypto = require('crypto');
const method = 'POST';
const url = 'https://route.black/api/v1/payments';
const body = JSON.stringify({ currency: 'RUB', amount: 5000 });
const secretKey = 'your_secret_key';
const stringToSign = method + url + body;
const signature = crypto
.createHmac('sha256', secretKey)
.update(stringToSign)
.digest('base64');
// Set header: X-Signature: <signature>
Python:
import hmac
import hashlib
import base64
import json
method = 'POST'
url = 'https://route.black/api/v1/payments'
# Use separators=(',', ':') for compact JSON — must match the body sent in the request exactly
body = json.dumps({'currency': 'RUB', 'amount': 5000}, separators=(',', ':'))
secret_key = 'your_secret_key'
string_to_sign = method + url + body
signature = base64.b64encode(
hmac.new(
secret_key.encode(),
string_to_sign.encode(),
hashlib.sha256
).digest()
).decode()
# Set header: X-Signature: <signature>
Important: The signature is computed over the exact bytes of the request body. Ensure that the JSON you sign and the JSON you send are byte-for-byte identical. Use compact serialization (no extra spaces) to avoid inconsistencies across languages.
Error Response Format
All errors from the API follow a single envelope:
{
"success": false,
"error": "<token>"
}
The error field is a stable, snake_case token — never raw English text. Merchant integrators should switch on this token to render localised messages or branch their retry/escalation logic. The HTTP status code carries the broad class (400 / 401 / 404 / 409 / 410 / 422 / 500), and the token carries the precise reason.
Conventions
-
4xx tokens are stable. Once a token is published here it does not change shape. New tokens may be added.
-
5xx are masked. Any internal failure (DB unavailable, RabbitMQ down, provider integration error, …) surfaces as a single generic token:
HTTP 500 { "success": false, "error": "internal_error" }The original cause is logged server-side. Treat any 5xx response as transient and follow the retry policy.
-
No-capacity is NOT an error. When the platform cannot issue requisites for a well-formed, authenticated request — for any reason — the response is HTTP 200 with an empty
databody:HTTP 200 { "success": true, "data": null }The merchant is never informed of the underlying cause. This includes (and is not limited to): no eligible trader, no available requisite, insufficient trader balance, shop-level block, concurrent-deal limit, per-requisite limit exceeded, shop not yet attached to a routing group, cascade exhausted, internal provider error. All such cases collapse into the same empty 200 response.
Merchants should treat the empty 200 as a transient "not right now" signal: retry with backoff, or route the customer to an alternative payment method. The real cause is recorded server-side in the pre-deal-error log for admin diagnostics — never on the wire.
Canonical Token List
The following tokens are emitted by the payment endpoints documented below. Each row pins the HTTP status code, the token, and the trigger.
| Status | Token | When |
|---|---|---|
| 400 | currency_mismatch |
Request currency does not match the shop's allowed currency |
| 400 | amount_below_minimum |
amount < shop min_amount_in |
| 400 | amount_above_maximum |
amount > shop max_amount_in |
| 400 | duplicate_external_id |
external_id already used by this merchant (when shop has allow_duplicate_invoices=false) |
| 400 | callback_url_required |
No callback_url in the request and none configured on the shop |
| 401 | unauthorized |
Missing / invalid X-API-Key or X-Signature |
| 404 | payment_not_found |
GET /api/v1/payments/{id} for an ID that does not exist or that the caller does not own |
| 409 | payment_not_pending |
Operation requires waiting_sms/created state, but the payment is already confirmed/failed/expired |
| 410 | payment_expired |
Payment's expires_at has passed (RFC 7231 §6.5.9 Gone) |
| 500 | internal_error |
Sanitised server-side failure |
What is NOT in this table. Routing-side conditions ("no trader available", "shop not configured", "balance low", "cascade group missing", etc.) are deliberately absent. They never reach the merchant as 4xx — they all collapse into the same empty
200 OKresponse. See the no-capacity bullet above.
Host-to-Host Integration
Host-to-Host integration enables direct server-to-server communication between the merchant's backend and the payment platform API, allowing full automation of payment processing.
IP Allowlisting
For security, access to the Merchant API is restricted to approved source IP addresses. Before going live, provide your account manager with the static public IP address(es) or CIDR ranges of every server that will:
- send API requests to the platform (pay-in, pay-out, and status queries), and
- host your callback (webhook) endpoint.
Requests originating from IP addresses that are not on the allowlist may be rejected before they reach the API. If your infrastructure scales or your egress IP addresses change, notify us in advance so we can update the allowlist and avoid any interruption to your integration.
Receiving callbacks: platform callbacks are delivered from a fixed set of outbound IP addresses. If your callback endpoint sits behind a firewall, request our current outbound IP ranges from your account manager and allow them on your side.
Pay-In Request Creation
The pay-in flow allows merchants to accept payments from their customers.
Flow Overview
1. Merchant server -> POST /api/v1/payments -> Creates payment, receives requisites
2. Merchant -> Displays requisites to user -> User transfers funds
3. Platform -> Detects incoming transfer -> Confirms payment
4. Platform -> POST to merchant callback -> Notifies merchant of status change
5. Merchant -> GET /api/v1/payments/{id} -> (Optional) Verify final status
Step 1: Create Payment
The merchant sends a payment creation request. The system routes the payment to an available trader, selects appropriate requisites (card number or SBP phone), and returns them in the response.
Important — three response classes:
201 Created+ populateddata— requisites issued, show them to the customer.200 OK+data: null— the platform cannot currently issue requisites. The cause is never exposed: no trader, no requisite, balance issues, shop block, cascade gap, provider error — all surface as the same empty response. Retry with backoff or offer the customer an alternative method.4xx+{ "error": "<token>" }— caller-correctable rejection of the request itself (bad currency, amount out of shop range, duplicateexternal_id, missingcallback_url, etc.). See Error Response Format.Anything else (
5xx) is a transient platform failure — the body is sanitised tointernal_errorand the call should be retried.
Request:
POST /api/v1/payments
Content-Type: application/json
X-API-Key: sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
X-Signature: <computed_signature>
{
"currency": "RUB",
"amount": 5000.00,
"external_id": "order-12345",
"callback_url": "https://merchant.example.com/webhooks/payments",
"payment_method": "sbp"
}
currency,amountandpayment_methodare required. Theexternal_idandcallback_urlfields are optional (see the request body table under Create Payment).
Response (201 Created):
{
"success": true,
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "waiting_sms",
"currency": "RUB",
"amount": 5000.00,
"amount_usdt": 52.63,
"external_id": "ext-unique-id",
"requisite": {
"type": "card",
"card_number": "2200 1234 5678 9012",
"card_holder": "IVAN IVANOV",
"bank_name": "Sberbank",
"bank_code": "sberbank",
"timer_seconds": 900
},
"expires_at": "2026-02-15T15:15:00Z",
"created_at": "2026-02-15T15:00:00Z"
}
}
The merchant must display the returned requisites to the customer and instruct them to transfer the exact amount. The payment expires after the timer_seconds period (default 15 minutes).
Possible requisite types:
card— Bank card transfer / C2C (card_number + card_holder)sbp— SBP (Faster Payments System) transfer (phone_number + bank_name)transgran— Cross-border card transfer (card_number + card_holder)
Step 2: Payment Confirmation
Once the customer completes the transfer, the platform automatically detects the incoming payment. No manual confirmation endpoint is required from the merchant side — the system handles detection automatically via its internal network.
Step 3: Webhook Notification
Upon payment confirmation, the platform sends a webhook to the merchant's configured callback_url. See the Callbacks section for details.
Pay-Out Request Creation
Note: Pay-out functionality is currently under development. Contact your account manager for the timeline and early access.
The pay-out flow will allow merchants to send funds to customer bank accounts or cards.
Callbacks (Webhooks)
Overview
The platform sends HTTP POST callbacks to the merchant's callback_url whenever a payment status changes. The callback URL is configured in the merchant profile (not per-request).
Authentication
Each webhook request includes an X-Signature header computed using the merchant's secret key:
X-Signature = Base64(HMAC-SHA256(secret_key, "POST" + webhook_url + body))
The merchant must validate this signature to ensure the webhook is authentic and has not been tampered with.
Webhook Payload
{
"payment_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"external_id": "ext-unique-id",
"status": "confirmed",
"currency": "RUB",
"amount": 5000.00,
"amount_usdt": 52.63,
"tx_id": "",
"confirmed_at": "2026-02-15T15:05:30Z",
"timestamp": "2026-02-15T15:05:31Z"
}
Webhook Payload Fields
| Field | Type | Description |
|---|---|---|
payment_id |
string (UUID) | Unique payment identifier in the platform |
external_id |
string | External reference ID |
status |
string | New payment status (see PaymentStatus) |
currency |
string | Payment currency (e.g., RUB) |
amount |
number | Payment amount in original currency |
amount_usdt |
number | Payment amount converted to USDT |
tx_id |
string | Blockchain transaction ID (when completed) |
confirmed_at |
string (ISO 8601) | Confirmation timestamp |
timestamp |
string (ISO 8601) | Webhook generation timestamp |
Expected Response
The merchant must respond with HTTP status code 200 OK. Any other status code is considered a failure, and the webhook will be retried.
Retry Policy
If the merchant's server fails to respond with 200, the platform retries with exponential backoff:
| Attempt | Delay |
|---|---|
| 1 | 5 minutes |
| 2 | 15 minutes |
| 3 | 1 hour |
| 4 | 6 hours |
| 5-10 | 24 hours |
Maximum 10 retry attempts. After exhaustion, the webhook is marked as failed and can be manually retried through the dashboard or API.
Signature Verification Example
PHP:
<?php
$secretKey = 'your_secret_key';
$webhookUrl = 'https://yoursite.com/webhook';
$body = file_get_contents('php://input');
$receivedSignature = $_SERVER['HTTP_X_SIGNATURE'];
$stringToSign = 'POST' . $webhookUrl . $body;
$expectedSignature = base64_encode(
hash_hmac('sha256', $stringToSign, $secretKey, true)
);
if (!hash_equals($expectedSignature, $receivedSignature)) {
http_response_code(403);
exit('Invalid signature');
}
// Process the webhook...
http_response_code(200);
Python:
import hmac
import hashlib
import base64
secret_key = 'your_secret_key'
webhook_url = 'https://yoursite.com/webhook'
body = request.get_data(as_text=True)
received_signature = request.headers.get('X-Signature')
string_to_sign = 'POST' + webhook_url + body
expected_signature = base64.b64encode(
hmac.new(
secret_key.encode(),
string_to_sign.encode(),
hashlib.sha256
).digest()
).decode()
if not hmac.compare_digest(expected_signature, received_signature):
return 'Invalid signature', 403
# Process the webhook...
return 'OK', 200
Disputes (Appeals)
A dispute (appeal) is opened when a customer claims they completed a transfer but the payment was not credited to them — for example, they paid after the invoice expired, or to a requisite the system did not automatically match.
Important: merchants have no dedicated dispute API. Intake and resolution happen entirely on the platform side — the merchant only observes the outcome.
How it works
- Intake. The customer raises the appeal through platform support, providing the amount, the requisite (last 4 digits of the card or the phone number), the time of payment, and optionally supporting evidence (receipt/screenshot).
- Matching. The platform searches for the corresponding payment by requisite, exact amount, and time (±2 hours). If exactly one is found, the payment moves to
disputestatus and the appeal is linked to it. - Moderation. A platform operator reviews the evidence and makes a decision.
Outcome and merchant impact
| Decision | What happens to the payment | Merchant notification |
|---|---|---|
| Approved | The payment is settled and moves to confirmed |
Standard webhook with status confirmed — including for a payment previously reported as expired |
| Rejected | The payment returns to expired |
No additional webhook |
Integration note — late confirmation. Because of disputes, a payment you already received as
expiredmay later arrive asconfirmed. Do not treatexpiredas unconditionally final: process theconfirmedwebhook idempotently even when it arrives afterexpired, reconciling bypayment_id/external_id.
The transition into dispute itself is not delivered as a separate webhook — you can observe this status by polling GET /api/v1/payments/{id}.
API Reference
Create Payment
Creates a new pay-in payment request and returns transfer requisites.
Endpoint: POST /api/v1/payments
Authentication: API Key + Signature
Request Headers:
| Header | Required | Description |
|---|---|---|
X-API-Key |
Yes | Merchant API key |
X-Signature |
Yes | Request signature |
Content-Type |
Yes | application/json |
Request Body:
| Parameter | Type | Required | Description |
|---|---|---|---|
currency |
string | Yes | Currency code, 3 characters (e.g., RUB, KZT, USD, EUR) |
amount |
number | Yes | Payment amount (minimum: 100) |
payment_method |
string | Yes | Payment method — selects the routing (traffic group). One of card (C2C), sbp (SBP), transgran (cross-border cards). |
external_id |
string | No | Your own order/invoice ID for correlation. Auto-generated UUID if omitted. Must be unique per merchant. |
callback_url |
string | No | Webhook URL for this payment's status notifications. Overrides the default callback_url from merchant profile. |
shop_id |
string (UUID) | No | Target shop. Optional: if omitted, the platform picks your merchant's shop by currency (+ payment_method). Pass shop_id (see GET /api/v1/shops) only when more than one shop matches — otherwise shop_ambiguous is returned. Must belong to your merchant, otherwise shop_not_owned. |
customer_id |
string | Yes | Stable identifier of your customer (the payer), the same across all their payments. Used by antifraud for per-user history and blocking. If omitted — customer_id is required. If the user is blocked at your merchant, creating a payment returns user_blocked. |
customer_phone |
string | No | Payer phone (optional). |
customer_email |
string | No | Payer email (optional). |
device_id |
string | No | Payer device identifier (optional). |
Request Example:
{
"currency": "RUB",
"amount": 5000.00,
"external_id": "order-12345",
"callback_url": "https://merchant.example.com/webhooks/payments",
"payment_method": "sbp"
}
Response (201 Created):
{
"success": true,
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "waiting_sms",
"currency": "RUB",
"amount": 5000.00,
"amount_usdt": 52.63,
"external_id": "generated-uuid",
"requisite": {
"type": "card",
"card_number": "2200 1234 5678 9012",
"card_holder": "IVAN IVANOV",
"bank_name": "Sberbank",
"bank_code": "sberbank",
"timer_seconds": 900
},
"expires_at": "2026-02-15T15:15:00Z",
"created_at": "2026-02-15T15:00:00Z"
}
}
Error Responses:
The response body is {"success": false, "error": "<token>"} — see Error Response Format for the full token list. The most relevant tokens for this endpoint are:
| Status | Token | Trigger |
|---|---|---|
| 400 | currency_mismatch |
currency differs from shop's allowed currency |
| 400 | amount_below_minimum |
amount < shop min_amount_in |
| 400 | amount_above_maximum |
amount > shop max_amount_in |
| 400 | duplicate_external_id |
external_id already used by this merchant |
| 400 | callback_url_required |
No callback_url in request and none configured on the shop |
| 400 | shop_ambiguous |
shop_id omitted but more than one shop matches currency+payment_method — pass shop_id |
| 400 | invalid_shop_id |
shop_id is malformed or the shop was not found |
| 400 | shop_not_owned |
The given shop_id does not belong to your merchant |
| 400 | shop_id_conflict |
The key is shop-scoped but a different shop_id was sent in the body |
| 401 | unauthorized |
Missing/invalid X-API-Key or X-Signature |
| 500 | internal_error |
Transient server failure — retry |
No-capacity is not an error and never reveals the cause. When the platform cannot issue requisites — for any reason at all (no trader, no requisite, balance, shop block, cascade gap, provider error, …) — the response is HTTP 200 with
data: null. The 4xx tokens above are limited to syntactic / caller-correctable issues. See Error Response Format.
List Shops
Returns your merchant's shops. Use it to discover the shop_id required by the shop_id field in Create Payment.
Endpoint: GET /api/v1/shops
Authentication: API key (X-API-Key) — the X-Signature header is not required for this GET request. Works with both a unified merchant key and a shop-bound key (both return all of the merchant's shops).
Response (200 OK):
{
"success": true,
"data": [
{
"shop_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "My Shop",
"currency": "RUB",
"methods": ["sbp"],
"payment_types": ["card", "sbp"],
"active": true
}
]
}
| Field | Description |
|---|---|
shop_id |
Shop UUID — pass it in the POST /api/v1/payments body |
name |
Shop name |
currency |
Shop currency (may be empty if unset) |
methods |
Allowed pay-in methods (e.g. sbp, card) |
payment_types |
Allowed payment types |
active |
true if the shop is active and pay-in is not blocked |
Error responses:
| Status | Token | Condition |
|---|---|---|
| 401 | unauthorized |
Missing/invalid X-API-Key |
| 500 | internal_error |
Transient server failure — retry |
Get Payment by ID
Retrieves the current status and details of a payment.
Endpoint: GET /api/v1/payments/{id}
Authentication: API Key (X-API-Key) or JWT Bearer Token — no X-Signature required for GET requests
URL Parameters:
| Parameter | Type | Description |
|---|---|---|
id |
string (UUID) | Payment identifier |
Response (200 OK):
{
"success": true,
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "confirmed",
"currency": "RUB",
"amount": 5000.00,
"amount_usdt": 52.63,
"external_id": "generated-uuid",
"created_at": "2026-02-15T15:00:00Z",
"updated_at": "2026-02-15T15:05:30Z"
}
}
Error Responses:
| Status | Token | Trigger |
|---|---|---|
| 400 | invalid_id |
Path parameter is not a valid UUID |
| 401 | unauthorized |
Missing/invalid X-API-Key |
| 404 | payment_not_found |
No payment with that ID/external_id exists for this merchant |
| 500 | internal_error |
Transient server failure — retry |
Get Merchant Payments
Retrieves a paginated list of payments for the authenticated merchant.
Endpoint: GET /api/v1/merchants/{merchant_id}/payments
Authentication: JWT Bearer Token
URL Parameters:
| Parameter | Type | Description |
|---|---|---|
merchant_id |
string (UUID) | Merchant identifier |
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
page |
integer | 1 | Page number |
page_size |
integer | 20 | Items per page (max: 100) |
Response (200 OK):
{
"success": true,
"data": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "confirmed",
"currency": "RUB",
"amount": 5000.00,
"amount_usdt": 52.63,
"external_id": "generated-uuid",
"created_at": "2026-02-15T15:00:00Z"
}
],
"pagination": {
"total": 150,
"page": 1,
"page_size": 20,
"total_pages": 8
}
}
Webhook Management
Endpoints for monitoring and managing webhook delivery.
Authentication: JWT Bearer Token
Get Webhook Statistics
Endpoint: GET /api/v1/merchant/webhooks/stats
Response:
{
"success": true,
"data": {
"pending": 2,
"processing": 0,
"completed": 145,
"failed": 3
}
}
Get Failed Webhooks
Endpoint: GET /api/v1/merchant/webhooks/failed
Retry Failed Webhook
Endpoint: POST /api/v1/merchant/webhooks/{id}/retry
Get Webhook Details
Endpoint: GET /api/v1/merchant/webhooks/{id}
API Object Descriptions
PaymentStatus
| Status | Description |
|---|---|
created |
Payment has been created, trader assignment in progress |
waiting_sms |
Requisites assigned, waiting for customer to transfer funds |
confirmed |
Payment confirmed — funds received and verified |
completed |
Payment fully completed — settlement processed |
failed |
Payment failed at any processing stage |
expired |
Payment not completed within the timeout period (default: 15 min) |
dispute |
A dispute (appeal) has been opened for the payment — under review. See Disputes (Appeals) |
PaymentResponse
| Field | Type | Description |
|---|---|---|
id |
string (UUID) | Unique payment identifier |
status |
PaymentStatus | Current payment status |
currency |
string | Payment currency code (e.g., RUB) |
amount |
number | Payment amount in original currency |
amount_usdt |
number | Payment amount converted to USDT |
external_id |
string | External reference identifier |
requisite |
RequisiteDTO | Transfer requisites (when status is waiting_sms) |
expires_at |
string (ISO 8601) | Payment expiration timestamp |
created_at |
string (ISO 8601) | Payment creation timestamp |
RequisiteDTO
| Field | Type | Description |
|---|---|---|
type |
string | Requisite type: card, sbp or transgran |
phone_number |
string | SBP phone number (for type sbp) |
card_number |
string | Bank card number (for type card / transgran) |
card_holder |
string | Cardholder name (for type card) |
bank_name |
string | Bank name |
bank_code |
string | Bank identifier code |
timer_seconds |
integer | Time in seconds before payment expires |
WebhookPayload
| Field | Type | Description |
|---|---|---|
payment_id |
string (UUID) | Payment identifier |
external_id |
string | External reference ID |
status |
PaymentStatus | Updated payment status |
currency |
string | Payment currency |
amount |
number | Amount in original currency |
amount_usdt |
number | Amount in USDT |
tx_id |
string | Blockchain transaction hash (on completion) |
confirmed_at |
string (ISO 8601) | Confirmation timestamp |
timestamp |
string (ISO 8601) | Webhook timestamp |
Error Handling
Response Format
All error responses follow the same format:
{
"success": false,
"error": "Error description message"
}
HTTP Status Codes
| Code | Description |
|---|---|
| 200 | Success |
| 201 | Created successfully |
| 400 | Bad Request — invalid parameters or business logic violation |
| 401 | Unauthorized — invalid or missing API key / signature |
| 404 | Not Found — requested resource does not exist |
| 409 | Conflict — duplicate request (idempotency) |
| 500 | Internal Server Error |
Common Error Messages
| Error | Cause |
|---|---|
Invalid request body |
Malformed JSON or missing required fields |
currency must be 3 characters |
Currency code not in ISO 4217 format |
Merchant not authenticated |
Missing or invalid X-API-Key header |
Invalid signature |
X-Signature does not match computed value |
Merchant has no callback URL configured |
Merchant profile missing callback_url |
Payment not found or already processed |
Payment ID not found or in terminal state |
Payment is not waiting for SMS confirmation |
Payment in unexpected status |
Supported Currencies
| Code | Currency |
|---|---|
| RUB | Russian Ruble |
| USD | US Dollar |
| EUR | Euro |
| THB | Thai Baht |
Note: Available currencies depend on the merchant's traffic group configuration. Contact your account manager for activation.
Rate Limits
API requests are rate-limited per merchant. Default limits:
| Endpoint | Limit |
|---|---|
POST /payments |
60 requests/minute |
GET /payments/* |
120 requests/minute |
Exceeding rate limits returns HTTP 429 (Too Many Requests).
Testing and Sandbox
For integration testing, use the sandbox environment provided by your account manager. Sandbox API keys use the prefix sk_test_.
All flows work identically to production, but no real money movement occurs.