#Wallet and Exchange Suspension
Status: Available.
GET /walletand halting exchange on a negative balance (402 wallet_suspended).
Purpose: how the tenant wallet affects exchange over the API, which states exist, what happens to requests and webhooks when the balance is negative, and how to resume exchange.
#Contents
- Wallet states
- GET /wallet
- What happens when exchange is suspended
- Webhooks during suspension
- Unknown state
- How to resume exchange
- Notifications to the tenant
#Wallet states
The tenant wallet is kept in rubles (RUB) on any cell. Amounts are passed as an {amount, currency} object, where amount is a string with two decimal places ("125.50", "-3.20").
state | When | exchange | What happens to requests |
|---|---|---|---|
ok | Funds are sufficient | active | Everything works |
low | Funds are low (below the warning threshold) | active | Everything works; time to top up |
negative | The balance is below zero | suspended | Exchange is halted |
blocked | The wallet is blocked | suspended | Exchange is halted |
unknown | The balance source did not respond | active | Reads that do not charge the wallet work, paid operations are closed (below) |
A balance of exactly zero does not halt exchange. The platform owner (the platform's internal service tenant) is not billed and is never suspended.
#GET /wallet
No scope is needed and there is no charge. The route works even while exchange is suspended: this is where you see the reason for the suspension and the top-up address.
curl -sS "$API_BASE_URL/wallet" -H "Authorization: Bearer $API_KEY"
{
"balance": { "amount": "-3.20", "currency": "RUB" },
"state": "negative",
"exchange": "suspended",
"topup_url": "https://formula-cream.pro/ru/promo/economics#wallet",
"daily_spend": null
}
| Field | Description |
|---|---|
balance | The balance. null if the state is unknown or the tenant is not billed |
state | ok, low, negative, blocked, unknown |
exchange | active or suspended |
topup_url | The dashboard address where the wallet is topped up. A top-up is performed by a tenant employee with the top-up permission; you cannot top up the wallet with an API key |
daily_spend | The integration's daily spending; null while spending is not tracked per integration. The integration's limit is visible in GET /me (wallet.daily_spend) |
The same block (balance, state, exchange, topup_url, daily_spend) arrives in the wallet field of the GET /me response.
#What happens when exchange is suspended
While exchange = suspended, every route except GET /me and GET /wallet responds with 402:
{
"type": "https://formula-cream.pro/developers/errors#wallet_suspended",
"title": "Wallet is suspended",
"status": 402,
"code": "wallet_suspended",
"detail": "Data exchange is suspended because the tenant wallet balance is negative or the wallet is blocked. Top up the wallet to resume.",
"topup_url": "https://formula-cream.pro/ru/promo/economics#wallet",
"balance": { "amount": "-3.20", "currency": "RUB" },
"request_id": "req_01J…"
}
Rules:
- The check runs immediately after authentication: before scopes are checked, before the rate limit, and before the body is read. The request creates nothing: no operation, no idempotency log entry, no data.
- This applies to both reads and writes: incoming data (events, messages, leads) is not accepted while exchange is suspended. Repeat the request after the top-up with the same
Idempotency-Key. - Do not retry
402 wallet_suspendedautomatically in a loop: switch to checkingGET /walletonce a minute, or wait for the notification.
#Webhooks during suspension
Outgoing webhooks are neither sent nor lost while exchange is suspended:
| Rule | Description |
|---|---|
| Deferral | The delivery is pushed back 15 minutes (next_attempt_at) and checked again |
| Attempts | The attempt counter does not increase: a suspension caused by the wallet does not consume the eight retries |
| Resumption | After the top-up, deferred deliveries go out automatically, in queue order |
| Retention | Events are retained for 30 days. If an event passes this limit before exchange resumes, its delivery is closed as a "dead letter" with the reason wallet_suspended; fetch anything missing from the API by the resource identifier |
#Unknown state
If the platform could not obtain the balance (a temporary failure of the source), the state is unknown, not "suspended": a read error must neither halt exchange nor hide a debt.
- Routes that do not charge the wallet (reads) work; the response carries the header
Wallet-State: unknown. - Paid operations are closed:
503 upstream_unavailablewithRetry-After. Retry later with the sameIdempotency-Key. - The "unknown" answer is cached for only a few seconds, so a return to normal operation is quick.
#How to resume exchange
- Open
topup_url(dashboard → Promotion → Economics → wallet) as an employee with the top-up permission and top up the wallet. For a tenant with a shared wallet, the top-up is made in the wallet's "home" cell. - The platform clears the state cache when funds are credited. The state in the API updates immediately on the server where the payment was processed, and on the others within 30 seconds at most.
- Check
GET /wallet:exchangeis nowactive. Deferred webhooks will go out on their own.
#Notifications to the tenant
When the exchange state changes, the tenant receives a message in its own linked channels (the tenant employees' Telegram; the platform's chat is not used):
- "Exchange suspended": on the transition to
suspended, with a link to top up; - "Exchange resumed": on the return to
active.
The message is sent once per state change, without repeats, in the tenant's language (Russian, English, Indonesian). The unknown state does not trigger notifications.
#Error codes
| Code | HTTP | When |
|---|---|---|
wallet_suspended | 402 | Exchange is halted |
upstream_unavailable | 503 | The wallet state is unknown, a paid operation is closed |
See the catalog and paid operations.