AlfaBit API Documentation
Complete REST API for integrating all AlfaBit features into your applications.
Funding Account — main wallet for deposits, withdrawals, transfers. Trading Account — shared balance for exchange trading; via the API it exposes the USDT/RUB orderbook, while crypto exchange over the API runs from the Funding Account.
To get started, create API keys in Developer Console.
Quick Start
Get started with the API in 5 minutes. This guide will walk you through from creating an API key to your first trade.
Create an API Key
Go to Developer Console → API Keys → Create Key. Select permissions and save the Secret Key — it is shown only once.
Set Up Request Signing
Every request is signed with HMAC-SHA256. Copy the ready-made code from the Authentication section (Python / JavaScript) — it works out of the box.
Verify Connection
Send your first request — get the account profile:
GET /api/v1/integration/account/profile
→ If you see your email and ID — everything works!Check Your Balances
Make sure you have funds for operations:
GET /api/v1/integration/account/wallets
→ You will see all wallets with balances and deposit addresses.Make Your First Operation
Choose what you want to do and go to the relevant section:
- Crypto spot rates — BTC, ETH tickers, all pairs (public)
- Fiat spot rates — USDT/RUB orderbook, instruments, stats (public)
- Converter rates — crypto/crypto and crypto/fiat with fixed rate (public)
- Crypto spot trading — market/limit orders BTC ↔ ETH
- Fiat spot trading — USDT/RUB orderbook orders
- Converter exchange — instant exchange at fixed rate
- Issue a card — Visa/Mastercard with crypto
- Accept payment — invoices for your business
Platform Architecture
Before you start, it is important to understand how the platform works. This will help you choose the right endpoints and avoid errors.
Two Account Types
Funding Account
Main account. All your funds are stored here. Used for: deposits, withdrawals, crypto exchanges, transfers, service payments and cards.
Trading Account
A shared balance for exchange trading — in the personal cabinet terminal every pair trades from it. Via the API it currently runs the USDT/RUB orderbook with market and limit orders: transfer funds via /funding/transfer/to-trading before trading. Crypto pairs are exchanged via the API as an instant market swap from the funding account (/integration/spot/crypto) — the orderbook and limit orders are not yet available for them via the API.
The currency code in the examples below (e.g. RUB or USDT) is shown to illustrate the request structure. The actual list of supported currencies for your integration is confirmed separately during onboarding.
Exchange Methods — When to Use Which?
| Module | What is it | Public Rates | Trading | Pairs |
|---|---|---|---|---|
| Crypto Spot | Crypto markets — crypto ↔ crypto | GET /spot/crypto/tickersGET /spot/crypto/market-info | Market / Limit orders | BTC/USDT, ETH/USDT |
| Fiat Spot | AlfaBit orderbook — crypto ↔ fiat | GET /spot/fiat/orderbookGET /spot/fiat/statsGET /spot/fiat/instruments | Market / Limit orders | USDT/RUB |
| Converter | Instant exchange with fixed rate | GET /converter/crypto/rateGET /converter/fiat/rate | Exchange at quote | BTC → ETH, USDT → RUB |
Each of the three modules has public endpoints (rates, tickers, orderbook) available without an API key — for widget integration, price monitoring and analytics. Trading operations (orders, exchange) require an API key with appropriate permissions.
Common Response Format
All API responses follow a common format. Successful operations return data in the data field, errors — in the error field:
{ "success": true, "data": { ... }, "ts": 1706000000 }{ "success": false, "error": { "code": "...", "message": "..." }, "ts": 1706000000 }Authentication
All requests must be signed with HMAC-SHA256 using your secret key.
HTTP Headers
| Header | Required | Description |
|---|---|---|
X-API-Key | Required | Your public key pk_live_xxx |
X-API-Signature | Required | HMAC-SHA256 signature |
X-API-Timestamp | Required | Unix timestamp (seconds) |
X-Device-Id | Optional | Device identifier / fingerprint (anti-fraud). Pass it if available. |
All operation-creating requests (deposits, withdrawals, transfers, conversions, invoices) are logged on our side: source IP, User-Agent and X-Device-Id (if provided). This data is used for API key security and anti-fraud scoring and does not affect operation processing. The KYC-API checkout additionally requires the mandatory payer_ip and external_payment_id fields (see the “KYC-API Checkout” section).
IP allowlist
Each key can be restricted to a list of allowed addresses in the Developer Console — the “IP Whitelist” field when creating a key or in its settings. While the list is empty the key is accepted from any address. Once it contains at least one entry, requests from every other address are rejected: HTTP 403, error code IP_NOT_ALLOWED.
Accepted values are an exact address (203.0.113.10), a CIDR subnet (203.0.113.0/24) and IPv6. An entry like ::ffff:203.0.113.10 is treated as the same address as 203.0.113.10. Separate multiple values with commas; clearing the field removes the restriction. The address checked is the public one your request arrives from, so for a server behind NAT specify its public IP, not a private network address.
IP_NOT_ALLOWED is returned even when the signature is completely valid, so if you get a 403 after changing hosting or adding a server, check the key’s address list first. An IP restriction is the strongest protection for a key: even a leaked secret is useless from another address. Always set it for production integrations.
Signature Formula
message = timestamp + method + path + body
signature = HMAC-SHA256(secret_key, message)Code Examples
import hmac, hashlib, time, requests, json
API_KEY = "pk_live_xxxxxxxxxxxxx"
SECRET_KEY = "sk_live_xxxxxxxxxxxxx"
BASE_URL = "https://alfabit.org"
def sign_request(method, path, body=""):
timestamp = str(int(time.time()))
message = f"{timestamp}{method.upper()}{path}{body}"
signature = hmac.new(
SECRET_KEY.encode(), message.encode(), hashlib.sha256
).hexdigest()
return {
"X-API-Key": API_KEY,
"X-API-Signature": signature,
"X-API-Timestamp": timestamp,
"Content-Type": "application/json"
}
# Получить балансы кошельков
path = "/api/v1/integration/account/wallets"
resp = requests.get(f"{BASE_URL}{path}", headers=sign_request("GET", path))
print(resp.json())
# {"success": true, "data": [{"symbol": "USDT", "available": "1250.00", ...}], "ts": 1706000000}
# Создать ордер на крипто обмен
path = "/api/v1/integration/spot/crypto/order"
body = json.dumps({"from_symbol": "USDT", "to_symbol": "BTC", "from_amount": "100"})
resp = requests.post(f"{BASE_URL}{path}", headers=sign_request("POST", path, body), data=body)
print(resp.json())Rate Limits
Rate limits are configured individually per API key. The value is set when creating a key (rate_limit_per_minute field). When the limit is exceeded, the API returns a 429 error.
Error Handling
{
"success": false,
"error": {
"code": "INVALID_SIGNATURE",
"message": "Invalid request signature",
"details": null
},
"ts": 1706000000
}| Code | HTTP | Description |
|---|---|---|
INVALID_API_KEY | 401 | Invalid API key |
INVALID_SIGNATURE | 401 | Invalid signature |
SIGNATURE_EXPIRED | 401 | Timestamp expired (>5 min) |
API_KEY_EXPIRED | 401 | API key has expired |
API_KEY_INACTIVE | 401 | API key is inactive |
IP_NOT_ALLOWED | 403 | Client IP is not in the key’s allowed list |
PERMISSION_DENIED | 403 | Permission denied |
INSUFFICIENT_BALANCE | 400 | Insufficient balance |
DEEP_PAGINATION_NOT_SUPPORTED | 400 | Transaction feed page is too deep (page × limit > 2000) — narrow the period |
Orderbook errors (Fiat Spot)
Rejections from POST /spot/fiat/order, DELETE /spot/fiat/order/{id} and trading account transfers come with a dedicated code, and the numbers you need are in details. Branch on code, not on the message text: message and details.reason_text may change.
{
"success": false,
"error": {
"code": "INSUFFICIENT_LIQUIDITY",
"message": "Not enough counter liquidity in the orderbook to fill this market order",
"details": {
"quote_amount": "3041.770000",
"quote_currency": "RUB",
"available_liquidity": "0.654719",
"base_currency": "USDT",
"min_order_size": "1.000000",
"reason": "available_liquidity_below_min_order_size",
"hint": "Retry later, reduce the order, or place a limit order to wait in the book."
}
},
"ts": 1706000000
}| Code | HTTP | Description | What to do |
|---|---|---|---|
INSUFFICIENT_LIQUIDITY | 400 | The opposite side of the book cannot fill your market order: it is empty or the remainder is below the minimum lot. details: available_liquidity, min_order_size, reason. | Retry later, reduce the order or place a limit order — increasing the order does not help here. |
ORDER_BELOW_MIN_SIZE | 400 | The amount you sent is below the pair minimum lot. details.min_order_size. | Increase amount up to min_order_size. |
ORDER_ABOVE_MAX_SIZE | 400 | The order exceeds the pair maximum. details.max_order_size. | Split it into several orders. |
ORDER_BELOW_MIN_VALUE | 400 | Order value (amount × price) is below the minimum. details.min_order_value. | Increase amount or price. |
QUOTE_AMOUNT_TOO_SMALL | 400 | quote_amount does not cover even the minimum lot of the pair. | Increase quote_amount, see limits in GET /spot/fiat/market-info. |
PRICE_OUT_OF_BAND | 400 | Limit price is too far from the market. details: allowed_price_min, allowed_price_max. | Move the price inside the range from details. |
PRICE_NOT_ON_TICK | 400 | Price is not a multiple of the pair price step. details.price_step. | Round the price to price_step. |
PRICE_REFERENCE_UNAVAILABLE | 400 | No market reference price to validate the limit price. | Retry in a few seconds. |
INSUFFICIENT_BALANCE | 400 | Not enough funds on the trading account. details: currency, required, available, account. | Top up via POST /funding/transfer/to-trading. |
BALANCE_LOCK_FAILED | 400 | The balance changed concurrently with the order. | Re-read the balance and retry with a new Idempotency-Key. |
PAIR_NOT_FOUND | 400 | No such pair. details.pair. | Use a pair from GET /spot/fiat/instruments. |
PAIR_NOT_ACTIVE | 400 | The pair is temporarily closed. details: pair, pair_status. | Wait for status active in GET /spot/fiat/instruments. |
ORDER_NOT_FOUND | 404 | No order with this id. | Check the id in GET /spot/fiat/orders. |
ORDER_ACCESS_DENIED | 403 | The order belongs to another account. | Check the id and the API key. |
ORDER_NOT_CANCELLABLE | 400 | The order is already filled or cancelled. details.order_status. | Read the final state: GET /spot/fiat/order/{id}. |
INVALID_ORDER_REQUEST | 400 | Invalid request body. details.field. | Check the fields against the documentation. |
STOCKBOOK_ERROR | 4xx / 5xx | A rejection not yet mapped to a code. details: upstream_status, reason_text, upstream_body (legacy field, kept for compatibility). | Retry later; if it repeats, send reason_text to support. |
STOCKBOOK_UNAVAILABLE | 502 | The trading system is unreachable (network/timeout). | Retry in a few seconds with the same Idempotency-Key. |
Account
User profile and crypto wallet management. View balances, create wallets and get deposit addresses.
On first connection, start with GET /account/profile to verify authentication works. Then get wallet list via GET /account/wallets. If you need a wallet that does not exist — create it via POST /account/wallets (this also returns a deposit address).
/api/v1/integration/account/walletsReturns all user wallets with balances and deposit addresses. Wallets not yet created will not appear in the list.
{
"success": true,
"data": [
{
"symbol": "USDT",
"is_active": true,
"balance": "1250.00",
"available": "1250.00",
"pending_deposit": "0.00",
"pending_withdraw": "0.00",
"balance_usdt": "1250.00",
"balance_source": "ledger",
"addresses": [
{ "network": "TRX", "address": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE" },
{ "network": "ETH", "address": "0x742d35Cc6634C0532925a3b..." }
]
},
{
"symbol": "BTC",
"is_active": true,
"balance": "0.05432100",
"available": "0.05432100",
"pending_deposit": "0.00",
"pending_withdraw": "0.00",
"balance_usdt": "2150.50",
"balance_source": "ledger",
"addresses": []
}
],
"ts": 1706000000
}/api/v1/integration/account/wallets/{symbol}Returns balance and addresses for specific currency. Returns 404 if wallet not found.
{
"success": true,
"data": {
"symbol": "USDT",
"is_active": true,
"balance": "1250.00",
"available": "1250.00",
"pending_deposit": "0.00",
"pending_withdraw": "0.00",
"balance_usdt": "1250.00",
"balance_source": "ledger",
"addresses": [
{ "network": "TRX", "address": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE" }
]
},
"ts": 1706000000
}/api/v1/integration/account/walletsCreates a wallet for specified currency and network. If wallet already exists — returns existing one with deposit address. Use this to get a deposit address.
{
"symbol": "USDT",
"network": "TRX"
}{
"success": true,
"data": {
"symbol": "USDT",
"available": "0.00",
"addresses": [
{ "network": "TRX", "address": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE" }
]
},
"ts": 1706000000
}/api/v1/integration/account/profileReturns profile info: ID, email, KYC status, tariff.
{
"success": true,
"data": {
"id": 12345,
"username": "john_doe",
"email": "john@example.com",
"email_verified": null,
"kyc_status": "verified",
"tariff": null,
"sso_client_id": "wallet-web",
"created_at": "1700000000"
},
"ts": 1706000000
}Deposits
Fund your account with crypto and fiat. Crypto — via deposit addresses, fiat — via bank transfer or SBP.
- Get deposit address: GET /funding/deposit/crypto/address
- Send crypto to the received address from an external wallet
- Track status via GET /transactions
- Get available methods: GET /funding/deposit/fiat/methods — use code (SBER, TINKOFF, …)
- Create request: POST /funding/deposit/fiat with currency, amount and payment_provider_alias_code from the methods list
- Pay via invoice_public_url, requisites_qr_code or requisites — they are the same SBP link. If all three are still null, poll GET /funding/deposit/fiat/{transaction_id}
For crypto deposits, you only need to get the address once — it does not change. Save it on your side and reuse.
/api/v1/integration/funding/deposit/crypto/addressGet deposit address for cryptocurrency. If address not yet generated — it will be created automatically. Params: symbol (required), network (optional).
{
"success": true,
"data": {
"symbol": "USDT",
"addresses": [
{ "network": "TRX", "address": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE" }
]
},
"ts": 1706000000
}/api/v1/integration/funding/deposit/fiat/methodsReturns available fiat deposit methods: banks, SBP, etc. Parameter: currency (default RUB).
{
"success": true,
"data": {
"methods": [
{ "code": "SBER", "name": "Сбербанк", "type": "sbp" },
{ "code": "TINKOFF", "name": "Т-Банк", "type": "sbp" },
{ "code": "ALFA", "name": "Альфа-Банк", "type": "sbp" }
]
},
"ts": 1706000000
}/api/v1/integration/funding/deposit/fiatCreates a fiat deposit. Take payment_provider_alias_code from GET /funding/deposit/fiat/methods (SBER, TINKOFF, ALFA, …). Do not send internal channel codes. invoice_public_url, requisites_qr_code and requisites are the same SBP payment link. If all three are still null, poll GET /funding/deposit/fiat/{transaction_id}. amount is the requested sum; credited_amount is the net credit after fee; fee is the commission. Both are null until the fact is known.
{
"currency": "RUB",
"amount": "10000",
"payment_provider_alias_code": "SBER"
}{
"success": true,
"data": {
"transaction_id": "7ad2bfd0-673f-455a-b75b-95a949a7476a",
"status": "processing",
"currency": "RUB",
"amount": "10000",
"credited_amount": null,
"fee": null,
"payment_provider_alias_code": "SBER",
"requisites": "https://qr.nspk.ru/...",
"requisites_qr_code": "https://qr.nspk.ru/...",
"invoice_public_url": "https://qr.nspk.ru/...",
"expires_at": "2026-08-14T12:00:00Z",
"created_at": "2026-08-14T11:00:00Z"
},
"ts": 1706000000
}/api/v1/integration/funding/deposit/fiat/{transaction_id}Fiat deposit status. transaction_id is the UUID from POST /funding/deposit/fiat. invoice_public_url, requisites_qr_code and requisites are the same SBP link. status: processing | success | failed. The terminal outcome also arrives as deposit.confirmed / deposit.failed webhooks (data.kind=fiat). This is not invoice.paid: Invoice V2 is a separate product. amount is the requested sum; credited_amount is the net credit after fee; fee is the commission. Reconcile against credited_amount, not amount.
{
"success": true,
"data": {
"transaction_id": "7ad2bfd0-673f-455a-b75b-95a949a7476a",
"status": "success",
"currency": "RUB",
"amount": "6500",
"credited_amount": "6383",
"fee": "117",
"payment_provider_alias_code": "SBER",
"requisites": "https://qr.nspk.ru/...",
"requisites_qr_code": "https://qr.nspk.ru/...",
"invoice_public_url": "https://qr.nspk.ru/...",
"expires_at": "2026-08-14T12:00:00Z",
"created_at": "2026-08-14T11:00:00Z"
},
"ts": 1706000000
}Withdrawals
Withdraw funds from your funding account to external addresses and via SBP. Crypto is sent to a blockchain address; fiat (RUB) is paid by phone number via SBP to the selected bank.
Withdrawal deducts from your funding account. Make sure balance is sufficient (GET /account/wallets). Larger withdrawals may require KYC verification.
/api/v1/integration/funding/withdraw/cryptoCreates crypto withdrawal to external address. Funds are debited from funding account. Symbol, network, address and amount are required.
{
"symbol": "USDT",
"amount": "100",
"address": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
"bch_code": "TRX",
"idempotency_key": "unique-withdraw-key-123"
}{
"success": true,
"data": {
"transaction_id": "...",
"symbol": "USDT",
"amount": "100",
"fee": "1.0",
"total": "101.0",
"address": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
"network": "TRX",
"status": "processing"
},
"ts": 1706000000
}amount is what the recipient gets. fee is the full commission charged on top of amount. total is the debit from your balance (amount + fee). Reconcile the balance against total, not amount. withdraw_service_fee from the currencies directory is the network base rate; the actual quote is in this POST response. Send idempotency_key: a repeat with the same key within 24 h returns the first request (even if it is already failed) — a new attempt needs a new key. Without a key, a dropped response plus retry risks a second payout. Terminal outcome — withdrawal.processing / .completed / .failed webhooks (kind=crypto).
Amount precision and network limits
Every network accepts its own number of decimals. USDT on TRX allows 6, the same USDT on BSC allows 8. Extra decimals are not an error: the amount is truncated down to the network precision and the remainder stays on your balance. The amount field in the response holds the actually accepted value — reconcile against it, not against what you sent.
Precision, minimum, maximum and withdrawal availability for every coin+network pair come from the GET /api/v1/integration/market/currencies directory — fields withdraw_amount_decimals, min_withdraw_amount, max_withdraw_amount, withdraw_service_fee, is_withdraw_active.
/api/v1/integration/funding/withdraw/fiat/banksBank directory for SBP withdraw. Returns public bank_code values (TINKOFF, SBER, …) to pass into POST /withdraw/fiat. Internal Pay provider codes are not exposed.
{
"success": true,
"data": {
"currency": "RUB",
"banks": [
{ "bank_code": "TINKOFF", "name": "T-Pay" },
{ "bank_code": "SBER", "name": "S-Pay" },
{ "bank_code": "ALFA", "name": "A-Pay" }
]
},
"ts": 1706000000
}/api/v1/integration/funding/withdraw/fiatCreates a RUB SBP withdrawal to the recipient phone. Payout channel (provider) is resolved server-side from the profile assignment — do not send it. Ledger balance is checked synchronously (amount + fee).
{
"currency": "RUB",
"amount": "500",
"recipient": "79992122496",
"bank_code": "TINKOFF",
"idempotency_key": "unique-fiat-withdraw-key-456"
}| Field | Type | Description |
|---|---|---|
amount | string | Amount to receive (without fee), string |
recipient | string | SBP phone in 7XXXXXXXXXX format |
bank_code | string | Bank code from GET /withdraw/fiat/banks |
currency | string? | RUB only (default RUB) |
idempotency_key | string? | Idempotency key, 24 h. After failed the same key returns the first request and does not create a new one — use a new key. |
{
"success": true,
"data": {
"transaction_id": "68182a10-9f8a-4c06-a1bf-4d5d66841284",
"currency": "RUB",
"amount": "500.00",
"amount_fact": null,
"fee": "15.00",
"total": "515.00",
"status": "processing",
"recipient": "7999****96",
"bank_code": "TINKOFF"
},
"ts": 1706000000
}amount is what the recipient gets (without fee). fee is the commission. total is the debit from your balance (amount + fee). The balance is checked against total before the request is created. Send idempotency_key: TTL 24 h; after failed the same key does not create a new payout. Terminal outcome — withdrawal.completed / withdrawal.failed webhooks (kind=fiat); there is no processing event for SBP.
/api/v1/integration/funding/withdraw/fiat/{transaction_id}Fiat withdraw status by transaction_id from POST /withdraw/fiat. The terminal outcome also arrives as withdrawal.completed / withdrawal.failed webhooks (data.kind=fiat). There is no withdrawal.processing event for SBP — while the request is in flight, poll this endpoint (status: processing | success | failed). amount is to the recipient; fee is the commission; total is the debit from your balance; amount_fact is the actual payout once completed. The SBP bank reference is not in the response.
{
"success": true,
"data": {
"transaction_id": "68182a10-9f8a-4c06-a1bf-4d5d66841284",
"status": "success",
"currency": "RUB",
"amount": "500.00",
"amount_fact": "500.00",
"fee": "15.00",
"total": "515.00",
"bank_code": "TINKOFF",
"recipient": "7999****96",
"created_at": 1706000000,
"updated_at": 1706000120,
"error": null
},
"ts": 1706000120
}Transfers
Three types of transfers: internal (to another user), to trading account and back. Internal transfers are instant and free.
If you want to trade on fiat spot (USDT/RUB orderbook), funds must be on the trading account. Use /funding/transfer/to-trading before trading and /funding/transfer/from-trading to return funds after trading.
/api/v1/integration/funding/transfer/internalTransfer funds to another user by username or ID. Funds are debited from sender funding account.
The endpoint accepts crypto symbols only; RUB cannot be sent via API. A RUB transfer is made by the user in the wallet interface — you can hand them a pre-filled link (see “Pre-filled transfer link”). Incoming funds are observable the same way for both currencies: the transfer.received webhook and a record in GET /integration/transactions.
{
"symbol": "USDT",
"amount": "100",
"to_username": "john_doe",
"to_user_id": null,
"comment": "Payment for services",
"idempotency_key": "unique-transfer-key-789"
}{
"success": true,
"data": {
"task_id": "celery-task-id-...",
"symbol": "USDT",
"amount": "100",
"to": "john_doe",
"status": "processing"
},
"ts": 1706000000
}/api/v1/integration/funding/transfer/to-tradingTransfer from funding to trading account. Required for fiat spot trading. Accepts currency symbol (USDT or RUB) and amount.
{
"currency": "USDT",
"amount": "1000"
}{
"success": true,
"data": {
"status": "ok"
},
"ts": 1706000000
}/api/v1/integration/funding/transfer/from-tradingTransfer from trading back to funding account. Accepts currency symbol (USDT or RUB) and amount.
{
"currency": "USDT",
"amount": "500"
}{
"success": true,
"data": {
"status": "ok"
},
"ts": 1706000000
}Crypto Spot
Crypto pair trading. Supports market and limit orders. Operations are executed from the funding account.
- Check tickers: GET /spot/crypto/tickers — current prices for all pairs (public, no key)
- Get pair details: GET /spot/crypto/market-info?from_symbol=BTC&to_symbol=USDT (public, no key)
- Check available trading pairs: GET /spot/crypto/pairs (requires API key)
- Create order: POST /spot/crypto/order with type="market" (instant) or type="limit" (at your price)
- Track status: GET /spot/crypto/orders
market— instant execution at current market price. Specify from_amount OR to_amount. Ideal for quick exchange.limit— order at a specified price. Executed when market price reaches the target. Requires from_amount and order_price. Good for DCA strategies and buying dips.
Market — when speed matters (exchange right now). Limit — when price matters (want to buy cheaper). Limit orders can be cancelled before execution via DELETE /spot/crypto/order/{order_id}.
Public Data (no API key)
/api/v1/integration/spot/crypto/tickersCurrent tickers for all crypto pairs — prices, bid/ask, 24h volumes. Cached and updated every ~3 minutes. No API key required.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
symbol | string | Filter by base symbol: BTC, ETH (optional) |
{
"success": true,
"data": [
{
"pair": "BTC-USDT",
"base": "BTC",
"quote": "USDT",
"last_price": "97450.50",
"bid_price": "97448.00",
"ask_price": "97453.00",
"volume_24h": "12450000",
"high_24h": "98100.00",
"low_24h": "96800.00",
"price_change_24h": "0.0124"
}
],
"ts": 1706000000
}/api/v1/integration/spot/crypto/market-infoDetailed info for a specific trading pair: current rate, min/max amounts, trading filters. No API key required.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
from_symbol * | string | Source currency: BTC |
to_symbol * | string | Target currency: USDT |
{
"success": true,
"data": {
"symbol": "BTCUSDT",
"priceTo": "71114.201",
"priceOut": "69706.098",
"price": "70410.2",
"mainSymbol": "BTC",
"minorSymbol": "USDT",
"quote_asset_precision": 7,
"base_asset_precision": 6,
"step": "0.000001",
"minValue": "0.000001",
"min_value_minor": "5",
"maxValue": "230",
"max_value_minor": "8000000",
"limit_price_step": "0.005",
"price_step": "0.1"
},
"ts": 1706000000
}Trading (API key required)
/api/v1/integration/spot/crypto/pairsList of available trading pairs with limits. Requires API key with can_spot_crypto_read permission.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
symbol | string | Filter by symbol (optional) |
{
"success": true,
"data": ["NAKA", "SIGN", "ARKM", "BTC", "ETH", "SOL"],
"ts": 1706000000
}/api/v1/integration/spot/crypto/pair-infoPair info: current rate, min/max exchange amounts.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
from_symbol | string | Source currency (required) |
to_symbol | string | Target currency (required) |
{
"success": true,
"data": {
"symbol": "BTCUSDT",
"priceTo": "71114.201",
"priceOut": "69706.098",
"price": "70410.2",
"mainSymbol": "BTC",
"minorSymbol": "USDT",
"quote_asset_precision": 7,
"base_asset_precision": 6,
"step": "0.000001",
"minValue": "0.000001",
"min_value_minor": "5",
"maxValue": "230",
"max_value_minor": "8000000",
"limit_price_step": "0.005",
"price_step": "0.1"
},
"ts": 1706000000
}/api/v1/integration/spot/crypto/orderMarket Order
Instant exchange at current market price. Specify from_amount (how much to spend) OR to_amount (how much to receive).
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
from_symbol | string | yes | Source currency (e.g. USDT) |
to_symbol | string | yes | Target currency (e.g. BTC) |
from_amount | string | yes* | Amount in source currency |
to_amount | string | yes* | Amount in target currency |
type | string | no | "market" (default) |
* Specify from_amount OR to_amount, but not both.
{
"from_symbol": "USDT",
"to_symbol": "BTC",
"from_amount": "100",
"type": "market"
}{
"success": true,
"data": {
"order_id": "550e8400-e29b-41d4-a716-446655440000",
"from_symbol": "USDT",
"to_symbol": "BTC",
"from_amount": "100",
"to_amount": "0.00231",
"type": "market",
"order_price": null,
"status": "processing"
},
"ts": 1706000000
}/api/v1/integration/spot/crypto/orderLimit Order
Order at a specified price. Will be executed when market price reaches the target level. Requires from_amount and order_price.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
from_symbol | string | yes | Source currency (e.g. USDT) |
to_symbol | string | yes | Target currency (e.g. BTC) |
from_amount | string | yes | Amount in source currency |
type | string | yes | "limit" |
order_price | string | yes | Desired execution price (e.g. "42000.00") |
{
"from_symbol": "USDT",
"to_symbol": "BTC",
"from_amount": "500",
"type": "limit",
"order_price": "42000.00"
}{
"success": true,
"data": {
"order_id": "550e8400-e29b-41d4-a716-446655440001",
"from_symbol": "USDT",
"to_symbol": "BTC",
"from_amount": "500",
"to_amount": null,
"type": "limit",
"order_price": "42000.00",
"status": "processing"
},
"ts": 1706000000
}/api/v1/integration/spot/crypto/order/{order_id}Cancel a limit order. Only works for limit orders that have not been executed yet.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
order_id | string | Order ID from POST /order response |
{
"success": true,
"data": {
"cancelled": true,
"order_id": "550e8400-e29b-41d4-a716-446655440001",
"status": "cancelled"
},
"ts": 1706000000
}// Order not found
{ "success": false, "error": { "code": "ORDER_NOT_FOUND", "message": "Order not found" } }
// Order is not a limit order
{ "success": false, "error": { "code": "CANCEL_FAILED", "message": "Not a limit order" } }
// Order already cancelled
{ "success": false, "error": { "code": "CANCEL_FAILED", "message": "Order already cancelled" } }/api/v1/integration/spot/crypto/ordersCrypto exchange order history with pagination. Includes market and limit orders.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 50 | Number of records (1-100) |
page | integer | 1 | Page number |
{
"success": true,
"data": [
{
"order_id": "...",
"from_symbol": "USDT", "to_symbol": "BTC",
"from_amount": "100", "to_amount": "0.00231",
"type": "market",
"status": "completed", "created_at": "2024-01-15T14:30:00Z"
},
{
"order_id": "...",
"from_symbol": "USDT", "to_symbol": "ETH",
"from_amount": "500", "to_amount": null,
"type": "limit", "order_price": "2100.00",
"status": "processing", "created_at": "2024-01-15T15:00:00Z"
}
],
"ts": 1706000000
}Fiat Spot
Full USDT/RUB orderbook with market and limit orders. Powered by the in-house AlfaBit trading engine. Unlike Crypto Spot, here you see a real orderbook and can control the price.
Trading runs on a separate trading balance. Before your first trade, transfer funds:
POST /api/v1/integration/funding/transfer/to-trading
{ "currency": "USDT", "amount": "1000" }- Transfer RUB to trading account: POST /funding/transfer/to-trading
- Check balance: GET /spot/fiat/balance
- Study orderbook and current price: GET /spot/fiat/orderbook and GET /spot/fiat/stats
- Accept an order: POST /spot/fiat/order — you get an id immediately (usually status=new). Do not wait for filled on this response.
- Track fill: GET /spot/fiat/order/{id} / GET /spot/fiat/orders or webhook order.filled
- Return USDT to main account: POST /funding/transfer/from-trading
Public Data (no API key)
/api/v1/integration/spot/fiat/instrumentsList of trading instruments (pairs) and their statuses.
{
"success": true,
"data": [
{ "trading_pair": "USDT/RUB", "status": "active", "base": "USDT", "quote": "RUB" }
],
"ts": 1706000000
}/api/v1/integration/spot/fiat/balanceTrading account balances: USDT and RUB (available, locked).
{
"success": true,
"data": {
"USDT": { "available": "5000.00", "locked": "100.00" },
"RUB": { "available": "150000.00", "locked": "0.00" }
},
"ts": 1706000000
}/api/v1/integration/spot/fiat/orderbook?pair=USDT/RUB&depth=20Orderbook: bids and asks. Parameter depth — orderbook depth (default 20).
{
"success": true,
"data": {
"bids": [
{ "price": "92.45", "amount": "5000" },
{ "price": "92.40", "amount": "12000" }
],
"asks": [
{ "price": "92.55", "amount": "3000" },
{ "price": "92.60", "amount": "8000" }
]
},
"ts": 1706000000
}/api/v1/integration/spot/fiat/trades?pair=USDT/RUB&limit=50Public trades feed. No API key required.
{
"success": true,
"data": [
{ "id": "...", "price": "92.50", "amount": "100", "side": "buy", "time": "2024-01-15T14:30:00Z" }
],
"ts": 1706000000
}/api/v1/integration/spot/fiat/stats?pair=USDT/RUB24h statistics: volume, high, low, last price.
{
"success": true,
"data": {
"pair": "USDT/RUB", "last": "92.50", "high": "93.10",
"low": "91.80", "volume": "1250000", "change": "+0.5%"
},
"ts": 1706000000
}Trading (API key required)
/api/v1/integration/spot/fiat/orderAccept an order. Response is the same JSON with an id (usually status=new). Execution is async: do not expect filled on this POST. Use GET /spot/fiat/order/{id}, GET /spot/fiat/orders, or webhook order.filled. 4xx (balance, book, limits) is still synchronous — no order is created.
Market Order
{
"pair": "USDT/RUB",
"side": "buy",
"type": "market",
"amount": "100"
}Market Order — by quote amount (quote_amount)
If you start with rubles and do not know how much USDT you will get — pass quote_amount (the amount of quote currency you want to spend) instead of amount. The engine picks the base amount from the current orderbook without exceeding your budget. Market buy only. amount and quote_amount are mutually exclusive. Any unused budget is refunded; in a fast market the actually filled amount may differ slightly (as with any market order).
{
"pair": "USDT/RUB",
"side": "buy",
"type": "market",
"quote_amount": "10000"
}Limit Order
{
"pair": "USDT/RUB",
"side": "sell",
"type": "limit",
"amount": "500",
"price": "93.00"
}{
"success": true,
"data": {
"order_id": "ord_limit_xyz789",
"pair": "USDT/RUB",
"side": "sell",
"type": "limit",
"amount": "500",
"price": "93.00",
"status": "open"
},
"ts": 1706000000
}/api/v1/integration/spot/fiat/order/{order_id}Cancel limit order by ID. Market orders cannot be cancelled — they execute instantly.
/api/v1/integration/spot/fiat/ordersYour open and filled orders on fiat spot.
{
"success": true,
"data": [
{ "id": "...", "pair": "USDT/RUB", "side": "buy", "type": "limit",
"price": "92.00", "amount": "100", "filled": "0", "status": "open" }
],
"ts": 1706000000
}/api/v1/integration/spot/fiat/my-tradesYour executed trades on fiat spot.
{
"success": true,
"data": [
{ "id": "...", "pair": "USDT/RUB", "side": "buy",
"price": "92.45", "amount": "100", "fee": "0.1", "time": "2024-01-15T14:30:00Z" }
],
"ts": 1706000000
}More public data
/api/v1/integration/spot/fiat/tickersAggregated tickers for all active pairs: price, bid/ask, 24h volume, change. Counterpart of /spot/crypto/tickers for fiat pairs.
/api/v1/integration/spot/fiat/market-info?pair=USDT/RUBDetailed info for one pair: current price, spread, min/max amounts, price/quantity steps, precision.
/api/v1/integration/spot/fiat/instruments/{pair}Details of one pair (status, base/quote, limits).
/api/v1/integration/spot/fiat/currenciesTrading account currency directory (USDT, RUB, etc.) with symbol and precision.
More private endpoints (API key required)
/api/v1/integration/spot/fiat/balance/{symbol}Trading balance for a single currency (e.g. USDT or RUB).
/api/v1/integration/spot/fiat/order/{order_id}Single order details by ID: status, fills, average price, fees.
{
"success": true,
"data": {
"id": "3f1c9a2e-8b7d-4e6a-9c11-2a5f4d7e0b9c",
"order_number": 10432,
"user_id": "b2c9e7a1-4f83-4d2a-9e6b-1c0f5a8d3e21",
"side": "buy",
"type": "market",
"price": null,
"amount": "100",
"filled": "100",
"status": "filled",
"created_at": "2026-07-24T14:28:00.123456",
"trading_pair": "USDT/RUB",
"execution_message": "Order executed successfully",
"avg_execution_price": "92.35",
"total_fee": "9.235",
"user_input_price": null,
"user_input_price_total": "10000",
"user_input_amount": null,
"locked_quote_amount": "10000"
},
"ts": 1753363680
}Numeric fields are returned as strings (to preserve precision). price and avg_execution_price are null until set / until the order is executed.
id— order ID (UUID).order_number— human-readable order number.user_id— internal user ID.side— buy or sell.type— market or limit.price— limit order price; null for market.amount— order amount in base currency (USDT).filled— fill — how much base currency is already executed (in amount units).status— new / partially_filled / filled / cancelled.created_at— creation time (ISO-8601).trading_pair— trading pair, e.g. USDT/RUB.execution_message— execution message (relevant for market); may be null.avg_execution_price— average execution price; null until there are trades.total_fee— total fee for the order.user_input_price,user_input_price_total,user_input_amount— original user input (display only). For a market buy with quote_amount, user_input_price_total holds the requested budget.locked_quote_amount— amount of quote currency (RUB) locked for the BUY order.
/api/v1/integration/spot/fiat/trading/operationsHistory of deposit/withdraw operations on the trading account, paginated. Filters: currency_id, operation_type (deposit/withdraw), status (pending/completed/failed).
/api/v1/integration/spot/fiat/trading/settings/{symbol}Deposit/withdraw limits and fee_percent for a specific currency.
Idempotency
POST /spot/fiat/order and DELETE /spot/fiat/order/{id} support Idempotency-Key header (1-128 chars: letters/digits/-/_, UUID recommended). Repeat POST with the same key does not create a second order (TTL 24h). For create, retry returns the current order from the engine (not the frozen accept snapshot) — safe after a dropped connection to learn status/filled.
X-API-Key: pk_live_...
X-API-Signature: ...
X-API-Timestamp: ...
Idempotency-Key: 4b8c8a1e-3f2c-4f3a-9c0d-2b3a4b5c6d7eWebSockets
Real-time channel for integrators. Single WS server for all topics: Spot Fiat trading events, balance updates, invoice status, converter updates.
wss://alfabit.org/wallet-web/ws/wsAuthentication (auth_api_key)
WS uses a simplified signature HMAC-SHA256(secret, timestamp + api_key) — no method/path/body. Allowed timestamp drift is ±300 sec.
const ws = new WebSocket('wss://alfabit.org/wallet-web/ws/ws');
// 1) handshake
ws.send(JSON.stringify({
action: 'auth_api_key',
api_key: 'pk_live_xxxxxxxxxxxx',
timestamp: '1714210000',
signature: '<hmac-sha256(secret, timestamp + api_key) hex>',
}));
// 2) subscribe
ws.send(JSON.stringify({
action: 'subscribe',
topics: [
'spot.fiat.balance',
'spot.fiat.order',
'spot.fiat.orderbook.USDT/RUB'
]
}));import hashlib, hmac, time
ts = str(int(time.time()))
sig = hmac.new(secret_key.encode(), (ts + api_key).encode(), hashlib.sha256).hexdigest()Public topics (no auth)
Orderbook, trades, stats, charts — industry standard.
spot.fiat.orderbook.{pair}— Orderbook snapshot (bids/asks) for the pair.spot.fiat.trades.{pair}— Trade tape for the pair.spot.fiat.stats.{pair}— 24h stats for the pair.spot.fiat.chart.{symbol}.{resolution}— TradingView candles for pair and interval (1, 5, 15, 30, 60, 240, D, W, M).
Private topics (require can_spot_fiat_read)
Your own orders and trading balance — filtered by profile_id, only visible to the API key owner.
spot.fiat.order— Status changes of your orders (created, filled, partially_filled, cancelled).spot.fiat.balance— Trading balance changes.
Reconnect
On disconnect reconnect with exponential backoff: 1s → 2s → 4s → ... up to 30s. After reconnect repeat auth_api_key and subscribe.
Converter
Fast conversion with fixed rate. Supports crypto-crypto and crypto-fiat exchange. Unlike Spot, the converter guarantees the price — you get exactly what the quote showed.
The quote locks the rate for a limited time (see expires_in_seconds in response). Call /execute with the received quote_id within this time. If you miss the window — request a new quote.
Converter — for one-off operations with guaranteed price (send a client exactly $100 in ETH). Spot — for regular trading and strategies (DCA, limit orders, large volumes).
Crypto Converter
Rates and symbols are public. Exchange requires API key.
/api/v1/integration/converter/crypto/symbolsList of cryptocurrencies available for conversion. No API key required.
/api/v1/integration/converter/crypto/rate?from=BTC&to=ETHFinal exchange rate, limits and quote TTL. No API key required.
{
"success": true,
"data": {
"from_symbol": "BTC",
"to_symbol": "ETH",
"rate": "16.468",
"min_amount_usdt": "10",
"max_amount_usdt": "50000",
"expires_in_seconds": 30
},
"ts": 1706000000
}/api/v1/integration/converter/crypto/estimateGet a fixed quote. Returns quote_id for /execute. Quote lifetime is specified in the expires_in_seconds field.
{
"from_symbol": "BTC",
"to_symbol": "ETH",
"from_amount": "0.5"
}{
"success": true,
"data": {
"quote_id": "qt_abc123def456",
"from_symbol": "BTC",
"to_symbol": "ETH",
"from_amount": "0.5",
"to_amount": "8.234",
"rate": "16.468",
"expires_in_seconds": 30
},
"ts": 1706000000
}/api/v1/integration/converter/crypto/executeExecute conversion by previously obtained quote. Returns error if quote expired. Optional idempotency_key (valid 24h): a repeat request with the same key will not create a second conversion — the same result is returned (retry-safe). An identical request still in progress returns HTTP 409.
{
"quote_id": "qt_abc123def456",
"idempotency_key": "your-unique-key-123"
}{
"success": true,
"data": {
"uid": "conv_op_abc123",
"from_symbol": "BTC",
"to_symbol": "USDT",
"from_amount": "0.01",
"to_amount": "704.10",
"rate": "70410.2",
"status": "completed"
},
"ts": 1706000000
}Fiat Converter
Convert crypto to fiat and vice versa (e.g. USDT → RUB). Rates are public, exchange requires API key.
/api/v1/integration/converter/fiat/currenciesFiat currencies currently open for conversion. No API key required. Read it before requesting a rate: the set changes, and a request with a code missing from this response will be rejected.
{
"success": true,
"data": [
{
"code": "RUB",
"name": "Российский рубль",
"symbol": "₽",
"decimals": 2,
"min_amount": "100.00",
"max_amount": "250000.00"
}
],
"ts": 1706000000
}decimals— amount precision for this currency. Round by it, not by your own format.min_amount/max_amount— per-conversion limits in fiat units. null means no limit is set.
/api/v1/integration/converter/fiat/crypto-symbolsA flat array of crypto symbols allowed against fiat. No API key required. The list is long and changes — do not hardcode it, fetch and cache it on your side.
{
"success": true,
"data": ["0G", "1INCH", "AAVE", "ADA", "BTC", "ETH", "USDT", "..."],
"ts": 1706000000
}/api/v1/integration/converter/fiat/rate?crypto=USDT&fiat=RUB&direction=sellCurrent crypto/fiat rate. Params: crypto (symbol), fiat (code), direction (buy/sell).
{
"success": true,
"data": {
"crypto": "USDT", "fiat": "RUB", "direction": "sell",
"rate": "92.50", "min_amount": "10", "max_amount": "100000"
},
"ts": 1706000000
}/api/v1/integration/converter/fiat/estimateGet quote for fiat conversion.
{
"crypto_symbol": "USDT",
"fiat_code": "RUB",
"direction": "sell",
"amount": "100"
}{
"success": true,
"data": {
"quote_id": "qt_fiat_abc123",
"crypto_symbol": "USDT",
"fiat_code": "RUB",
"direction": "sell",
"crypto_amount": "100",
"fiat_amount": "9250.00",
"rate": "92.50",
"fee": "1.00",
"expires_at": "2024-01-20T12:35:00Z"
},
"ts": 1706000000
}/api/v1/integration/converter/fiat/executeExecute fiat conversion by quote. Same fields as /fiat/estimate (crypto_symbol, fiat_code, direction), amount in from_amount, plus quote_id from the estimate response. quote_id alone is not enough — missing fields return 422. There is no separate idempotency_key: a repeat with the same quote_id returns the already created operation. A direct-exchange quote lives 30 seconds. Status — GET /converter/operations; success also arrives as conversion.completed.
{
"crypto_symbol": "USDT",
"fiat_code": "RUB",
"direction": "sell",
"from_amount": "100",
"quote_id": "qt_fiat_xyz789"
}{
"success": true,
"data": {
"uid": "fiat_conv_xyz789",
"crypto_symbol": "USDT",
"fiat_code": "RUB",
"direction": "sell",
"crypto_amount": "100",
"fiat_amount": "9250.00",
"status": "completed"
},
"ts": 1706000000
}/api/v1/integration/converter/operations?limit=20All conversion history (crypto + fiat). Parameter type: crypto or fiat.
{
"success": true,
"data": [
{ "uid": "...", "type": "crypto", "from_symbol": "BTC", "to_symbol": "ETH",
"from_amount": "0.5", "to_amount": "8.234", "status": "completed" }
],
"ts": 1706000000
}Virtual Cards
Issue and manage virtual Visa/Mastercard bank cards. Pay for subscriptions, advertising and purchases using cryptocurrency.
SHOPPING — for online purchases. ADVERTISING — for ad platforms. Types and available options depend on your plan.
- Check conditions: GET /cards/settings (fees, limits) and GET /cards/meta (regions, payment systems)
- Get rate: GET /cards/rate (USDT → USD for cost calculation)
- Issue card: POST /cards (amount + payment_method: balance_usdt | onchain_usdt | rub_sbp). The ready card appears in GET /cards (async for balance_usdt; card.transaction webhook is also sent)
- Top up card: POST /cards/{card_id}/topup — funds are converted to USD
- Use card for payments and track transactions: GET /cards/{card_id}/transactions
/api/v1/integration/cards/settingsCard settings: types, issuance fee (buy_fee), top-up fee (top_up_fee), required top-up.
/api/v1/integration/cards/metaMeta info: available currencies, regions, payment systems (Visa/Mastercard), Apple Pay.
/api/v1/integration/cards/rateCurrent USDT/RUB rate for issuance and top-up cost calculation.
/api/v1/integration/cardsAll user cards: type, status, balance, payment system.
/api/v1/integration/cardsIssue a virtual card (book + payment). amount is required. Payment via payment_method: balance_usdt (debit from balance), onchain_usdt (USDT transfer, network required) or rub_sbp (SBP). See current region / payment_system / currency in GET /cards/meta.
{
"amount": "10",
"payment_method": "balance_usdt",
"source_symbol": "USDT",
"card_type": "SHOPPING",
"is_apple_pay_available": false
}// rub_sbp — в ответе qr/ссылка на оплату
{ "amount": "10", "payment_method": "rub_sbp", "card_type": "SHOPPING" }
// onchain_usdt — в ответе адрес депозита
{ "amount": "10", "payment_method": "onchain_usdt", "onchain_network": "trc20", "card_type": "SHOPPING" }| Parameter | Description |
|---|---|
amount | Required. Initial card top-up amount (USD). |
payment_method | balance_usdt, onchain_usdt, rub_sbp |
source_symbol | Debit asset for balance_usdt (default USDT). |
onchain_network | trc20 | erc20 — required for onchain_usdt. |
card_type | SHOPPING or ADVERTISING |
region / payment_system | Optional. Current values from GET /cards/meta (now HK / mastercard). Auto-selected if omitted. |
idempotency_key | Optional (uuid4). Protects against duplicate issuance. |
For balance_usdt issuance is async: the response is an order; get the ready card via GET /cards and the card.transaction webhook. For rub_sbp/onchain_usdt pay first using the returned details.
/api/v1/integration/cards/{card_id}Card data: number, CVV, expiry date, status (ACTIVE, FROZEN, BLOCKED).
{
"success": true,
"data": {
"id": 123, "card_type": "SHOPPING", "status": "ACTIVE",
"number": "4111 **** **** 1234", "cvc": "***",
"date_expired": "12/27", "payment_system": "VISA",
"balance": "150.00", "region": "WW"
},
"ts": 1706000000
}/api/v1/integration/cards/{card_id}/balanceCurrent card balance in USD.
/api/v1/integration/cards/{card_id}/topupTop up card. Amount converted to USD. Method: crypto, fiat_wallet or sbp.
{
"amount": "50",
"source_symbol": "USDT",
"payment_method": "crypto"
}/api/v1/integration/cards/{card_id}/transactionsCard transactions: purchases, top-ups, refunds.
/api/v1/integration/cards/transactions/allTransactions across ALL user cards. Can filter by card_type.
Gift Cards
Purchase gift cards and certificates of popular services (Steam, PlayStation, Spotify, etc.) with crypto. Certificate code is delivered instantly.
First find the product in the catalog, then check how much it costs in crypto via /estimate, and finally buy via /purchase.
/api/v1/integration/giftcards/catalogAvailable gift cards catalog. Supports category filtering, name search and pagination.
/api/v1/integration/giftcards/categoriesCategory list: Games, Entertainment, Music, Marketplaces, etc.
{
"success": true,
"data": [
{ "id": 1, "title": "Игры", "slug": "games", "is_active": true, "products_count": 42 },
{ "id": 2, "title": "Развлечения", "slug": "entertainment", "is_active": true, "products_count": 18 },
{ "id": 3, "title": "Музыка", "slug": "music", "is_active": true, "products_count": 7 }
],
"ts": 1706000000
}/api/v1/integration/giftcards/estimateCalculate gift card cost in crypto before purchase. Shows final amount including fees.
{
"product_id": 123,
"face_value": 1000,
"symbol": "USDT"
}{
"success": true,
"data": {
"product_id": 123,
"face_value": 1000,
"crypto_amount": "10.85",
"symbol": "USDT",
"fee": "0.15",
"total": "11.00"
},
"ts": 1706000000
}/api/v1/integration/giftcards/purchasePurchase gift card. Funds debited from crypto wallet. Certificate code is available in response (GET /giftcards/orders/{order_id}).
{
"product_id": 123,
"face_value": 1000,
"symbol": "USDT",
"email": "user@example.com"
}{
"success": true,
"data": {
"order_id": "gc_ord_abc123",
"product_id": 123,
"face_value": 1000,
"crypto_amount": "11.00",
"symbol": "USDT",
"status": "processing"
},
"ts": 1706000000
}/api/v1/integration/giftcards/ordersGift card purchase history with certificate codes and statuses.
{
"success": true,
"data": [
{ "id": "...", "product": "Steam 1000₽", "face_value": 1000,
"cost_usdt": "11.50", "status": "completed", "code": "XXXX-YYYY-ZZZZ" }
],
"ts": 1706000000
}Pay Services
A service catalog for resale: mobile, internet, games, utilities and more. You buy the service from your AlfaBit balance (RUB or USDT) and sell it to your customer at your own price. Markup is yours — the API does not calculate or store it.
Catalog and categories are public. Estimate, requisite check, pay and orders need an API key: can_services_read (read) and can_services_pay (debit). The catalog lists only enabled services. If it is empty, contact support to open the storefront.
- GET /services/catalog — show available services to your customer. Build the form from inputs (fallback: required_fields).
- POST /services/estimate — amount to debit from your balance in RUB or USDT (client_amount / client_currency).
- If requires_check=true or payment_type=REQUISITES — POST /services/check-requisite. Requisite: the requisite field or field_values.account / field_values.phone.
- POST /services/pay — debits your balance. You charge your customer yourself. Status: service_payment.* webhook or GET /services/orders/{id}.
/api/v1/integration/services/categoriesCategories: Mobile, Games, Internet, TV, etc. No API key. Filter: country.
/api/v1/integration/services/catalogService catalog with pagination. No API key. Filters: category (alias), category_id, country, search, page, page_size.
/api/v1/integration/services/catalog/{service_id}Service card: inputs, required_fields, payment_type, fixed_payment, requires_check, instruction. No API key.
{
"success": true,
"data": {
"id": 2,
"name": "MegaCom",
"category_alias": "mobile",
"country": "Кыргызская Республика",
"payment_type": "SIMPLIFIED",
"fixed_payment": false,
"requires_check": false,
"inputs": [
{ "name": "account", "required": true, "title": "Номер", "regexp": "^0\\d{9}$" }
],
"required_fields": []
},
"ts": 1706000000
}/api/v1/integration/services/estimateAmount to debit from your balance. debit_currency: RUB or USDT. For fixed_payment you may omit client_amount — the response returns it. Permission: can_services_read.
{
"service_id": 2,
"debit_currency": "RUB",
"client_amount": 500
}{
"success": true,
"data": {
"service_id": 2,
"service_name": "MegaCom",
"client_amount": "512.40",
"client_currency": "RUB",
"quote_expires_at": "1706000300",
"payment_type": "SIMPLIFIED",
"fixed_payment": false,
"requires_check": false
},
"ts": 1706000000
}/api/v1/integration/services/check-requisiteValidate the account number. Required when requires_check=true or payment_type=REQUISITES. Requisite: requisite or field_values.account / phone. Pass agent_transaction_id and check_snapshot from the response into pay. Permission: can_services_read.
{
"service_id": 2,
"debit_currency": "RUB",
"client_amount": "512.40",
"field_values": { "account": "0555123456" }
}{
"success": true,
"data": {
"success": true,
"agent_transaction_id": "agent-tx-uuid",
"client_amount": "512.40",
"client_currency": "RUB",
"requires_identity": false,
"check_snapshot": { "agent_transaction_id": "agent-tx-uuid" }
},
"ts": 1706000000
}/api/v1/integration/services/payPay from your balance (RUB or USDT). Pass quote_expires_at from estimate. If you called check-requisite — also agent_transaction_id and check_snapshot. Webhook: service_payment.*. Permission: can_services_pay.
{
"service_id": 2,
"debit_currency": "RUB",
"client_amount": "512.40",
"field_values": { "account": "0555123456" },
"quote_expires_at": "1706000300"
}{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"service_id": 2,
"status": "CREATED",
"status_for_client": "wait",
"client_amount": "512.40",
"client_currency": "RUB",
"requisite": "0555123456"
},
"ts": 1706000000
}/api/v1/integration/services/ordersPayment history for your profile. Params: limit, offset. Permission: can_services_read. Use status_for_client for the storefront: wait, success, failed.
/api/v1/integration/services/orders/{order_id}Order details. Same DTO as the pay response. Can be polled instead of a webhook.
Invoices — Accept Payments
Full-featured invoicing for business: one-off invoices, permanent QR/templates for cashier flows, donation links, open amount, deferred currency choice by the payer, automatic hedging, on-balance payments inside AlfaBit (no on-chain), embeddable Checkout widget and webhooks. Suitable for e-commerce, freelancers, charities, brick-and-mortar shops and POS cashier flows.
All invoice endpoints live under the prefix /api/v2/integration/invoices/.... v1 endpoints were removed on 2026-04-25 — use v2 only.
API-key permissions: can_invoices_create for create/cancel, can_invoices_read for reading and fetching the URL.
What it can do
- One-off invoice. Fixed amount + TTL up to 30 days. The on-chain invoice is created without a hard amount: whatever arrives is credited. Overpay is the same success / invoice.paid with a larger amount_received. Underpay ≥ the network minimum is also success / invoice.paid with a smaller amount_received, not expired. expired is set only when there is no txid by TTL (no payment). Below the network minimum — failed, no invoice.paid.
- Open amount (donations). Omit amount — the payer enters it on the payment page within the global min/max.
- Deferred currency choice. Omit symbol/bch_code — the payer picks the coin and network on the page. Convenient for marketplaces.
- Permanent QR (template). One QR forever — every payment creates its own child invoice. Suitable for cashier flows, salons, donation pages.
- POS API. A cashier issues a receipt against a template via POST .../payments with idempotency_key and external_payment_id — repeated POST does not duplicate and is echoed back in the webhook.
- Hedging (auto-conversion). Got USDT but want BTC on hand — set is_hedging + hedging_symbol to auto-convert on credit.
- AlfaBit Checkout — pay from balance. AlfaBit users get a "Pay from AlfaBit" button. Internal balance transfer is instant, no on-chain transaction or network fee (payment_source="alfabit_balance", txid=null).
- Payer-paid commission (shift_to_payer). Global platform setting: deduct the fee from the receiver, or grow the amount to pay — the payer pays on top.
- Webhooks. Events invoice.created / paid / expired / cancelled / refunded / hedged (+ template.deactivated). URL is set separately via POST /api/v1/integration/webhooks — not in the invoice create body. HMAC signature; payload matches GET /invoices/{invoice_id}.
- Embed widget. AlfaBit Checkout embeds right into your site via iframe / JS SDK / React / Vue with no buyer redirect.
- Get commissions and limits:
GET /api/v2/integration/invoices/settings. - Create an invoice:
POST /api/v2/integration/invoiceswith the Idempotency-Key header (one UUID per invoice) — get invoice_id and payment_url. On timeout retry with the same key: you get the same invoice, no duplicate. - Send payment_url to the buyer (link/QR/iframe).
- Subscribe to the invoice.paid webhook (see Webhooks).
- Optionally, poll the status:
GET /api/v2/integration/invoices/{invoice_id}.
Use cases
Real patterns covered by the current API. Each scenario is a sequence of endpoints and parameters that work in production.
1. E-commerce — invoice for a specific order
You know the amount and the currency. You want exactly that amount and want to track payment by your order_id.
{
"symbol": "USDT",
"bch_code": "TRX",
"amount": "100",
"currency": "USDT",
"description": "Order #ORD-12345 — iPhone case",
"life_time_minutes": 30,
"payment_policy": "all",
"payer_email": "client@example.com",
"show_receiver_publicly": false
}In the `invoice.paid` webhook you receive the same uid (invoice_id) — match it in your DB against the saved order_id↔invoice_id mapping and close the order.
2. Freelancer — "pay any way" invoice
Amount known (in USDT) but the client prefers TON or BTC. Let the payer pick.
{
"amount": "250",
"currency": "USDT",
"description": "Frontend audit — May 2026"
}Skip `symbol` and `bch_code` — the payment page shows the coin/network picker. Once chosen, both fields appear in GET responses and in the webhook.
3. Donations / tips — open amount
One recipient, many payers, each pays whatever they want. Better implemented as a permanent QR (see scenario 5).
{
"symbol": "USDT",
"bch_code": "TRX",
"description": "Buy me a coffee ☕"
}No `amount` → open-amount invoice. The payer enters it within min/max from GET /settings (in USDT equivalent).
4. Receive USDT — keep BTC (hedging)
{
"symbol": "USDT",
"bch_code": "TRX",
"amount": "1000",
"is_hedging": true,
"hedging_symbol": "BTC"
}After payment USDT is auto-converted to BTC at the converter rate. GET /settings exposes `hedging_commission_percent` — the extra hedging fee.
The actually credited BTC amount is in `hedged_amount` / `hedged_symbol` of GET /api/v2/integration/invoices/{invoice_id}. Conversion runs after payment, so at `invoice.paid` time they are still null; the final result arrives as a separate `invoice.hedged` event.
5. Permanent QR on the cafe wall
One QR — many payments. Each payment becomes a separate child invoice with its own address and TTL.
{
"symbol": "USDT",
"bch_code": "TRX",
"description": "Coffee Shop on Tverskaya 7",
"show_payments_count_publicly": true
}You receive `template_uid` and `payment_url` — print a QR from this URL. The payer creates their own child invoice on the public page (entering the amount), pays — and you receive `invoice.paid` with `parent_template_uid`. `show_payments_count_publicly` shows the count of donors on the public page.
6. POS cashier — issue a receipt against a template
The shop has already created a template (see scenario 5). For every receipt the cashier sends POST .../payments with idempotency_key and external_payment_id (its own receipt ID).
{
"amount": "12.50",
"currency": "USDT",
"description": "Receipt POS-AAA-9876",
"external_payment_id": "POS-AAA-9876",
"life_time_minutes": 15,
"idempotency_key": "pos-aaa-9876-2026-05-15"
}A repeat POST with the same `idempotency_key` returns THE SAME child — no duplicates. The `invoice.paid` webhook carries `external_payment_id` and `parent_template_uid` so the POS can auto-close the receipt.
7. Accept AlfaBit-balance only (closed community)
{
"symbol": "USDT",
"amount": "10",
"payment_policy": "alfabit_only"
}`payment_policy`: "all" (default — both buttons), "alfabit_only" (AlfaBit balance only), "external_only" (blockchain only — "Pay from AlfaBit" button is hidden).
Settings & fees
A single endpoint to fetch current commissions, limits and feature availability. Use it before creating an invoice to validate the amount and show fees to the payer.
/api/v2/integration/invoices/settingsRequired permission: can_invoices_read.
{
"success": true,
"data": {
"is_active": true,
"invoice_commission_percent": "0.5",
"hedging_commission_percent": "0.5",
"shift_commission_to_payer": false,
"is_hedging_enabled": true,
"min_invoice_amount_usdt": "1",
"max_invoice_amount_usdt": "10000000",
"default_lifetime_minutes": 1440,
"min_lifetime_minutes": 5,
"max_lifetime_minutes": 43200
}
}Fields
| Field | Description |
|---|---|
is_active | Invoices v2 globally enabled on the platform. |
invoice_commission_percent | Base commission for accepting a payment, %. |
hedging_commission_percent | Extra commission when is_hedging=true. |
shift_commission_to_payer | true — fee is added on top (payer pays it). false — fee is deducted from the receiver amount. |
is_hedging_enabled | Hedging is available (if false — is_hedging in POST is ignored). |
min_invoice_amount_usdt | Minimum invoice amount in USDT equivalent. For non-USDT — recalculated at the rate. |
max_invoice_amount_usdt | Maximum invoice amount in USDT equivalent. |
default_lifetime_minutes | Default TTL in minutes — if life_time_minutes is omitted. |
min_lifetime_minutes | Minimum allowed TTL. |
max_lifetime_minutes | Maximum allowed TTL (43200 = 30 days). |
Create invoice
/api/v2/integration/invoicesPermission: can_invoices_create. Returns payment_url and (if symbol/bch_code are set) the on-chain payment details.
Send the Idempotency-Key header (it overrides body.idempotency_key): 8–36 chars A–Z a–z 0–9 - _, UUID recommended. One key = one invoice. A repeat with the same key within 24 h returns the already created invoice — safe retry after a timeout or dropped connection. A new key creates a new invoice. If the first request has not been persisted yet, a retry may return HTTP 409 — wait 2–3 seconds and retry with the same key.
Request body
| Field | Type | Description |
|---|---|---|
symbol | string? | Coin (USDT, BTC, ETH...). NULL — the payer picks on the page. |
bch_code | string? | Network (TRX, ETH, TON...). NULL — the payer picks. |
amount | string? | Amount as a decimal string. NULL = open amount. |
currency | string? | Currency of the amount. Defaults to symbol. Supports USDT/USD as unit of account. |
description | string? (≤500) | Visible to the payer on the page and in QR caption. |
life_time_minutes | int (1..43200) | TTL in minutes. Default 60. Max 30 days. |
is_hedging | bool | After credit, auto-convert to hedging_symbol. |
hedging_symbol | string? | Target hedging coin (e.g. BTC). |
payer_email | string? | Payer email (optional). Prefills AlfaBit Checkout; not returned in Integration GET responses. |
payment_policy | enum | all (default) / alfabit_only / external_only. |
show_receiver_publicly | bool (default true) | Whether to show the receiver name/email on the public page. |
idempotency_key | string? (8..36) | Idempotency key in the body. If Idempotency-Key header is also sent, the header wins. |
curl -X POST "https://alfabit.org/api/v2/integration/invoices" \
-H "X-API-Key: pk_live_..." \
-H "X-API-Signature: ..." \
-H "X-API-Timestamp: ..." \
-H "Idempotency-Key: 4b8c8a1e-3f2c-4f3a-9c0d-2b3a4b5c6d7e" \
-H "Content-Type: application/json" \
-d '{"amount":"100","currency":"RUB","payment_policy":"alfabit_only"}'{
"symbol": "USDT",
"bch_code": "TRX",
"amount": "100"
}{
"symbol": "USDT",
"bch_code": "TRX",
"amount": "250.50",
"currency": "USDT",
"description": "Order #ORD-12345",
"life_time_minutes": 180,
"is_hedging": true,
"hedging_symbol": "BTC",
"payment_policy": "all",
"show_receiver_publicly": false,
"idempotency_key": "4b8c8a1e-3f2c-4f3a-9c0d-2b3a4b5c6d7e"
}{
"success": true,
"data": {
"invoice_id": "01J7HZ...",
"status": "wait",
"symbol": "USDT",
"network": "TRX",
"address": "TXa1B2...",
"memo_tag": null,
"amount_requested": "250.50",
"amount_requested_currency": "USDT",
"amount_to_pay": "250.50",
"exchange_rate": null,
"commission": {
"commission_percent": "0.5",
"commission_amount": "1.25",
"is_paid_by_payer": false
},
"amount_received": null,
"txid": null,
"payment_source": "blockchain",
"description": "Order #ORD-12345",
"is_hedging": true,
"hedging_symbol": "BTC",
"is_payer_marked_paid": false,
"payment_url": "https://alfabit.org/en/pub/invoice/order/01J7HZ...",
"alfabit_payment_policy": "all",
"is_open_amount": false,
"show_receiver_publicly": false,
"created_at": "1747325000",
"expires_at": "1747335800"
}
}Fields symbol/network/address/memo_tag will be NULL when you create an invoice with deferred currency — they get populated after the payer chooses the coin/network on the public page.
Get invoice details
/api/v2/integration/invoices/{invoice_id}Permission: can_invoices_read. Same DTO as POST /invoices. Adds alfabit_payment_route and alfabit_payer_symbol/network when paid via AlfaBit Checkout. For QR+KYC, payer_name is the verified payer display name; omitted when there is no link. For auto-conversion invoices (is_hedging=true) this response also carries the outcome: hedged_amount and hedged_symbol — the amount and coin actually credited. Conversion runs after payment, so right after invoice.paid they are still null; prefer the invoice.hedged event over polling.
{
"success": true,
"data": {
"invoice_id": "01J7HZ...",
"status": "success",
"symbol": "USDT",
"network": "TRX",
"address": "TXa1B2...",
"amount_requested": "250.50",
"amount_received": "250.50",
"txid": null,
"payment_source": "alfabit_balance",
"alfabit_payment_route": "direct_internal_transfer",
"alfabit_payer_symbol": "USDT",
"alfabit_payer_network": "TRX",
"alfabit_payment_intent_uid": "intent_01J...",
"is_payer_marked_paid": true,
"payer_name": "И. Иван Иванович",
"payment_url": "https://alfabit.org/en/pub/invoice/order/01J7HZ...",
"show_receiver_publicly": false,
"created_at": "1747325000",
"expires_at": "1747335800"
}
}List invoices
/api/v2/integration/invoicesPermission: can_invoices_read. Pagination by page/limit. Each item is the same object as in GET /invoices/{invoice_id}.
Query parameters
| Param | Type / range | Description |
|---|---|---|
status | string? | Filter: wait / success / failed. |
limit | int (1..100, default 50) | Page size. |
page | int (≥1, default 1) | Page number. |
{
"success": true,
"data": [ /* массив объектов как в GET /invoices/{invoice_id} */ ],
"pagination": {
"total": 137,
"limit": 20,
"offset": 0
}
}Cancel invoice
/api/v2/integration/invoices/{invoice_id}/cancelPermission: can_invoices_create. Cancellation works only in status wait. Moves the invoice to failed. If already paid (success / aml / blocked) — 409 INVALID_STATUS.
{
"success": true,
"data": { /* тот же объект инвойса со status="failed" */ }
}{
"success": false,
"error": {
"code": "INVALID_STATUS",
"message": "Cannot cancel invoice in status 'success'"
}
}Payment URL
/api/v2/integration/invoices/{invoice_id}/payment-urlPermission: can_invoices_read. The link is public — share it with the payer. The same URL is already present as `payment_url` in any GET /invoices/{invoice_id} response; this endpoint is for cases when you don't want to fetch the whole object.
{
"success": true,
"data": {
"payment_url": "https://alfabit.org/en/pub/invoice/order/01J7HZ..."
}
}Permanent QR / template
A permanent invoice is a template. One template_uid lives forever; every actual payment creates a separate child invoice with its own address and TTL. Ideal for cashiers, permanent wall QR codes, donation links and POS terminals.
The template itself is not a payment — invoice.created webhook does NOT fire for it. invoice.paid / invoice.expired fire for each child and carry parent_template_uid + external_payment_id (when set).
Active children per template are capped — see template_max_active_children in GET template response (default 100).
Embed an amount into the public link/QR: append ?amount=500¤cy=RUB&fixed=1 to payment_url — the payment page shows the amount as fixed (price tag), the payer cannot change it. Without parameters the amount stays open.
Create template
/api/v2/integration/invoices/v2/permanentPermission: can_invoices_create. Creates a template without a one-time payment order and without TTL (lives until is_active=true).
Request body
| Field | Type | Description |
|---|---|---|
symbol | string? | Receiver target coin. NULL = payer picks on the receipt. |
bch_code | string? | Network. If set — inherited by every child. |
currency | string? | Default currency of amount for child receipts. |
description | string? (≤500) | Public template description (visible to all payers). |
life_time_minutes | int (1..43200, default 60) | Default TTL for child receipts. |
is_hedging | bool | Hedging is inherited by children. |
hedging_symbol | string? | Hedging target coin. |
payment_policy | enum | all / alfabit_only / external_only. |
show_receiver_publicly | bool | Show receiver name/email on the page. |
show_payments_count_publicly | bool | Show SUCCESS-payments counter on the public page. |
{
"success": true,
"data": {
"template_uid": "01J8AB...",
"status": "wait",
"is_active": true,
"payment_url": "https://alfabit.org/en/pub/invoice/permanent/01J8AB...",
"invoice_currency": "USDT",
"description": "Coffee Shop on Tverskaya 7",
"payments_total": 0,
"is_hedging": false,
"hedging_symbol": null,
"payment_policy": "all",
"show_receiver_publicly": true,
"show_payments_count_publicly": true,
"template_max_active_children": 100,
"created_at": "1747325000"
}
}/api/v2/integration/invoices/v2/permanent/{template_uid}Current template state + payments_total (counter of SUCCESS children). Permission: can_invoices_read.
Issue a receipt against template (POS API)
/api/v2/integration/invoices/v2/permanent/{template_uid}/paymentsPermission: can_invoices_create. Creates a normal child invoice with its own address, TTL and link. Idempotent: a repeat POST with the same idempotency_key returns the SAME child.
Request body
| Field | Type | Description |
|---|---|---|
amount | string (required) | Receipt amount (decimal string > 0). |
currency | string? | Currency of the amount. NULL — inherited from template. |
life_time_minutes | int? (1..43200) | Receipt TTL. NULL = 60. |
description | string? (≤500) | Visible to the payer on the receipt page. |
external_payment_id | string? (≤128) | Your POS-side receipt ID. Echoed back in invoice.paid / invoice.expired webhook. |
idempotency_key | string (8..64, required) | Receipt deduplication. Repeat POST with the same key does not create a second child. |
{
"success": true,
"data": {
"invoice_id": "01J8CD...",
"status": "wait",
"symbol": "USDT",
"network": "TRX",
"address": "TXa1B2...",
"amount_requested": "12.50",
"amount_requested_currency": "USDT",
"amount_to_pay": "12.50",
"payment_source": "blockchain",
"description": "Receipt POS-AAA-9876",
"payment_url": "https://alfabit.org/en/pub/invoice/order/01J8CD...",
"alfabit_payment_policy": "all",
"show_receiver_publicly": true,
"created_at": "1747326000",
"expires_at": "1747326900",
"parent_template_uid": "01J8AB...",
"external_payment_id": "POS-AAA-9876"
}
}Template receipts list
/api/v2/integration/invoices/v2/permanent/{template_uid}/paymentsPermission: can_invoices_read. All child receipts of the template with pagination. Supports filters by status and external_payment_id (exact match).
Query parameters
| Param | Type / range | Description |
|---|---|---|
status | string? | wait / success / failed / expired. |
external_payment_id | string? | Exact match with previously sent receipt ID. |
limit | int (1..200, default 50) | Page size. |
page | int (≥1, default 1) | Page number. |
{
"uid": "01J8CD...",
"status": "success",
"invoice_amount": "12.50",
"invoice_currency": "USDT",
"amount_received": "12.50",
"symbol": "USDT",
"bch_code": "TRX",
"txid": "0x1a2b3c...",
"public_comment": "Receipt POS-AAA-9876",
"external_payment_id": "POS-AAA-9876",
"created_at": "1747326000",
"finished_at": "1747326200"
}Statuses & payment_source
The status field in GET /invoices/{invoice_id} and in webhook data.status is the same. See the table below. Do not confuse status (invoice state) with event (webhook event name).
status
| Value | Meaning | Webhook event |
|---|---|---|
wait | Invoice created, awaiting payment. TTL not yet expired. | invoice.created |
success | Payment received and credited. amount_received is the actual inbound amount; it may be greater or less than amount_requested when the sum is ≥ the network minimum. | invoice.paid |
expired | TTL elapsed and txid is empty: no payment arrived. An on-chain transfer that already arrived will not expire the invoice. | invoice.expired |
failed | Merchant cancel via POST /cancel — or an on-chain payment below the network minimum / dust. A technical create failure has no invoice and no status. | invoice.cancelled (cancel only; below minimum — no webhook, poll GET) |
refunded | The provider refunded the payer (e.g. name mismatch on QR+KYC). The merchant was not credited. | invoice.refunded |
aml | Payment received but AML-frozen. Resolved by support. | — |
blocked | Security policy block. | — |
There are no invoice.aml / invoice.blocked webhooks: poll GET /api/v2/integration/invoices/{invoice_id} for an AML hold. After invoice.paid with status=success a move to aml / blocked is not possible — AML runs before credit. A second transfer to the same address after txid is recorded does not close the same invoice_id again.
payment_source
| Value | Meaning |
|---|---|
blockchain | On-chain payment. txid and network fee present. Default for status=wait. |
alfabit_balance | Payment from AlfaBit balance. txid=null. alfabit_payment_* fields populated. |
Invoice webhooks
There is no callback_url / webhook_url field on invoice create and there will not be one. The URL is set once per account via the Integration Webhooks API (or Developer Console → Webhooks). After you subscribe, events for all your invoices go to that URL.
1. How to subscribe
{
"url": "https://your.domain/hooks/alfabit",
"events": [
"invoice.created",
"invoice.paid",
"invoice.expired",
"invoice.cancelled",
"invoice.refunded",
"invoice.hedged"
]
}The response returns secret once — store it to verify HMAC (X-Webhook-Signature header). Manage via GET/PATCH/DELETE /api/v1/integration/webhooks/{id}; delivery logs: GET .../webhooks/{id}/logs. Event catalog: GET /api/v1/integration/webhooks/events.
2. Delivery envelope
Headers:
Content-Type: application/json
X-Webhook-Event: invoice.paid
X-Webhook-Signature: <hmac-sha256 hex>
Body:
{
"event": "invoice.paid",
"timestamp": "2026-07-21T11:17:55Z",
"signature": "<same hmac>",
"data": { /* см. ниже */ }
}The signature covers the envelope {event, timestamp, data} without the signature field, re-serialized as canonical JSON — see Webhooks → “Signature verification (HMAC)” for the exact algorithm and Python / Node.js examples.
3. Events and data.status
| event | data.status | When |
|---|---|---|
invoice.created | wait | Right after successful POST /api/v2/integration/invoices (and UI). NOT sent for permanent templates. |
invoice.paid | success | Payment credited (blockchain or AlfaBit balance), including permanent children. Reconcile amount_received: it may differ from the nominal (overpay or underpay ≥ the network minimum). |
invoice.expired | expired | TTL elapsed and txid is empty. An underpay that already arrived does not go here. |
invoice.cancelled | failed | Merchant called POST /api/v2/integration/invoices/{invoice_id}/cancel. |
invoice.refunded | refunded | The provider refunded the payer. The merchant was not credited. |
invoice.hedged | success | Auto-conversion (is_hedging) finished: data carries hedged_amount and hedged_symbol — the amount actually credited in the target coin. |
invoice.template.deactivated | — | Permanent invoice template deactivated. |
There is no invoice.failed event. Track merchant cancel via invoice.cancelled, TTL with no payment via invoice.expired, provider refund via invoice.refunded. A payment below the network minimum sets status=failed with no webhook — poll GET. An AML hold is GET-only as well (status=aml / blocked).
4. data fields
{
"invoice_id": "9b854097-3de2-42aa-9f9c-1c0412058c73",
"status": "success",
"symbol": "USDT",
"network": "BSC",
"address": "0x9760...",
"memo_tag": null,
"amount_requested": "10",
"amount_requested_currency": "USDT",
"amount_received": "10",
"txid": "0x1a2b...",
"description": null,
"is_hedging": false,
"hedging_symbol": null,
"is_payer_marked_paid": false,
"payer_name": "И. Иван Иванович",
"payment_source": "blockchain",
"payment_url": "https://alfabit.org/en/pub/invoice/order/9b854097-...",
"created_at": "1784631832.0136988",
"expires_at": "1784632732.0136988",
"fee_breakdown": {
"invoice_fee_mode": "deduct_from_amount",
"fiat_topup_fee_mode": "payer",
"currency": "USDT"
},
"alfabit_payment_intent_uid": null,
"alfabit_payment_route": null,
"alfabit_payer_symbol": null,
"alfabit_payer_network": null,
"parent_template_uid": null,
"external_payment_id": null
}Fields match GET /api/v2/integration/invoices/{invoice_id} (data). For AlfaBit-balance payments: txid=null, payment_source=alfabit_balance, alfabit_* set. For permanent children: parent_template_uid; external_payment_id if you passed it via POS-API. For QR+KYC: payer_name is the verified payer display name; omitted when there is no link.
{
"invoice_id": "9b854097-3de2-42aa-9f9c-1c0412058c73",
"hedged_amount": "0.01324718",
"hedged_symbol": "BTC"
}Sent only for invoices with is_hedging=true and only after the auto-conversion succeeds — i.e. AFTER invoice.paid, as a separate delivery. hedged_amount is the amount actually credited in hedged_symbol (rate and hedging fee already applied). The same two fields are available in GET /api/v2/integration/invoices/{invoice_id}: they stay null until the conversion completes.
KYC-API Checkout (KYC+QR)
A second, equally supported way to accept RUB payments — fully via API, without our hosted payment page. All UI (form, document upload, QR display) is on your side; the payer never leaves your site. Suited for exchangers and crypto projects that need full UX control.
1) Hosted invoice: POST /integration/invoices → payment_url → the payer pays on our page (KYC handled by us). 2) KYC-API checkout (this section): you collect payer documents yourself, submit them to us, receive a QR and render it in your own UI. Which flow is enabled for your account is defined by the acceptance profile (assigned by AlfaBit).
Flow
1. POST /api/v1/integration/checkout/payments
{ amount, payer_phone, payer_ip, external_payment_id, ... }
→ status=kyc_required (инвойс НЕ создаётся)
2. POST /api/v1/integration/checkout/payers/{phone}/documents
(multipart: doc_type + file — паспорт главная стр., прописка)
→ kyc_status=pending, processing=true (это НЕ финальный статус)
3. GET /api/v1/integration/checkout/payers/{phone}
опрашивать каждые 1–2 сек, пока processing=true
→ kyc_status=approved + expected_payer_name
→ processing=false и pending / manual_review=true → ручная сверка
(те же фото заново не отправлять)
→ kyc_status=retry, client_action_required=true → нужны новые документы:
покажите плательщику retry_comment и вернитесь к шагу 2
4. POST /api/v1/integration/checkout/payments (обязательно повторно)
→ payment_uid, status=created (без этого шага QR не будет)
5. GET /api/v1/integration/checkout/payments/{uid}
→ qr_url / qr_payload (СБП-реквизиты, когда готовы)
6. Webhook invoice.paid (оплата прошла, ФИО плательщика совпало)
или invoice.refunded (провайдер вернул платёж, например ФИО не совпало)
Телефон в PATH — только цифры, без «+»:
/payers/79001234567
В JSON-теле payer_phone можно передавать +79001234567 — мы нормализуем.
Вебхука на смену KYC плательщика нет: статус только GET (или повторный POST /payments)./api/v1/integration/checkout/paymentsPermission: can_invoices_create. Creates a RUB payment. If your acceptance profile requires payer verification and the phone is not approved yet, returns kyc_required: no invoice and no QR are created at this step. After documents are uploaded and GET returns approved, call this POST again — otherwise there will be no QR. payer_phone is the verification key: once verified, the payer pays again without re-uploading documents. Required anti-fraud fields: payer_ip (end payer IP, IPv4/IPv6) and external_payment_id (your own order id, for reconciliation — does not protect against duplicates). Optional: payer_user_agent, payer_device_id (device fingerprint).
amount is always the sum you will receive on your balance. Who pays our acceptance fee is controlled by the optional invoice_fee_mode field: deduct_from_amount (default, fee is deducted from amount on credit — as before) or payer (the amount encoded in the SBP QR is increased by the fee, you receive the full amount). The exact rate and figures are in the response below (commission_percent, commission_amount, amount_to_pay) and can be checked in advance via GET /channels.
Send idempotency_key (8–64 chars) for safe retries after a dropped connection: a repeat with the same key within 24 h will not create a second payment — the already created one is returned (same payment_uid/QR); if the original request is still running you get HTTP 409 IDEMPOTENCY_IN_FLIGHT, retry later with the same key. external_payment_id alone does not protect against duplicates — it is only your reconciliation ID. The key only gates the actual payment creation: it has no effect on the kyc_required response, and reusing the same key on the step-4 retry (after payer approval) is intentional — the payment is still created exactly once.
{
"amount": "5000.00",
"external_payment_id": "MM-1234",
"idempotency_key": "order-mm-1234-attempt-1",
"description": "Order #1234",
"payer_phone": "+79001234567",
"payer_ip": "203.0.113.10",
"payer_user_agent": "Mozilla/5.0 ...",
"payer_device_id": "fp_7c9e6679",
"invoice_fee_mode": "deduct_from_amount"
}{
"success": true,
"data": {
"status": "kyc_required",
"kyc_required": true,
"payer_status": "not_started",
"processing": false,
"manual_review": false,
"client_action_required": false,
"retry_comment": null,
"required_documents": ["passport_main", "passport_registration"],
"commission_percent": "1.0",
"upload_documents_endpoint": "/api/v1/integration/checkout/payers/79001234567/documents",
"poll_status_endpoint": "/api/v1/integration/checkout/payers/79001234567"
}
}{
"success": true,
"data": {
"payment_uid": "01J8...",
"external_payment_id": "MM-1234",
"status": "created",
"amount": "5000.00",
"amount_to_pay": "5000.00",
"currency": "RUB",
"invoice_fee_mode": "deduct_from_amount",
"commission_percent": "1.0",
"commission_amount": "50.00"
}
}amount_to_pay is the sum actually encoded in the SBP QR (what the payer must send). With invoice_fee_mode=payer it is greater than amount by exactly commission_amount; with deduct_from_amount (as in the example above) it equals amount, and commission_amount will be deducted from the credited balance.
/api/v1/integration/checkout/payers/{phone}/documentsPermission: can_invoices_create. Multipart upload of a payer document: fields doc_type (passport_main | passport_registration | selfie) and file (JPEG/PNG/PDF, up to 25 MB). Recommended anti-fraud fields (form-data): payer_ip, payer_user_agent, payer_device_id.
The response is returned as soon as the files are accepted, with processing: true — this is not the final status. Recognition runs in the background for a few seconds. The final status comes only from GET /payers/{phone} (see poll_status_endpoint): keep polling while processing is true. When kyc_status=approved, call POST /payments again. processing: false with pending / manual_review=true means an operator is reviewing the payer — do not re-upload the same files. With kyc_status=retry, on the contrary, a new upload is expected: the operator left a note in retry_comment and waits for a new set — re-sending the same photos is pointless. Do not put “+” in the phone path (it is decoded as a space): /payers/79001234567, not /payers/+79001234567.
{
"success": true,
"data": {
"phone": "+79001234567",
"kyc_status": "pending",
"expected_payer_name": null,
"low_confidence": false,
"processing": true,
"poll_status_endpoint": "/api/v1/integration/checkout/payers/79001234567"
}
}The full name is recognized from the main passport page — once approved, show it to the payer with a warning that payment is expected exactly from {name}: the bank matches the actual sender against the passport. The name is returned by GET /payers/{phone} in expected_payer_name, masked as “И. Иван Иванович” (last-name initial + first name + patronymic), the way SBP does it. The payer recognizes themselves while the full surname is never exposed.
Retrying the request is safe: the same file uploaded again under the same doc_type does not create a duplicate document.
/api/v1/integration/checkout/payers/{phone}Permission: can_invoices_read. Payer verification status by phone: not_started | pending | retry | approved | rejected. pending means the check is on our side, retry means we are waiting for new documents from the payer. Phone in the URL is 11 digits with no “+” (/payers/79001234567). Use it after uploading documents (poll while processing is true) and before retrying POST /payments. There is no webhook for KYC status changes.
processing: true — OCR is still running, the status is not final, poll again in 1–2 seconds. processing: false and kyc_status=approved — retry POST /payments to get the QR. processing: false with pending / manual_review=true — the automatic check has finished and the payer is with an operator; this is not a stuck OCR. kyc_status=retry with client_action_required=true — the documents need to be re-submitted: the status will not change on its own, show the payer the retry_comment text and upload a new set (POST /documents).
{
"success": true,
"data": {
"phone": "+79001234567",
"kyc_status": "approved",
"verified_at": "2026-07-27T10:39:26.657298+00:00",
"expected_payer_name": "И. Иван Иванович",
"required_documents": ["passport_main", "passport_registration"],
"processing": false,
"manual_review": false,
"client_action_required": false,
"retry_comment": null
}
}/api/v1/integration/checkout/payments/{uid}Permission: can_invoices_read. Payment status + SBP requisites: qr_url (NSPK link) and qr_payload appear as soon as the payment system generates them (usually seconds). Terminal statuses are also delivered via invoice.paid / invoice.expired / invoice.refunded webhooks. For QR+KYC both names — payer_name and expected_payer_name — are masked as “И. Иван Иванович”.
{
"success": true,
"data": {
"payment_uid": "01J8...",
"status": "success",
"amount": "5000.00",
"currency": "RUB",
"qr_url": "https://qr.nspk.ru/...",
"qr_payload": "https://qr.nspk.ru/...",
"expected_payer_name": "И. Иван Иванович",
"payer_name": "И. Иван Иванович"
}
}/api/v1/integration/checkout/channelsPermission: can_invoices_read. The set of SBP acceptance channels assigned to your account (code, name, fee). A specific channel can be selected per payment via the channel field — from your own set.
{
"success": true,
"data": {
"channels": [
{ "code": "sbp_1", "name": "SBP #1", "fee_percent": 1.0 }
],
"commission_percent": "1.0"
}
}commission_percent (top-level) is the effective acceptance fee rate for your account. This is the authoritative value for planning amount/invoice_fee_mode in POST /payments in advance, before creating a payment; fee_percent inside channels is an informational per-channel field.
Embeddable Widget
Embed AlfaBit Checkout payment widget right on your site — modal, inline block, or iframe. User does not leave the page or get redirected. Analogous to Stripe Elements / PayPal Buttons.
- Create invoice via POST /api/v2/integration/invoices — get invoice_id (uid)
- Load SDK from CDN or via npm @alfabit/checkout-js
- Open the widget: AlfaBitCheckout.open(uid) or mount(...)
- Listen for onSuccess / onError or subscribe via postMessage
1. Pure iframe (no JS)
The simplest option — for static landings and Telegram mini-apps. Just an <iframe>:
<iframe
src="https://alfabit.org/{lang}/embed/invoice/order/{uid}?theme=dark&primary=9ee248"
width="100%"
height="640"
frameborder="0"
allow="payment *; camera; microphone">
</iframe>2. JS SDK (modal popup)
Pay button next to a product — click opens modal:
<script src="https://alfabit.org/checkout/v1/checkout.umd.js"></script>
<button onclick="AlfaBitCheckout.open('{uid}')">Pay</button>3. Inline mount
Widget embeds into the site layout, without modal:
<div id="alfabit-checkout"></div>
<script>
AlfaBitCheckout.mount('#alfabit-checkout', {
invoice: '{uid}',
theme: 'auto',
primary: '#9ee248',
locale: 'ru',
onSuccess: (e) => { window.location = '/thank-you' },
});
</script>4. React / Vue
// React
import AlfaBitCheckout from '@alfabit/checkout-js';
<button onClick={() => AlfaBitCheckout.open('{uid}', { theme: 'dark' })}>
Pay
</button>Events (postMessage)
| Event | Payload | When |
|---|---|---|
alfabit:ready | { invoice_uid, currency, amount } | iframe loaded |
alfabit:resize | { height } | for inline mode |
alfabit:payment.completed | { invoice_uid, amount, currency } | paid |
alfabit:payment.failed | { invoice_uid, error, message } | rejected |
alfabit:payment.cancelled | { invoice_uid } | user cancelled |
alfabit:payment.expired | { invoice_uid } | expired |
Widget options
| Option | Type / Default | Description |
|---|---|---|
invoice | string (required) | invoice uid from POST /api/v2/integration/invoices |
type | 'order' | 'permanent' / 'order' | Single-use or permanent QR |
theme | 'auto' | 'light' | 'dark' / 'auto' | Widget theme |
primary | hex / '#9ee248' | Accent color (6 hex chars) |
locale | 'ru' | 'en' / 'ru' | Language |
height | number / 640 | Inline-mount height |
onSuccess | function | Callback on successful payment |
After creating an invoice in the wallet, the success modal has an "Embed on website" button. It opens a modal with ready-to-paste snippets for all 5 platforms (HTML, JS SDK, React, Vue, Telegram), live preview of the widget, and theme/color controls. Just copy the code.
Real /pub/invoice/* pages are protected by X-Frame-Options: SAMEORIGIN. The widget loads from a separate /embed/invoice/* prefix with CSP frame-ancestors *. SDK filters postMessage by origin (only alfabit.org or your baseUrl). primary is validated by regex (6 hex chars), theme/locale — strict enum. User SSO for "Pay from AlfaBit balance" happens in a popup window, not inside the iframe.
Load the SDK from CDN: https://alfabit.org/checkout/v1/checkout.umd.js or via npm: @alfabit/checkout-js
Travel
Book flights and hotels with crypto, fiat or Wallet balance. The API supports the full cycle from search to order management.
Crypto (USDT, BTC, ETH), fiat (RUB) via invoice, or instant payment from Wallet balance.
This section is under development. Please use Production environment for integration.
| Environment | Base URL | API key |
|---|---|---|
| Production | https://alfabit.org | Create API key in Developer Console |
Reads (search, orders) — can_travel_read. Booking and payments — can_travel_book. Money refunds — can_travel_refund (for refund endpoints and cancellation of paid orders). Existing keys with can_travel_book automatically get refund access too — backward compatible.
All mutating endpoints support the Idempotency-Key header. Send a unique UUID per operation — repeated requests with the same key within 24 hours return the previously stored response without double charging/booking.
Supported on:
- Hotels:
book,pay/crypto,pay/wallet,cancel,create-order,create-order-with-view,create-order-and-select-room,select-room - Avia:
book,pay/crypto,pay/wallet,order/{uuid}/cancel,order/{uuid}/refund
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000Key format: A-Z, a-z, 0-9, hyphen, underscore (1-128 chars). Spaces, colons, and special chars → 400 INVALID_IDEMPOTENCY_KEY.
Instead of polling /order/status, subscribe to events via POST /api/v1/integration/webhooks. Available hotel events:
travel.hotel_order.created— order draft createdtravel.hotel_order.booked— booked at provider, awaiting paymenttravel.hotel_order.paid— order paidtravel.hotel_order.confirmed— hotel confirmed reservationtravel.hotel_order.cancelled— order cancelledtravel.hotel_order.refunded— refund completedtravel.hotel_order.failed— booking/payment error
For avia same events: travel.avia_order.created/booked/paid/cancelled/refunded/failed.
Each step returns data needed for the next. For example, search returns recommendation_id used in create-order.
General
/api/v1/integration/travel/ratesCurrent USDT/RUB and USDT/KGS rates for price calculation.
{ "success": true, "data": { "usdt_rub": 92.5, "usdt_kgs": 89.1 }, "ts": 1706000000 }✈️ Flights
/api/v1/integration/travel/avia/searchSearch flights with recommendations. Specify route segments, passenger counts by type and cabin class. Returns recommendation_id used in /create-order.
{
"adt": 1, // adults: 1-9
"chd": 0, // children: 0-9
"inf": 0, // infants: 0-9
"trip_class": "e", // e / b / f
"segments": [ // массив сегментов (1 = OW, 2 = RT, 3+ = multi-city)
{ "from": "SVO", "to": "LED", "date": "2026-06-15" },
{ "from": "LED", "to": "SVO", "date": "2026-06-20" }
],
"lang": "ru"
}{
"success": true,
"data": {
"flights": [
{
"id": "21DKEASYOWE100...", // длинный ID рейса
"recommendation_id": "21DKEASYOWE100...", // алиас на id (для удобства)
"duration": 185, // общая длительность в минутах
"segments_direction": [
{
"direction": 0,
"segments": [
{
"dep": { "city": "Frankfurt", "airport_code": "FRA", "datetime": "15.06.2026 17:35:00", "ts": 1781534100 },
"arr": { "city": "Istanbul", "airport_code": "SAW", "datetime": "15.06.2026 21:40:00", "ts": 1781548800 },
"flight_info": { "flight_number": "996", "airline": "Pegasus Airlines", "airline_code": "PC" },
"baggage": { "is_baggage": false, "baggage_pieces": 0 }
}
]
}
],
"short_result": {
"duration": 185,
"isBaggage": true, "baggageWeight": 20, "baggagePiece": 1,
"transfers": []
},
"price": { /* цены в разных валютах */ },
"hash": "ce7069..."
}
]
},
"ts": 1714063200
}
// Используйте flights[N].recommendation_id (или flights[N].id) для:
// GET /avia/fare-families?recommendation_id=...
// POST /avia/create-order { recommendation_id, ... }
// POST /avia/book { recommendation_id, ... }/api/v1/integration/travel/avia/fare-families?recommendation_id=...&lang=ruFare families for selected flight: baggage, refund, exchange.
/api/v1/integration/travel/avia/create-orderCreate flight order from selected recommendation.
/api/v1/integration/travel/avia/select-tariffSelect tariff (economy, business, etc.) for order.
/api/v1/integration/travel/avia/bookBook flight with passenger data. Send Idempotency-Key — repeat will not create double booking.
{
"order_uuid": "550e8400-e29b-41d4-a716-446655440000",
"recommendation_id": "REC_12345", // ОБЯЗАТЕЛЬНО. Если выбирали тариф — nonUpgradedId /
// upgradeId из /avia/fare-families, иначе тот же id,
// что в /avia/create-order
"client_email": "user@example.com",
"client_phone": "+79001234567",
"passengers": [
{
"first_name": "IVAN", // КАК В ПАСПОРТЕ — латиницей для международных рейсов
"last_name": "IVANOV",
"type": "adt", // adt / chd / inf
"birth_date": "1990-01-15", // YYYY-MM-DD
"document_type": "passport", // passport / national_id / birth_certificate (для inf)
"document_number": "1234567890",
"document_expire": "2030-01-15",
"citizenship": "RU",
"sex": "M" // M / F
}
],
"lang": "ru"
}Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000{
"success": true,
"data": {
"order_uuid": "550e8400-e29b-41d4-a716-446655440000",
"billing_number": "BN-AVIA-2026-78901",
"status": "booked",
"ticket_time_limit": "2026-04-25T22:00:00Z" // до этого времени надо оплатить
},
"ts": 1714063200
}/api/v1/integration/travel/avia/pay/cryptoPay with crypto/fiat via invoice. Returns address/QR. Supports Idempotency-Key.
{
"order_uuid": "550e8400-e29b-41d4-a716-446655440000",
"currency_in_code": "USDT_TRC20" // USDT_TRC20 / BTC / ETH / RUB / ...
}{
"success": true,
"data": {
"invoice_id": "INV-AVIA-12345",
"deposit_address": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
"qr_code_url": "https://...",
"amount": "95.50",
"currency": "USDT_TRC20",
"expires_at": "2026-04-25T22:00:00Z"
},
"ts": 1714063200
}/api/v1/integration/travel/avia/pay/walletInstant payment from Wallet balance. Funds debited immediately. CRITICAL to send Idempotency-Key.
{
"order_uuid": "550e8400-e29b-41d4-a716-446655440000",
"currency": "USDT"
}Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000{
"success": true,
"data": {
"order_uuid": "550e8400-e29b-41d4-a716-446655440000",
"status": "paid",
"paid_at": "2026-04-25T20:05:00Z",
"amount_charged": "95.50",
"currency": "USDT",
"balance_after": "1054.50",
"ticket_status": "issued" // issued / pending / failed
},
"ts": 1714063200
}/api/v1/integration/travel/avia/wallet/balance?currency=USDTWallet balance for flight payment.
/api/v1/integration/travel/avia/ordersAll your flight orders.
/api/v1/integration/travel/avia/order/{order_uuid}Order details: route, passengers, status, cost.
/api/v1/integration/travel/avia/order/{order_uuid}/statusOrder status: created, booked, pay_waiting, ticketed, cancelled, refunded.
/api/v1/integration/travel/avia/order/{order_uuid}/pdfDownload PDF itinerary receipt / e-ticket.
/api/v1/integration/travel/avia/order/{order_uuid}/cancelCancel flight booking.
/api/v1/integration/travel/avia/order/{order_uuid}/refundFlight refund (voluntary or involuntary).
/api/v1/integration/travel/avia/passengersSaved passengers for quick booking.
🏨 Hotels
Dictionaries
/api/v1/integration/travel/hotels/destination?part=Mosc&lang=ruCity/region autocomplete for hotel search.
/api/v1/integration/travel/hotels/cities/search?q=Mosc&limit=15Search canonical cities via our dictionary (fast, no provider call). Separate dictionary: its hotel_city_id is NOT valid for the city field in hotel search — use the id from /hotels/destination.
/api/v1/integration/travel/hotels/avia-bridge?hotel_id=12345Bridge "hotel → city IATA → nearby airports" — for cross-selling flights after hotel selection.
Search
/api/v1/integration/travel/hotels/searchSynchronous hotel search by city, dates and guest count. May take 30-60 sec. For better UX use /search/async.
{
"city": "7000546", // числовой id из /hotels/destination.
// НЕ название города и НЕ hotel_city_id из /hotels/cities/search
"check_in": "15.05.2026", // строго dd.mm.yyyy — с точками, иначе 400
"check_out": "20.05.2026",
"adults": 2, // 1-6
"children": [{ "child_age": 5 }], // optional
"lang": "ru"
}{
"success": true,
"data": {
"search": { "city": "7000546", "check_in": "15.05.2026", ... },
"hotels": [
{
"id": "12345",
"name": "Hotel Example",
"stars": 4,
"city": "Москва",
"country": "Россия",
"address": "...",
"photos": [{ "url": "...", "thumb": "...", "is_default": true }],
"rooms": [...],
"hs": "search-hash-for-view"
}
],
"is_completed": true
},
"ts": 1714063200
}/api/v1/integration/travel/hotels/search/asyncAsync search with Redis cache and gzip stream. Returns first batch + agent_hash for polling. Recommended for progressive UX.
{
"city": "7000546", // числовой id из /hotels/destination
"check_in": "15.05.2026", // строго dd.mm.yyyy
"check_out": "20.05.2026",
"adults": 2,
"children": [],
"lang": "ru"
}/api/v1/integration/travel/hotels/search/async-by-hash?search_hash=...&search_params_hash=...&lang=ruPolling for async search. Call until response contains is_completed: true. Body is delivered as gzip stream.
/api/v1/integration/travel/hotels/search/create-hashLow-level: create only agent_hash without starting search. Normally /search/async is enough.
/api/v1/integration/travel/hotels/search/by-idsSearch by list of known hotel_ids — for favorites, viewing history.
/api/v1/integration/travel/hotels/search/viewAvailable rooms in specific hotel with prices and cancellation conditions.
{
"hotel_id": "12345",
"check_in": "15-05-2026", // dd-mm-yyyy
"check_out": "20-05-2026",
"adults": 2,
"children": [{ "child_age": 5 }],
"lang": "ru"
}{
"success": true,
"data": {
"hs": "eyJob3RlbF9pZCI6IjQxODUzMyIsImhzIjpudWxsLCJjaGVja19pbiI6...",
// передавайте строку как есть в /check-rate и /book.
// Это base64 параметров просмотра; при поиске по hotel_id
// внутри лежит "hs": null — это нормально, не блокирует бронь
"hotel": {
"id": "12345",
"name": "Hotel Example",
"rooms": [
{
"id": "149e71dd-3257-43e3-8f06-9bb51208a270", // UUID, внутренний ключ.
// НЕ передавайте его в rate_id — провайдер его не знает
"identifier": "418533..roomOnly..Улучшенный..19082026..835828f9..30,30",
// ЭТО и есть идентификатор тарифа:
// → recommendation_id в /select-room
// → rate_id в /check-rate и /book
"name": "Standard Double",
"type": "DBL",
"price": 12500.00, // в валюте провайдера
"totalAmount": 12500.00,
"mealType": "BB", // BB / HB / FB / AI
"isFreeCancellation": true,
"freeCancellationBefore": "...",
"is_non_refundable": false,
"cancelationRules": [...], // → cancellation_policy_rules для /book
"available_count": 3
}
]
}
},
"ts": 1714063200
}/api/v1/integration/travel/hotels/search/view-by-hash?hotel_id=...&hs=...&lang=ruQuick room view by hs from search step (no repeat /view call).
/api/v1/integration/travel/hotels/hotel/{hotel_id}?lang=ruHotel data: description, photos, rating, amenities.
/api/v1/integration/travel/hotels/check-rateMandatory final check of price and cancellation rules BEFORE /book. If price/rules changed — show user and ask confirmation.
{
"hs": "rooms-search-hash",
"hotel_id": "12345",
"rate_id": "418533..roomOnly..Улучшенный..19082026..835828f9..30,30", // rooms[].identifier
"price": 12500.00,
"is_not_refundable": false,
"free_cancellation_before": "2026-05-13T12:00:00+00:00",
"cancellation_policy_rules": [
{
"isPossible": true,
"amount": 0,
"UTCDateFrom": "2026-04-25T00:00:00+00:00",
"UTCDateTo": "2026-05-13T12:00:00+00:00"
}
],
"lang": "ru"
}{
"success": true,
"data": {
"rate_id": "ROOM_ID",
"check_price_changes": {
"old": { "totalAmount": 12500, "price": 12500 },
"new": { "totalAmount": 12500, "price": 12500 }
},
"cancellation_policy_rules_changes": {
"old": [...],
"new": [...]
}
},
"ts": 1714063200
}Order & Booking
/api/v1/integration/travel/hotels/create-orderCreate empty hotel order draft. Supports Idempotency-Key.
{
"order_uuid": "550e8400-e29b-41d4-a716-446655440000", // сгенерируйте UUID v4
"hotel_id": "12345",
"search_hash": "rooms-search-hash", // optional, hs из /view
"hotel_name": "Hotel Example", // для отображения
"lang": "ru"
}{
"success": true,
"data": {
"order_uuid": "550e8400-e29b-41d4-a716-446655440000",
"order_id": 78901,
"status": "created",
"datetime": "2026-04-25T20:00:00Z"
},
"ts": 1714063200
}/api/v1/integration/travel/hotels/create-order-with-viewAtomic: create order + get hotel view (rooms, prices) in one call. Convenient for "selected hotel by name" flow.
{
"order_uuid": "550e8400-e29b-41d4-a716-446655440000",
"view": {
"hotel_id": "12345",
"check_in": "15-05-2026",
"check_out": "20-05-2026",
"adults": 2,
"children": [],
"lang": "ru"
}
}{
"success": true,
"data": {
"order": { "order_uuid": "...", "order_id": 78901, "status": "created" },
"view": { /* такой же формат, как у /search/view */ }
},
"ts": 1714063200
}/api/v1/integration/travel/hotels/select-roomSelect specific room and rate in existing order.
{
"order_uuid": "550e8400-e29b-41d4-a716-446655440000",
"recommendation_id": "ROOM_IDENTIFIER", // identifier из /view → rooms[].identifier
"payload": "{\"...full room JSON from /view...\"}" // payload номера, как пришёл из /view
}{
"success": true,
"data": {
"order_uuid": "550e8400-e29b-41d4-a716-446655440000",
"status": "room_selected"
},
"ts": 1714063200
}/api/v1/integration/travel/hotels/create-order-and-select-roomAtomic: create order and select room at once. If room selection fails, order is auto-deleted.
{
"order_uuid": "550e8400-e29b-41d4-a716-446655440000",
"hotel_id": "12345",
"search_hash": "rooms-search-hash",
"hotel_name": "Hotel Example",
"lang": "ru",
"recommendation_id": "ROOM_IDENTIFIER",
"payload": "{\"...room JSON...\"}"
}{
"success": true,
"data": {
"order_uuid": "550e8400-e29b-41d4-a716-446655440000",
"order_id": 78901,
"status": "room_selected",
"myagent_price": 12500.00,
"alfabit_price": 13125.00 // включая нашу маржу
},
"ts": 1714063200
}/api/v1/integration/travel/hotels/bookFinal booking with guests, contacts and confirmed cancellation rules. ALWAYS call /check-rate first. Send Idempotency-Key — repeat will not create double booking.
{
"order_uuid": "550e8400-e29b-41d4-a716-446655440000",
"client_email": "user@example.com",
"client_phone": "+79001234567",
"guests": [
{ "first_name": "Ivan", "last_name": "Ivanov", "type": "adt" },
{ "first_name": "Anna", "last_name": "Ivanova", "type": "chd", "age": 5 } // age обязателен для chd/inf
],
"hs": "rooms-search-hash",
"hotel_id": "12345",
"hotel_name": "Hotel Example",
"rate_id": "418533..roomOnly..Улучшенный..19082026..835828f9..30,30",
// тот же rooms[].identifier, что в /select-room и /check-rate
"price": 12500.00, // из /check-rate
"is_not_refundable": false,
"free_cancellation_before": "2026-05-13T12:00:00+00:00",
"cancellation_policy_rules": [ // из /check-rate
{
"isPossible": true,
"amount": 0,
"UTCDateFrom": "2026-04-25T00:00:00+00:00",
"UTCDateTo": "2026-05-13T12:00:00+00:00"
}
],
"lang": "ru"
}Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000{
"success": true,
"data": {
"order_uuid": "550e8400-e29b-41d4-a716-446655440000",
"billing_number": "BN-2026-78901",
"status": "booked" // booked / failed
},
"ts": 1714063200
}Payment
/api/v1/integration/travel/hotels/pay/cryptoCreates invoice for crypto/fiat payment. Returns wallet address, QR code and invoice lifetime. Supports Idempotency-Key.
{
"order_uuid": "550e8400-e29b-41d4-a716-446655440000",
"currency_in_code": "USDT_TRC20", // USDT_TRC20 / BTC / ETH / RUB / ...
"use_miles": false,
"miles_to_redeem": null // если use_miles=true
}{
"success": true,
"data": {
"invoice_id": "INV-12345",
"order_uuid": "550e8400-e29b-41d4-a716-446655440000",
"currency": "USDT_TRC20",
"amount": "100.00",
"deposit_address": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
"qr_code_url": "https://...",
"expires_at": "2026-04-25T21:00:00Z",
"status": "pending"
},
"ts": 1714063200
}/api/v1/integration/travel/hotels/pay/walletInstant hotel payment from Wallet balance (deducted from balance). CRITICAL to send Idempotency-Key, otherwise repeat will double-charge.
{
"order_uuid": "550e8400-e29b-41d4-a716-446655440000",
"currency": "USDT", // валюта баланса
"use_miles": false,
"miles_to_redeem": null
}Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000{
"success": true,
"data": {
"order_uuid": "550e8400-e29b-41d4-a716-446655440000",
"status": "paid",
"paid_at": "2026-04-25T20:05:00Z",
"amount_charged": "100.00",
"currency": "USDT",
"balance_after": "1150.00"
},
"ts": 1714063200
}/api/v1/integration/travel/hotels/wallet/balance?currency=USDTWallet balance for hotel payment. Check before /pay/wallet.
currency=USDT // USDT / BTC / ETH / RUB{
"success": true,
"data": {
"user_id": "sso-id-of-user",
"currency": "USDT",
"balance": "1250.00"
},
"ts": 1714063200
}Miles (loyalty)
/api/v1/integration/travel/hotels/miles/preview?order_uuid=...How many miles can be spent on this order (limits and balance considered).
/api/v1/integration/travel/hotels/miles/balanceLoyalty miles balance and public accrual settings.
Order management
/api/v1/integration/travel/hotels/ordersAll your hotel orders.
/api/v1/integration/travel/hotels/order/{order_uuid}Hotel order details: room, dates, guests, cost, voucher URL.
{
"success": true,
"data": {
"order_id": 78901,
"status": "paid",
"expire": "2026-04-25T21:00:00Z",
"guests": [
{ "first_name": "Ivan", "last_name": "Ivanov", "type": "adt" },
{ "first_name": "Anna", "last_name": "Ivanova", "type": "chd", "age": 5 }
],
"hotel": { /* такой же формат, как у /search/view */ },
"price": 12500.00,
"billing_number": "BN-2026-78901",
"receipt": "https://...voucher.pdf",
"client_email": "user@example.com"
},
"ts": 1714063200
}/api/v1/integration/travel/hotels/order/{order_uuid}/statusHotel order status. Possible values: created, room_selected, booked, paid, confirmed, cancelled, refunded, failed.
{
"success": true,
"data": {
"order_uuid": "550e8400-e29b-41d4-a716-446655440000",
"status": "paid",
"updated_at": "2026-04-25T20:05:00Z"
},
"ts": 1714063200
}/api/v1/integration/travel/hotels/order/{order_uuid}/cancelCancel hotel booking. Body is optional. If order is paid and refund applies, can_travel_refund is also required. Supports Idempotency-Key.
{
"reason": "hotel_other_reason", // optional
"reason_comment": "Изменились планы" // обязателен только если reason='hotel_other_reason'
}{
"success": true,
"data": {
"order_uuid": "550e8400-e29b-41d4-a716-446655440000",
"status": "cancelled",
"refund_amount": "12500.00", // если был paid
"refund_currency": "RUB"
},
"ts": 1714063200
}/api/v1/integration/travel/hotels/guestsSaved guests for quick booking.
Webhooks
A single webhook system for all products. The URL is set once per account (not in operation bodies). After you subscribe, events for wallet, trading, cards, invoices, service payments and Travel are delivered to your URL.
Always verify event names against this endpoint — it returns the live catalog. The table below matches it 1:1. Dots are part of the name (e.g. service_payment.created, card.transaction), NOT a wildcard like service.payment.*.
/api/v1/integration/webhooksCreate a subscription: pass url and an events array (names from the table below). The response returns secret once — store it to verify HMAC.
{
"url": "https://your-domain.com/webhook",
"events": [
"invoice.paid",
"card.transaction",
"service_payment.confirmed",
"travel.avia_order.paid"
]
}Manage via GET / PATCH / DELETE /api/v1/integration/webhooks/{id}. Change subscribed events with PATCH events. Delivery logs: GET .../webhooks/{id}/logs.
Signature verification (HMAC)
Every request carries X-Webhook-Event (event name) and X-Webhook-Signature (HMAC-SHA256 hex computed with your subscription secret — the one returned once when the webhook was created, not the API secret). The same value is duplicated in the signature field inside the body: compare against either one.
X-Webhook-Event: invoice.paid
X-Webhook-Signature: <hmac-sha256 hex>
Content-Type: application/jsonThe signature covers the event envelope {event, timestamp, data} WITHOUT the signature field, re-serialized as canonical JSON. Do not hash the raw request body: it already contains signature and its key order differs from the canonical one. Parse the body, drop signature and re-serialize.
message = canonical_json({"event": ..., "timestamp": ..., "data": {...}})
signature = HMAC-SHA256(webhook_secret, message) → hex- The signature field is excluded from the signed object.
- Keys are sorted alphabetically and recursively — including keys inside data. At the top level the order becomes data, event, timestamp.
- Compact separators, no spaces: "," and ":".
- Non-ASCII characters are escaped as \uXXXX (equivalent to json.dumps with ensure_ascii=True).
import hashlib, hmac, json
WEBHOOK_SECRET = "your_webhook_secret"
def verify(body: dict) -> bool:
received = body.get("signature", "")
payload = {k: v for k, v in body.items() if k != "signature"}
message = json.dumps(payload, separators=(",", ":"), sort_keys=True)
expected = hmac.new(
WEBHOOK_SECRET.encode(), message.encode(), hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, received)- Hashing the raw request body as-is — its key order is not sorted.
- Hashing only the data object — the whole envelope is signed, including event and timestamp.
- The signature field was left inside the signed object.
- JSON serialized with spaces after "," and ":".
- The API key secret was used instead of the webhook secret.
Full event catalog
| Event | Description |
|---|---|
| Wallet | |
deposit.pending | Deposit detected (crypto only) |
deposit.confirmed | Deposit confirmed (crypto and RUB) |
deposit.failed | RUB deposit failed or refunded |
withdrawal.processing | Withdrawal processing (crypto only; not sent for SBP) |
withdrawal.completed | Withdrawal completed (crypto and RUB) |
withdrawal.failed | Withdrawal failed, funds returned (crypto and RUB) |
transfer.received | Internal transfer received |
conversion.completed | Conversion completed |
| Trading (Spot) | |
order.created | Order created |
order.filled | Order filled |
order.partially_filled | Order partially filled |
order.cancelled | Order cancelled |
trade.executed | Trade executed (fill on order) |
trading.deposit.completed | Trading account deposit completed (Spot Fiat) |
trading.withdraw.completed | Trading account withdraw completed (Spot Fiat) |
trading.balance.updated | Trading balance updated (Spot Fiat) |
| Invoices | |
invoice.created | Invoice created |
invoice.paid | Invoice paid |
invoice.expired | TTL expired with empty txid (no payment). An underpay that already arrived does not go here |
invoice.cancelled | Invoice cancelled by merchant |
invoice.refunded | Provider refunded the payer |
invoice.hedged | Invoice auto-conversion finished, funds credited in the target coin |
invoice.template.deactivated | Permanent invoice template deactivated |
| Cards | |
card.transaction | Card transaction (incl. issue/top-up) |
| Service payments | |
service_payment.created | Service payment created |
service_payment.confirmed | Service payment confirmed |
service_payment.failed | Service payment failed |
service_payment.cancelled | Service payment cancelled |
| Travel — Hotels | |
travel.hotel_order.created | Hotel order draft created |
travel.hotel_order.booked | Hotel booked (awaiting payment) |
travel.hotel_order.paid | Hotel order paid |
travel.hotel_order.confirmed | Hotel confirmed (final) |
travel.hotel_order.cancelled | Hotel order cancelled |
travel.hotel_order.refunded | Hotel order refunded |
travel.hotel_order.failed | Hotel order failed |
| Travel — Avia | |
travel.avia_order.created | Avia order draft created |
travel.avia_order.booked | Avia ticket booked (awaiting payment) |
travel.avia_order.paid | Avia order paid (ticket issued) |
travel.avia_order.cancelled | Avia order cancelled |
travel.avia_order.refunded | Avia order refunded |
travel.avia_order.failed | Avia order failed |
There is NO giftcard.* event — track gift card purchases via GET /giftcards/orders. Wildcards like service.payment.* / card.* are not supported: subscribe to exact names from the table.
Deposits and payouts: crypto and RUB share the same events
The deposit.confirmed, withdrawal.completed and withdrawal.failed events are sent both for crypto operations and for RUB (SBP) requests. Their field sets differ, so data carries a kind discriminator: crypto or fiat. The deposit.failed event is fiat-only. withdrawal.processing and deposit.pending are crypto-only: SBP has no in-flight webhook — poll GET /funding/deposit/fiat/{id} or GET /funding/withdraw/fiat/{id} while the request is open.
{
"event": "deposit.confirmed",
"timestamp": "2026-08-28T09:14:02Z",
"data": {
"kind": "fiat",
"transaction_id": "6a2a0425-d681-4b8c-9792-4ce15d46ef0c",
"status": "success",
"currency": "RUB",
"amount": "10000.00",
"credited_amount": "9820.00",
"fee": "180.00",
"payment_provider_alias_code": "SBER",
"error": null,
"error_code": null
},
"signature": "..."
}{
"event": "withdrawal.failed",
"timestamp": "2026-08-28T09:41:55Z",
"data": {
"kind": "fiat",
"transaction_id": "6f05ad6f-6cd2-47f9-880b-87ed1b58abdc",
"status": "failed",
"currency": "RUB",
"amount": "500.00",
"amount_fact": "0.00",
"fee": "10.00",
"total": "510.00",
"bank_code": "GAZPROM",
"recipient": "7900****67",
"error": "Проведение операции невозможно",
"error_code": "generic_refund"
},
"signature": "..."
}| Field | Description |
|---|---|
kind | crypto or fiat. Use it to tell the two apart instead of probing for individual keys. |
transaction_id | The same identifier returned by the create request and accepted by GET /funding/deposit/fiat/{id} and GET /funding/withdraw/fiat/{id}. Deduplicate redeliveries by it. |
credited_amount | The RUB amount actually credited. null when no RUB were credited — e.g. for an invoice paid for conversion: the credited crypto arrives with conversion.completed. |
amount_fact | The amount actually sent to the recipient. 0.00 on failure. |
error / error_code | Failure reason and its code. Filled only on a terminal failure; when no reason is available they are null rather than repeating the word failed. |
recipient | Recipient's details, masked the same way as in the withdrawal GET response. |
The event is sent only for a terminal outcome: intermediate statuses are not duplicated over webhooks. Amounts come with two decimals — the same format the deposit and withdrawal GET endpoints return.
transfer.received
An incoming internal transfer credited to your balance. The format is identical for crypto and RUB.
{
"event": "transfer.received",
"timestamp": "2026-07-30T10:37:15Z",
"data": {
"transfer_id": "2b8bc516-e49a-40eb-b8c1-1a0fa02c2cfe",
"transaction_uid": "2b8bc516-e49a-40eb-b8c1-1a0fa02c2cfe",
"symbol": "RUB",
"amount": "3872.01",
"status": "credited",
"user_comment": "AB-7K3QF9XM",
"sender_profile_id": 904016,
"sender_email": "payer@example.com",
"recipient_profile_id": 7504,
"method": null
},
"signature": "..."
}| Field | Description |
|---|---|
transfer_id | Transfer identifier. Equals transaction_uid and the operation uid in GET /integration/transactions — use it to deduplicate redeliveries. |
status | Always credited: the event is sent only after the funds are actually credited and available to the recipient. |
user_comment | Sender's comment (up to 500 characters), null when not provided. |
sender_email | Sender's email, null if the profile has none. |
method | Addressing method of a crypto transfer (tg / email / phone). Always null for RUB transfers — the method is not stored. |
A redelivery is NOT byte-identical: the timestamp is regenerated on retry, so the signature changes too, while the data object stays the same. Deduplicate by transfer_id, not by a body hash.
A non-2xx response and a timeout (10s) trigger a retry: 3 attempts in total — immediately, after 30 seconds and after 2 minutes.
Pre-filled transfer link
The link opens the internal transfer form already filled in. It does not send anything: the recipient is verified by the platform, the amount is validated by the field rules, and the user presses confirm themselves.
https://alfabit.org/ru/user/transfer?to=user%40example.com&method=email&symbol=USDT&amount=25&comment=Order%20A-1042| Parameter | Description |
|---|---|
to | Recipient: email, @telegram_username or a phone number in +71234567890 format. |
method | email, telegram_username or phone. Optional — the method is derived from the format of to. |
symbol | Asset to transfer, e.g. USDT or RUB. If omitted, the user picks it themselves. |
amount | Amount without thousand separators, dot as the decimal separator. It is placed into the field and goes through the usual balance and limit checks. |
comment | Sender's comment, up to 140 characters. Reaches the recipient in the history and in the transfer.received webhook as user_comment. |
All parameters are optional and invalid ones are silently ignored — the form simply opens with an empty field. The link requires authentication: an unauthenticated user is sent to the login page first and lands on the filled form afterwards.
Currencies & networks directory
The single source of truth for coins and networks: amount precision, limits, fees and operation availability. Read it before a withdrawal or before issuing an invoice — parameters differ per network and change without notice (a network can be temporarily halted on the blockchain side).
/api/v1/integration/market/currenciesAn array of records, one per coin+network pair. No API key required.
{
"success": true,
"data": [
{
"symbol": "USDT",
"bch_code": "TRX",
"withdraw_amount_decimals": "6",
"min_withdraw_amount": "3",
"max_withdraw_amount": "150000",
"withdraw_service_fee": "2.5",
"is_withdraw_active": true,
"is_deposit_active": true,
"min_deposit_amount": "1",
"is_memo_tag_required": false,
"net_confirmations": 20
},
{
"symbol": "USDT",
"bch_code": "BSC",
"withdraw_amount_decimals": "8",
"min_withdraw_amount": "10",
"max_withdraw_amount": "150000",
"withdraw_service_fee": "1.2",
"is_withdraw_active": true
}
],
"ts": 1706000000
}symbol+bch_code— coin and network. Pass the same pair to withdrawals and invoices.withdraw_amount_decimals— how many decimals the network accepts on withdrawal. Extra decimals are truncated down.min_withdraw_amount/max_withdraw_amount— per-request withdrawal limits in this network.withdraw_service_fee— withdrawal fee denominated in the coin.is_withdraw_active/is_deposit_active— whether operations in this network are open right now.is_memo_tag_required— whether the recipient requires a memo/tag (XRP, TON and similar).
The payload is large — cache it on your side and refresh every few minutes rather than before every operation.
/api/v1/integration/market/ratesCoin prices denominated in USDT — one record per symbol. No API key required.
{
"success": true,
"data": [
{ "symbol": "USDT", "rate_usdt": "1" },
{ "symbol": "BTC", "rate_usdt": "78635.6" },
{ "symbol": "ETH", "rate_usdt": "2470.64" },
{ "symbol": "TRX", "rate_usdt": "0.3438" }
],
"ts": 1706000000
}Use these values for display, portfolio valuation and rough estimates. The price an operation actually executes at comes from a product quote — /converter/crypto/estimate or /converter/fiat/estimate: it accounts for direction, size and fee, and is valid for a limited time. Do not compute the charged amount from rate_usdt.
Tariffs & Fees
Current tariff plan info and fee rates for all operation types.
/api/v1/integration/market/tariffsFee schedule: withdrawal, exchange, card top-up and other operation fees. No API key required.
{
"tariff": "standard",
"fees": {
"withdraw_btc": "0.0001",
"withdraw_usdt_trc20": "1",
"exchange_fee": "0.1%",
"card_topup_fee": "2%"
}
}Transactions
Unified history of all operations: deposits, withdrawals, transfers, exchanges, purchases. Crypto and fiat (RUB) operations are merged into a single feed — tell them apart by the source field.
/api/v1/integration/transactionsAll transactions with filters by currency and period. Supports pagination and search.
| Parameter | Type | Description |
|---|---|---|
symbol | string | Filter by currency (USDT, BTC, RUB). RUB leaves only fiat operations in the feed, any other currency — only crypto ones. |
limit | number | Records count (default: 50, max: 100) |
page | number | Page number (default: 1) |
start_period | number | Period start (Unix timestamp) |
end_period | number | Period end (Unix timestamp) |
search_term | string | Search transactions |
{
"success": true,
"data": {
"page": 1,
"limit": 50,
"count": 156,
"results": [
{
"source": "fiat",
"uid": "0f3c1a7e-1b9d-4f2a-9c11-6a0d5e7b8c34",
"type": "deposit",
"symbol": "RUB",
"amount": "5000.00",
"status_for_client": "success",
"is_final_state": true,
"method": null,
"fee": "0.0",
"sender_id": 7504,
"receiver_id": 9001,
"user_comment": "order-12345",
"sender": { "email": "client@example.com", "telegram_username": null },
"receiver": { "email": "shop@example.com", "telegram_username": null },
"created_at": 1753812345.0
},
{
"source": "crypto",
"uid": "a1b2c3d4-...",
"type": "deposit",
"symbol": "USDT",
"amount": "500.00",
"status_for_client": "success",
"is_final_state": true,
"method": "email",
"created_at": 1753810000.0
}
]
},
"ts": 1706000000
}Record fields
| Field | Description |
|---|---|
source | "crypto" for crypto operations, "fiat" for RUB ones. |
uid | Operation identifier. For internal transfers it equals transfer_id from the transfer.received webhook — use it as the idempotency key. |
type | Type from the key owner point of view: incoming is deposit, outgoing is withdraw. Internal transfers are resolved by role: the recipient sees deposit. |
status_for_client | wait | success | failed | blocked | aml. |
is_final_state | The status is terminal. While false the state may still change — do not settle yet. |
method | Transfer addressing method (email, telegram, phone). Always null for RUB operations — the field is kept for uniform parsing. |
sender / receiver | Transfer participants (email, telegram_username). Match the payer by sender.email. |
user_comment | Sender comment — a convenient place to ask the client for an order number. |
created_at | Creation time, Unix timestamp (number, not a string). |
To accept top-ups via an internal AlfaBit transfer, subscribe to the transfer.received webhook — it is sent only once the funds are credited and carries transfer_id, amount, sender_email and the sender comment. Use this feed as a reconciliation fallback: filter records with type = deposit and is_final_state = true, deduplicate by uid.
Deep pagination of the merged feed is limited: page × limit must not exceed 2000, otherwise the API returns 400 DEEP_PAGINATION_NOT_SUPPORTED. To export history, narrow the range with start_period / end_period.
/api/v1/integration/transactions/{transaction_id}Specific transaction details: amount, status, fee, date, participants. Pass the uid from the feed (same value as the webhook transfer_id) as transaction_id; the endpoint looks the operation up among both crypto and RUB records.
{
"success": true,
"data": {
"source": "fiat",
"uid": "0f3c1a7e-1b9d-4f2a-9c11-6a0d5e7b8c34",
"type": "deposit",
"symbol": "RUB",
"amount": "5000.00",
"status_for_client": "success",
"is_final_state": true,
"fee": "0.0",
"sender_id": 7504,
"receiver_id": 9001,
"user_comment": "order-12345",
"created_at": 1753812345.0
},
"ts": 1706000000
}