# Changelog

Every change to this API and these docs, newest first. Each entry says whether it can
break an integration that already works, and what — if anything — you need to do.

Machine-readable at [`/changelog.json`](/changelog.json): poll it, compare `version`,
and act on `breaking`. That feed is generated from the same source as this page, so it
cannot fall behind it.

## 8.1.0 — Card close: one call is enough

**07 Nov 2026, 4:40 PM IST**

Closing a card that still holds a balance used to take two `DELETE /v1/cards/{id}` calls: the first returned the balance and answered `202`, and only a second call, made after the return settled, actually closed the card. A client that read the `202` as success left the card open and the holder's refund waiting. We now finish the close ourselves within about a minute of the return settling. Until then `GET /v1/cards/{id}` carries `close_in_progress: true`.

- A close whose balance return has settled is finished by us — close fee, terminate, refund queue — without a second call.
- `GET /v1/cards/{id}` carries `close_in_progress: true` while a close has started and the card is not yet terminated.
- `DELETE /v1/cards/{id}` on a card closed through us answers `200` with the card.

**What you need to do:** Nothing to change if you already call close twice: the second call still works, and a `DELETE` on a card we already closed now answers `200` with the card instead of `409 card_state`. If your close screen treats the first response as done, make it wait for `status_normalized: closed` or the `card.status` webhook instead.

## 8.0.7 — Card transactions: every payment by webhook, codes documented

**05 Nov 2026, 4:30 PM IST**

Some card payment (`170`) `card.transaction` webhooks were not delivered until today; that is fixed on our side, and every payment, refund, card fee, ATM withdrawal and pre-authorization now arrives. The cards guide, section 7, lists every `type` and `status` code: 170 payment, 171 refund (with `originalTransactionId`), 172 card fee, 173 ATM, 174 pre-authorization; status 1 processing, 2 success, 3 failed.

- `card.transaction` is now delivered for card payments (`transType` 170); before today some were not.
- Cards guide, section 7, and the API reference: every `type` / `transType` and `status` / `transStatus` code with its meaning.

**What you need to do:** Handle `card.transaction` for type 170 as well as 171 if you only saw refunds before. Payments made before today that never reached you by webhook are in `GET /v1/cards/{id}/transactions`.

## 8.0.6 — Card details: one iframe, and we draw the card

**04 Nov 2026, 4:48 PM IST**

`reveal_url` and `card_pan_url` now open our card page. Put the link in one iframe and it does the rest: a card in your company name and logo while it loads, then the card itself fitted to the frame, with a pointer to the eye that shows the number. There is nothing to style or size on your side.

- `reveal_url` (`POST /v1/cards/{id}/reveal`) and `card_pan_url` (`GET /v1/cards/{id}`) open our card page.
- Cards guide, section 5: a single iframe snippet; opening the card in a new tab is no longer documented.

**What you need to do:** Use the one iframe in the cards guide, section 5, and remove any box or styling you added around the old page. If your page sends a Content-Security-Policy, `frame-src https:` covers the new page.

## 8.0.5 — Card details are shown on an embedded card page

**04 Nov 2026, 2:52 PM IST**

The card issuer's security policy changed for current cards (BINs 493 and 404): the card number, CVV and expiry can no longer be returned by any API. They are shown on a secure card page that you embed in an iframe — `card_pan_url` on `GET /v1/cards/{id}`, or `reveal_url` on `POST /v1/cards/{id}/reveal`. The cards guide now has a copy-paste iframe, a card-shaped version that hides the page's empty space, and the checklist the page needs.

- Cards guide, section 5: embed snippet, card-shaped snippet and checklist (HTTPS, no sandbox, `frame-src https:` when you send a CSP, no `Cross-Origin-Embedder-Policy: require-corp`, WebView in mobile apps).
- `POST /v1/cards/{id}/reveal` for these cards: `reveal_url` filled, `pan`, `cvv` and `expiry` null.

**What you need to do:** Show the card by embedding `card_pan_url` (or `reveal_url`) in an iframe on an HTTPS page, with a fresh link each time. Follow the checklist in the cards guide, section 5.

## 8.0.4 — Card reveal gives its hosted card page again

**04 Nov 2026, 12:17 PM IST**

Since 3 November, `POST /v1/cards/{id}/reveal` could answer 200 with neither the card details nor a `reveal_url`, and `card_pan_url` on `GET /v1/cards/{id}` came back `null`. The card issuer changed how its card page hands over the details, so the raw details could not be read, and the hosted-page link a reveal falls back to was not being built on our side. The link is back: a reveal now always carries one of the two forms, as the API reference says. For these cards the issuer now shows the details only on its embedded card page (see 8.0.5).

- `POST /v1/cards/{id}/reveal`: `reveal_url` is filled whenever `pan` is not.
- `GET /v1/cards/{id}`: `card_pan_url` is filled again for cards with a hosted card page.

**What you need to do:** When `pan` is `null`, open `reveal_url` for the cardholder. Do not store it: it expires in ten minutes.

## 8.0.3 — USDC on TRON reads as not swappable on the price sheet

**03 Nov 2026, 1:56 PM IST**

The public `GET /v1/pricing` sheet showed `swappable: true` for every asset, including USDC on TRON, which no route carries. It now reads `false` for USDC, as `GET /v1/assets` always did. A swap or an exchange that names USDC on TRON is refused with `400 invalid_pair` before any route is asked, as the fees page says.

- `swappable` on the public `GET /v1/pricing` sheet is `false` for `usdc_trc20`.
- A swap quote, swap or exchange naming `usdc_trc20` answers `400 invalid_pair`.

**What you need to do:** Leave any asset with `swappable: false` out of your swap picker, whichever endpoint you read it from.

## 8.0.2 — Card gas is priced for the send a load really makes

**01 Nov 2026, 4:55 PM IST** · :breaking:

`gas` on a card load or issue is the network's price for the on-chain send behind it. It was priced as if the card's funding address already held the token. It almost never does, and a send to an address that holds none takes about twice the energy. `gas` is now priced for that send, so it no longer comes in under what the send costs. At today's network price the figure is about the same as before; at quieter moments it is higher than it was.

- `gas` on `GET /v1/cards/products`, the load and issue quotes and the public `GET /v1/pricing` sheet: the funding send is priced for a recipient that holds no token.
- `gas_burning_trx` on the price sheet is the same send, burning TRX, to that recipient.

**What you need to do:** If you show a load or an issue before the customer confirms, read `gas` and `total` from the quote when you show it, not from a figure saved earlier.

## 8.0.1 — Card codes for Apple Pay and checkouts show in the dashboard

**31 Oct 2026, 3:00 PM IST**

A card's page in the dashboard now has a Verification code section. When the card is added to Apple Pay or Google Pay, or a checkout asks for a code, it appears there live. Your server and your dashboard each get one read of a code, so an integration that fetches codes no longer hides them from the dashboard.

- GET /v1/cards/{id}/otp: one read for your API key and a separate one for the dashboard. A payment code now also carries `merchant_name`, `amount` and `currency` when the issuer sends them.
- `kind` is now `wallet_provisioning` for the code that adds a card to Apple or Google Pay. It used to say `payment_3ds` for every code. The `card.wallet_otp` webhook's `otpKind` is corrected the same way.
- GET /v1/cards/{id} adds `otp_waiting` while a code is waiting: its kind and, for a payment, the shop and amount. It never includes the code.

## 8.0.0 — The INR buy rail is retired

**26 Oct 2026, 8:00 PM IST** · :breaking:

Buying USDT with rupees over UPI is gone: the buy quote, buy orders and their reads, and the UPI commission settings. The `/v1/buy/*` and `/v1/merchant/upi*` endpoints now answer `404`. Orders placed before today stay in your history and balances.

- Removed: `POST /v1/buy/quote`, `POST /v1/buy/orders`, `GET /v1/buy/orders/{id}`, `POST /v1/buy/orders/{id}/cancel`, `GET /v1/merchant/upi`, `PUT /v1/merchant/upi/fee` and `GET /v1/merchant/upi/orders`.
- Dashboard: the Fiat (UPI) page is gone. Earnings already made on past orders still show on the Profit page.

**What you need to do:** Stop calling `/v1/buy/*` and `/v1/merchant/upi*`, and remove any "buy with UPI" option from your product. Nothing else changes.

## 7.11.2 — KYC upload handles, a 400 for unknown assets, daily caps on paid calls

**16 Oct 2026, 5:30 PM IST**

POST /v1/cards/kyc/upload now answers with a `kycfile_…` handle instead of a storage link; pass it to POST /v1/cards/kyc exactly as before. Starting identity verifications and uploading card documents are capped per account per day. An asset id we do not support answers 400 unknown_asset rather than 500.

- file_url from POST /v1/cards/kyc/upload is a handle, valid 24 hours on your account; the KYC submit accepts it (or a URL you host) in id_url, id_back_url, file_url, face_url and selfie_url.
- 429 daily_limit_reached on POST /v1/kyc/start (new sessions only) and POST /v1/cards/kyc/upload past the daily cap.
- 400 unknown_asset for an asset id that is not on GET /v1/assets.

**What you need to do:** If you displayed the uploaded document from file_url, keep your own copy instead — the handle is not a link.

## 7.11.1 — Card close: the refund charge and timing on the price sheet

**23 Sep 2026, 3:55 PM IST**

GET /v1/pricing now carries close_refund on every card product: the card network's charge to release a closed card's balance (a percentage with a minimum, capped below a threshold) and how long the refund takes to arrive, typically and at most. The fees page's card table and close section show them; the close quote still does the exact sum for one card.

- New on each card product in GET /v1/pricing: close_refund { release_fee_percent, release_fee_min, release_fee_max, release_fee_cap_below, settlement_max_days, settlement_typical }.
- Fees page corrected: a close is always accepted (never refused for the issuer's cancellation charge), and our close fee is our own flat part plus the network figure.

## 7.11.0 — USDT ⇄ TRX swaps run on-chain on TRON, from a dollar up

**23 Sep 2026, 3:40 PM IST** · :breaking:

A swap between USDT and TRX is now traded on the TRON network itself, signed by the user's own address, so small amounts swap at the market rate (a 5 USDT swap used to have no route). The swap fee is now 0.5% with a $0.50 minimum, plus the swap's own network cost in the sold coin — a small swap costs a little more than the old flat fee, a large one far less.

- Swap fee for USDT and TRX: 0.5% (minimum $0.50) plus network_fee, all taken out of the sold amount. network_fee is a new field on POST /v1/swap/quote and on the order.
- A USDT ⇄ TRX order goes pending → sent → completed, or failed with the held amount returned in full. There is no swapping state and no refund from an outside party.
- receive_minimum is a hard floor: the trade reverts on-chain rather than pay less, and the order fails with nothing traded.
- An exchange out of USDT or TRX (to a coin outside TRON) pays the same 0.5% and minimum plus its own send's network cost.
- GET /v1/pricing: the swap block for USDT and TRX carries percent, min_fee, network_fee and total (the fee on a small swap).

**What you need to do:** Show fee and network_fee from the quote rather than a fixed figure. If you display a swap as in progress while it is swapping, treat sent as in progress too.

## 7.10.1 — The refund wallet’s surplus can be swept to the profit wallet

**20 Sep 2026, 12:20 PM IST**

Internal only: withdrawing a rail's float brings the close refunds and our own fees out together, so the fees land in the refund wallet. The admin can now send that surplus to the profit wallet in one action, capped at what the wallet holds beyond every holder it still owes.

- New admin route POST /admin/cards/refund-wallet/send-profit.

## 7.10.0 — A close is always accepted, and its refund is a state you can read

**20 Sep 2026, 11:45 AM IST** · :breaking:

DELETE /v1/cards/{id} no longer answers 409 card_refund_wallet_empty. The card's balance goes to the rail and the card terminates whatever the refund wallet holds; paying the holder on-chain is reported as close_refund.state on GET /v1/cards/{id} — waiting, approved, sending, paid, or held with a reason — so there is nothing for a customer to retry and nothing for you to translate into an error.

- 409 card_refund_wallet_empty is retired.
- New close_refund block on GET /v1/cards/{id}.

## 7.9.0 — An empty card can be closed

**20 Sep 2026, 11:05 AM IST** · :breaking:

A card holding less than the issuer's cancellation charge — an empty one included — now closes like any other. 409 card_close_underfunded is no longer raised, and minimum_card_balance on the close quote is always 0 (the new issuer_fee field carries what the issuer keeps). The floor was ours, not the rail's: the rail terminates a zero-balance card at once, and refusing meant asking somebody to load a card they wanted to be rid of.

- 409 card_close_underfunded is retired.
- close-quote: minimum_card_balance is always 0; new issuer_fee / issuer_fee_base.

## 7.8.2 — A close quote states the issuer’s settlement period

**20 Sep 2026, 10:25 AM IST**

The close quote carries refund_eta_max_days (45) and refund_eta_typical ("24–48 hours"). Cancelling a card opens the issuer's settlement period and the balance is not released until it ends; the issuer states it can run to 45 days, though it has been far quicker. Quote your customer the issuer's limit.

- New fields refund_eta_typical and refund_eta_max_days on the close quote.

## 7.8.1 — The close fee is 5.00 plus the network figure

**20 Sep 2026, 10:05 AM IST**

close_fee is now 5.00 and is our own charge only — the issuer's cancellation is kept on the card by the issuer, so it is no longer folded into our flat part (which double-billed it). The live network figure for the on-chain send is added as before, and an affiliate share is paid on the whole of our 5.00.

- close_fee 4.00 -> 5.00, ours alone.

## 7.8.0 — A close is paid from the balance it returns

**20 Sep 2026, 9:40 AM IST** · :breaking:

Closing a card no longer debits the holder's wallet balance. Our close fee, the network cost of the on-chain send, your own fee and the rail's release charge all come out of the balance the card returns, and the holder receives the rest — refund_quote on the close quote shows every line. A close therefore no longer fails because the wallet is empty, which is the normal state of a wallet whose owner is closing their card.

- close-quote: charged_to is now "refund"; refund_quote adds close_fee, gas and merchant_fee.
- A close refund is sent only once the card is fully closed, and pays the net.

## 7.7.1 — The rail float sweep is planned and watched

**20 Sep 2026, 9:10 AM IST**

Internal only, no API change for merchants: a worker now prices what sits in the card rail's merchant float and the cheapest way to withdraw it (the rail's cap applies per withdrawal, so splitting below the threshold costs a fraction), mails the operator once a day above a threshold, and the admin records each withdrawal made by hand.

- New worker rail-float-watch and admin routes GET/POST /admin/cards/float-sweep.

## 7.7.0 — A close quotes the exact refund

**20 Sep 2026, 8:40 AM IST**

GET /v1/cards/{id}/close-quote now carries refund_quote — the card's live balance, the issuer's cancellation fee that stays on the card, the card rail's charge for releasing the balance back on-chain, and the refund the holder receives. That rail charge is new: it was always paid, silently, by us. It is now taken from the returned balance, booked as a rail cost, and shown before the close so a holder can be told the exact figure.

- New refund_quote block on the close quote.
- The card rail's release charge is deducted from a close refund and carries no commission.

## 7.6.0 — Deposits name their sender

**19 Sep 2026, 9:00 PM IST**

Every deposit record carries from_address and the deposit.credited webhook carries fromAddress. For USDT and USDC it is the from of the token Transfer event to the user's address — the real payer, not the operator address that signed an exchange's batch payout. For TRX it is the sender. A token moved by transferFrom() is now detected too.

- New field from_address on GET /v1/users/{externalId}/deposits and GET /v1/deposits.
- New field fromAddress on the deposit.credited webhook.
- Deposits made with transferFrom() are credited.

## 7.5.4 — Deposits sent by a contract are credited

**19 Sep 2026, 7:50 PM IST**

USDT or USDC that reaches a user's address from inside another contract — a swap provider paying out, an exchange's batch withdrawal — is now detected and credited like any deposit, with the usual deposit.credited webhook. Before, only a direct token transfer was seen. Its output_index is numbered above 1,000,000,000.

- Contract-sent token deposits are detected and credited.

## 7.5.3 — Balances show the address alongside the ledger

**19 Sep 2026, 3:00 PM IST**

GET /v1/users/{externalId}/balances carries on_chain and on_chain_base per asset — what the user's own address holds right now, read live — next to the spendable amount. The two differ by design (fees settle to the profit wallet, earnings are credited from there); amount is what a payout may spend. null when the chain could not be read in time.

- New fields on_chain / on_chain_base on every Balance.

## 7.5.2 — Payouts to the user’s own address are refused

**19 Sep 2026, 1:30 PM IST**

POST /v1/withdrawals answers 400 self_transfer when the recipient is the user's own deposit address. Such a send moved nothing and spent the network fee; the coins that came back were not a deposit. Nothing is held on a refused request.

- New 400 error code self_transfer on POST /v1/withdrawals.

## 7.5.1 — Partial card unload retired

**14 Sep 2026, 9:05 PM IST** · :breaking:

POST /v1/cards/{id}/withdraw is retired and answers 410 card_unload_retired. An unload credited the holder's balance with coins that never reached their own address; the only way value leaves a card is the close, which returns the whole balance on-chain.

- `POST /v1/cards/{id}/withdraw` → `410 card_unload_retired`, always.

**What you need to do:** Remove any "withdraw from card" action from your product; offer "close card" instead.

## 7.5.0 — A card load pays its own way on-chain, or waits

**14 Sep 2026, 7:40 PM IST** · :breaking:

A load is sent from the holder's own address, and only when the coins are there. A balance the ledger shows but the chain does not yet — a close refund or a deposit still on its way — no longer loads a card from our float: POST /v1/cards/{id}/topup answers 409 card_coins_not_on_chain and nothing is charged.

- `409 card_coins_not_on_chain` on `POST /v1/cards/{id}/topup` and `POST /v1/cards`; `details.needed` says how much must be on-chain.

**What you need to do:** Handle `409 card_coins_not_on_chain` on a load (and on an issue with an opening balance): show its message and let the holder try again once their coins have arrived.

## 7.4.1 — A close is refused while its refund cannot be paid

**14 Sep 2026, 7:10 PM IST**

A card's balance goes back to the holder's own address on-chain within 7 working days of the close, from a wallet cryptoinfra funds by hand. When that wallet cannot pay the balance today, DELETE /v1/cards/{id} answers 409 card_refund_wallet_empty and the card is untouched. The close quote says the ETA (`refund_eta_working_days`).

- `409 card_refund_wallet_empty` on `DELETE /v1/cards/{id}`; `refund_eta_working_days` on the close quote.

**What you need to do:** Handle `409 card_refund_wallet_empty` on a close: show its message ("Card refund wallet balance is empty, please contact cryptoinfra.") and let the holder try again later.

## 7.4.0 — Close fee: a flat part plus the live network figure

**14 Sep 2026, 5:30 PM IST** · :breaking:

The close fee now has two parts: a flat part (our fee plus the issuer's cancellation charge billed to us) and the live network figure for returning the balance to the holder's own address on-chain. `GET /v1/cards/{id}/close-quote` shows both, `gas` with `dynamic`, and `total` is what the balance is debited. The cardholder terms sheet for the live BIN prices the 3-D Secure check as the issuer bills it.

- `gas`, `gas_base` and `dynamic` on `GET /v1/cards/{id}/close-quote`; `total` includes it.
- `close_fee` on the product and the price sheet is the flat part only.

**What you need to do:** If you show a close before the holder confirms, read `total` from the close quote rather than the product's `close_fee` alone — the network part is priced when you ask.

## 7.3.0 — A flat platform fee on every card load

**13 Sep 2026, 2:20 PM IST** · :breaking:

A card load now carries a flat platform fee beside its percentage, the way a payout carries one beside its network figure — so every movement of money except a deposit has the same flat part. The figure is on the product as `load_fee_flat`, on the public price sheet, and in the fee calculator; a load's charge rises by it.

- `load_fee_flat` on every card product (`GET /v1/cards/products`, the public `GET /v1/pricing` sheet): a flat platform fee added to the percentage or its floor.
- The price sheet also carries `network_fee_burning_trx` / `network_fee_burning_trx_fresh_recipient` on a withdrawal and `gas_burning_trx` on cards — what the same send would burn in a wallet without energy, for a "you save" comparison. The docs' new fee calculator shows it.
- Docs: a live fee calculator at /fee-calculator — any amount, payout / card load / card issue / deposit, your own fee on top, every figure from the price sheet at load and re-read on every change.

**What you need to do:** If you show a load's cost before the customer confirms, price it from `GET /v1/cards/products` (`load_fee_bps`, `load_fee_min`, `load_fee_flat`, `gas`) rather than from a percentage alone — the docs' fee calculator does the arithmetic.

## 7.2.0 — Your affiliate commission has a public ledger page

**12 Sep 2026, 10:36 PM IST**

A shareable page of your affiliate commission — total per asset and one line per operation that earned some, with our fee, your share and what you got — readable by anyone holding the link, no login. The link is on your Profit page; replace it from there if it gets around.

- `GET /v1/merchant/affiliate-ledger` returns the link (`/a/{token}` on the merchant origin); `POST /v1/merchant/affiliate-ledger/rotate` replaces it (dashboard owner, both codes). The page reads the public `GET /v1/a/{token}`.

## 7.1.0 — A card order carries the hash of its on-chain send

**12 Sep 2026, 9:29 PM IST**

`txid` on every card order and on the `card.topup` / `card.status` webhooks — the hash of the send from the cardholder's address to the card rail — so a receipt for a load can link to a block explorer the way one for a deposit or a payout already does. Null until the send is broadcast; a failed order keeps the hash of the send that reverted.

- `GET /v1/cards/orders/{id}` and the `orders[]` of `GET /v1/cards/orders` carry `txid`; `card.topup` and `card.status` payloads carry `txid`. Additive.

## 7.0.1 — A card load whose send reverts is sent again before it fails

**12 Sep 2026, 9:10 PM IST**

When the on-chain send behind a load reverts, the load is now re-sent once instead of failing at once. The order stays `funding` a little longer; the cardholder's charge is untouched throughout. Only if the second send reverts too does the order turn `failed` and refund the whole charge, as before.

- `GET /v1/cards/orders/{id}` may stay `funding` for a few minutes more than before while a send is re-issued; `error` then notes the first send and the new one. Nothing to handle — keep treating `202` as pending and `failed` as the refund.

## 7.0.0 — Fees, simple: the total, one live flag, one time

**12 Sep 2026, 4:48 PM IST** · :breaking:

Today's fee responses had grown a breakdown nobody needs to charge a customer — the fallback figure, the bill behind the figure, the margin floor. They are gone again. A fee response is the amounts, `dynamic` (live or approx) and when it was priced. The guarantees behind the number (priced live, never below what a send cost yesterday) still hold; they are inside the figure, not beside it.

- Removed from `GET /v1/fees`: `static_fallback`, `static_fallback_base`, `network_cost`, `recent_cost_floor`, `floored`. Kept: `priced_at`, `dynamic`.
- Removed from `GET /v1/withdrawal-fee-quote`: `static_fallback`, `recent_cost_floor`, `floored`. Kept: `dynamic`, `quoted_at`, `network_cost`, `recipient_priced`.
- Removed from both card quotes: `network_cost`, `recent_cost_floor`, `floored`. Kept: `dynamic`.
- Removed from `GET /v1/merchant/card-fees`: `gas_static_fallback`, `gas_network_cost`, `gas_recent_cost_floor`, `gas_floored`. Kept: `gas`, `gas_dynamic`, `gas_priced_at`.
- Removed from `GET /v1/pricing`: `recent_cost_floor`, `floored`, `gas_static_fallback`, `gas_recent_cost_floor`, `gas_floored`, `gas_priced_from`. Kept: everything that was there before today, plus `gas_dynamic`.
- Docs: no fee example carries a real number any more — the field is shown, the value is read from the quote. A Quickstart of six copy-paste calls opens the docs.

**What you need to do:** If you read `static_fallback`, `network_cost`, `recent_cost_floor` or `floored` on `GET /v1/fees` or the card quotes, or `gas_static_fallback`, `gas_network_cost`, `gas_recent_cost_floor`, `gas_floored` or `gas_priced_from` on the card tariff, the products list or the price sheet, stop — they no longer exist (all were added earlier today). `network_cost` stays on `GET /v1/withdrawal-fee-quote` only, where it always was. Show `total_fee` (payouts) or `total` (cards); label it from `dynamic`.

## 6.3.0 — A network fee is never below what a send cost yesterday

**12 Sep 2026, 3:57 PM IST**

Every network figure — the payout quote, the card gas line, `GET /v1/fees`, the public price sheet and the charge itself — is now lifted to the dearest confirmed send of the last 24 hours (energy rented plus TRX burned, priced and cushioned like a quote). The platform part stays a fixed 0.50 on top; the floor is what keeps it 0.50 on a day the market moves faster than the cushion.

- `recent_cost_floor` and `floored` on `GET /v1/withdrawal-fee-quote`, both card quotes, `GET /v1/fees`, `GET /v1/pricing` and (as `gas_recent_cost_floor` / `gas_floored`) on `GET /v1/merchant/card-fees`.
- A first-time sender's activation is charged to that sender only and never raises the floor for anyone else.

## 6.2.0 — Every fee says whether it is live, everywhere

**12 Sep 2026, 3:26 PM IST**

What 6.1.0 did for the card network fee now holds for every network figure the platform publishes: it carries whether it was priced live or is the static fallback, the fallback itself, the bill it was built from, and when it was priced. Nothing you mirror from us needs to be shown as a fixed charge again.

- `GET /v1/fees`: `priced_at` on the response; `withdrawal.static_fallback` and `withdrawal.network_cost`; `deposit.dynamic` and `deposit.network_cost`.
- `GET /v1/withdrawal-fee-quote`: `static_fallback` and `quoted_at` beside `dynamic` and `network_cost`.
- `POST /v1/swap/quote`, `POST /v1/exchange/quote` and `POST /v1/buy/quote` carry `quoted_at`.
- `GET /v1/cards/products` carries the `gas` inside every `issue_total`, with `gas_dynamic` and `gas_priced_at`.
- Dashboard: the profit-withdrawal quote labels the network fee live or approx. The fees guide has one table of these fields and where each appears.

## 6.1.0 — The card network fee says whether it is live

**12 Sep 2026, 3:14 PM IST**

`ours.load.gas` on the card tariff has always been priced live — the cost of the load's two TRON transfers plus 0.50, computed when you ask, exactly like a payout's `network_fee` — but nothing said so, and a merchant mirroring the figure showed it as a fixed charge. Now the tariff, both card quotes and the public price sheet carry whether the figure is live or the static fallback, and the bill it was built from.

- `GET /v1/merchant/card-fees`: `ours.load` gains `gas_dynamic`, `gas_priced_at`, `gas_static_fallback` and `gas_network_cost`. Label the figure "live" when `gas_dynamic` is true and "approx" otherwise; re-read it rather than printing it.
- `GET /v1/cards/topup-quote` and `GET /v1/cards/issue-quote` carry `dynamic` and `network_cost`, the same shape as `GET /v1/withdrawal-fee-quote`.
- `GET /v1/pricing` `cards` carries `gas_dynamic`, `gas_static_fallback` and `gas_priced_from`.
- Documented: `gas` on a card order and on the `card.*` webhooks is the figure actually charged, fixed when the order was created — reconcile a quote against it.
- Dashboard: Profit → Our rates and both card review screens label the network line live or approx.

## 6.0.1 — Payouts have no minimum amount

**12 Sep 2026, 1:34 PM IST**

A payout no longer has a per-asset minimum. How much you send is your decision; the one condition is that the balance covers the amount plus the fee. Only a zero or negative amount is refused.

- `POST /v1/withdrawals` accepts any positive amount. `amount_below_minimum` now means zero or negative only; `min_withdrawal` on `GET /v1/assets` is no longer a payout rule.

## 6.0.0 — Invoices are removed

**12 Sep 2026, 1:33 PM IST** · :breaking:

The invoice product is gone: creating, listing and reading invoices, the invoice settings, the hosted invoice page a customer paid on, and the Invoices page of the dashboard. Bill an order from a deposit address instead — a user is free and permanent, so an order can have its own — and credit it from `deposit.credited`.

- Removed: `/v1/invoices`, `/v1/invoices/{uuid}`, `/v1/invoices/by-order/{orderId}`, `/v1/invoices/{uuid}/payments`, `/v1/invoice-settings`, and the public `/v1/i/{uuid}`, `/payments` and `/logo` pages.
- Removed: the `invoice.paid`, `invoice.expired` and `invoice.late_payment` webhook events. Nine events remain and the contract lists all of them.
- Dashboard: the Invoices page is gone; Deposits shows the day's incoming money.

**What you need to do:** Anything calling `POST /v1/invoices`, `GET /v1/invoices/*` or `/v1/invoice-settings` gets a `404`; stop calling them. Handlers for `invoice.paid`, `invoice.expired` and `invoice.late_payment` can be removed — the events are never sent, and the test-fire no longer offers them. Money a customer still sends to an old invoice address is credited and moved to your own balance like any other deposit; nothing is lost.

## 5.1.0 — Swaps work again, and a coin's picture comes from us

**2026-09-06**

Two fixes on the exchange. Creating and listing swap orders had been failing since 4 September on a database column the code expected and the schema had dropped; both work again, and nothing was lost — no order was created while it was broken. And the picture of a payout coin now comes back from this API instead of the router's own storage, so it loads and it says nothing about who the router is.

- `POST /v1/swap/orders` and `GET /v1/swap/orders` answer again (they returned `500` from 2026-09-04 to 2026-09-06).
- New: `GET /v1/exchange/assets/{identifier}/logo` — the coin's picture, served from here.

**What you need to do:** If you render `logo_url` from `GET /v1/exchange/assets`, it is now a path on this API (`/v1/exchange/assets/{identifier}/logo`) rather than an outside URL, so fetch it with your API key like any other call. It answers an image and is cacheable for a week. The old value had been redacted on the way out and did not load, so nothing that worked before stops working.

## 5.0.0 — One network, one way to move money, one place for every job

**2026-09-06** · :breaking:

The platform is TRON only, and the parts nobody used are gone. Internal transfers, payment links, the fee-in-TRX payout option, the mobile second factor, verification tiers and the approval step are removed. A signup is live at once and waits only on its email; every payout is an on-chain withdrawal; invoices are how you bill an order; the dashboard shows each thing in one place. The endpoints that stay are unchanged.

- Removed from the contract: the Transfers tag (`POST /v1/transfers` and its reads); `/v1/payment-links/*`, the hosted `/pay` page and the `payment.received` event; `fee_mode` as a choice, with `trx_fee_option`, `fee_balance_insufficient`, `fee_mode_unavailable` and `invalid_fee_mode`; `GET /v1/merchant/verification`, `POST /v1/merchant/verification/start`, `verification_tier`, and `tier`, `tier1_daily_auto_usd` and the new-merchant rule under `rules` on `GET /v1/merchant/profit`; `account_pending` and `account_rejected`; `/merchant/account/mobile/{start,confirm}`.
- `Chain` is `tron` and nothing else: `POST /v1/users` returns one address, and `GET /v1/users/{externalId}/balances` no longer lists an empty balance on a retired chain. Every network fee is charged in the sent asset; `fee_asset_id` stays on the withdrawal, read-only, and names that asset.
- Two factors everywhere: the authenticator and a code emailed to the login. The password-reset flow is email only.
- Dashboard: Swap carries both destinations (to my balance, to an address); Settings → API keys is the one developer page (key, webhooks, API log); the cards Overview is the programme page; Invoices is a top-level entry; the Assets page and the currency tabs on Fiat are gone. Approval thresholds are set under Settings → Security and parked payouts are decided on Payouts.
- The dashboard's webhook page lists all twelve events, and the test fire can rehearse any of them.

**What you need to do:** Anything still calling `POST /v1/transfers` must call `POST /v1/withdrawals` instead (a transfer is now a `404`). Anything creating payment links must create invoices (`POST /v1/invoices`); `/v1/payment-links/*` is gone and `payment.received` is never sent. Stop sending `fee_mode` — it is accepted with any value and ignored. Stop handling `account_pending` and `account_rejected`: an account is `active`, `paused` or `suspended`, and a key whose owner login never verified its email answers `403 email_unverified`. `/merchant/account/mobile/*` and the mobile fields on `/merchant-auth/me` are gone. Treat `chain` as `tron` everywhere. Exchange endpoints are unchanged; on the dashboard the Exchange page is now the "to an address" choice on Swap, and `/exchange` redirects there.

## 4.10.0 — Verification tiers: approval opens the API, verification the full limits

**2026-09-05**

Every account now has a tier. Tier 1 comes with approval and lets profit withdrawals go on their own up to a small daily figure; tier 2 — the business verified in the browser on our verification rail, or by our team by hand — lifts them to the ordinary limits. Settings → Verification shows where you stand and what each step opens.

- `GET /v1/merchant/verification` and `POST /v1/merchant/verification/start` (`503 verification_unavailable` while the flow is not open); `verification_tier` on `GET /merchant-auth/me`; `tier` and `tier1_daily_auto_usd` under `rules` on `GET /v1/merchant/profit`.
- A held profit withdrawal from an unverified business says so in its `desk_note`.

## 4.9.0 — Signup guarded; linked accounts and out-of-line days are flagged for a look

**2026-09-05**

Signup refuses disposable mailboxes and, when configured, asks for a bot check. Accounts that share a network, a Telegram username, a company e-mail domain, a payout address or their customers with another merchant — or a day of earnings far above their own history — are flagged for us to look at. A flag holds a profit withdrawal for a look, with the reason on the request; it never closes an account on its own.

- `400 email_disposable`, `400 bot_check_failed` and `503 bot_check_unavailable` from signup; `turnstile_token` accepted when the page showed the check.
- A held profit withdrawal may now carry `risk flag: …` in its `desk_note`.

## 4.8.0 — Profit matures before it is withdrawable; reversed operations claw back

**2026-09-05** · :breaking:

An earning still lands in your balance the moment the customer pays, but counts as profit available to withdraw only after a wait — a short one for what was earned on-chain, a longer one for UPI and cards, which can still be refunded or disputed. If an operation comes back, the earning on it comes back too: what your balance holds is taken at once, the rest is netted from your next earnings, and no profit withdrawal goes until it is settled.

- `profit_pending` (with the moment it matures), `profit_owed`, `reversals` and `maturity` on `GET /v1/merchant/profit`; `available_at`, `matured` and `reversed` on each earning row.
- `409 profit_not_matured` and `409 profit_owed` from `POST /v1/merchant/profit/withdraw`.
- A swap that fails or is refunded by the exchange reverses your fee and your share on its own; a UPI reversal or a rail refund is reversed by cryptoinfra with the reason on the row.

**What you need to do:** Profit earned in the last day (on-chain) or three (UPI, cards) shows as pending and cannot be withdrawn yet; `profit_available` on `GET /v1/merchant/profit` is smaller than before by that much. Nothing else changes.

## 4.7.0 — Profit withdrawals: the rules that decide on their own

**2026-09-05**

A profit withdrawal now goes on its own when it is within the limits, and waits for a look when it is large, from a merchant approved only days ago, to an address never paid before, or past the day's cap. The dialog shows the figures that apply, the Profit page shows why one waits, and owner and finance logins get a mail at each turn.

- `409 profit_below_minimum`, `profit_too_many_open` and `profit_cooldown` from `POST /v1/merchant/profit/withdraw`; `rules` and per-request `desk_note` on `GET /v1/merchant/profit`.
- An address never paid before waits a fixed number of hours, then the request goes on its own if the other rules still pass.

## 4.6.0 — Profit withdrawals are requests, listed on your Profit page

**2026-09-04**

A profit withdrawal is now a request to cryptoinfra, approved there — automatically as a rule — before it leaves our profit wallet. Your Profit page lists every request with where it stands, and shows what is on its way out.

- `withdrawals` and `in_flight` on `GET /v1/merchant/profit`; a request that is refused returns the whole hold to your profit, with the reason on it.
- A request waits as `awaiting_approval`; your own payout approvals never list it.

## 4.5.0 — Withdraw your profit from the Profit page

**2026-09-04**

Profit → Withdraw profit sends what you have earned — commission and your own fees, less what you already withdrew — to any address, from our profit wallet in real time, for our ordinary withdrawal fee. The Profit page now shows the profit still available rather than your whole balance.

- Your own fee on a customer's payout, deposit or swap now travels on-chain to our profit wallet with ours as the operation settles, so the wallet always holds what it pays you out.
- `profit_available` and `withdrawn` on `GET /v1/merchant/profit`; a profit withdrawal is a withdrawal of kind `merchant_profit` and refuses past the earnings with `409 profit_insufficient`.

## 4.4.0 — Affiliate commission: a share of our fee, on every operation, nothing to set

**2026-09-04**

You now earn two ways, together or alone. A share of OUR platform fee on every payout, swap and card your customers make is paid to your balance the moment our fee settles — automatically, with nothing to set. Your own fee stays what it was: a price above ours, yours entirely. The Profit page shows both.

- `affiliate` on `GET /v1/fees` — `enabled`, `share_bps`, `share_percent` — and on the public price sheet.
- A commission is a journal of its own; on the Profit page it reads as source `affiliate` with the operation it came from as its reference.
- The share is of our fee, never of a cost: network bills, the rail's take and card close fees carry none; deposits are free and carry none.

## 4.3.1 — Four new integration guides, and a menu organised by what you are building

**2026-09-04**

The documentation now walks every money flow once, in order: Payouts, Swaps and exchange, Payment links and invoices, and Buy with UPI join Users and deposits and Cards under one Integrate section. The menu reads Start here, Integrate, Conventions, Pricing, Resources, and the API reference is grouped by product. The front page's quickstart shows the request bodies the API actually accepts.

- New guides: Payouts, Swaps and exchange, Payment links and invoices, Buy with UPI.
- Stale wording removed: a user's coins sit at their own address (nothing is swept), and a payout's `fee` is the live network cost, not one flat price.

## 4.3.0 — A public price sheet, and docs that read it

**2026-09-04**

`GET /v1/pricing` needs no key and returns every price we charge, live: the platform fee and the network figures per asset, and every card product's terms. The fees documentation now renders its tables from it at page load, so a price on a docs page is the price of that minute, never a number typed into a page.

- New `GET /v1/pricing` (no authentication, cached for a minute). Your own fees stay on `GET /v1/fees` with your key; the exact charge for one send stays on `GET /v1/withdrawal-fee-quote`.
- The Fees and Cards pages show live tables instead of typed numbers. Text readers of the docs see the endpoint to call instead.

## 4.2.0 — Deposits are free

**2026-09-04**

A deposit to an address we issue is now credited in full. The platform's deposit fee — the flat part and the network part that came with it — is gone on every asset. Nothing changes in the API's shape: `deposit.total` on `GET /v1/fees` reads 0, and a deposit's `fee` and `network_fee` are 0. Your own fee on deposits, if you set one, is unchanged.

- `GET /v1/fees`: `deposit.total`, `deposit.flat` and `deposit.network_fee` are 0 for every asset.
- A deposit's `amount_credited` equals `amount` unless you charge your own `merchant_fee`. `min_deposit` (2 USDT) still applies as a dust floor.
- Payment links and invoices receive on the same terms. An invoice's fee split now has nothing to split.

## 4.1.0 — Your own fee on deposits, payouts and swaps; a Profit page

**2026-09-04**

You can now charge your own fee — a percentage, a flat amount, or both, with no ceiling — on deposits, payouts and swaps, as you already could on cards and UPI. It is charged to your customer and credited to your own balance in the same movement. The dashboard's new Profit page shows everything you earn, by source, with a Withdraw button; Your fees are set there in one place.

- `merchant_fee` (and `merchant_fee_base`) on deposits, on the withdrawal quote and the withdrawal, and on swap quotes and orders — 0 unless you set one. A deposit's `amount_credited` and a quote's `total_fee` include it.
- GET /v1/fees carries `merchant_fee` (your percent and flat) beside ours for deposit, withdrawal and swap.
- The `deposit.credited` webhook carries `fee`, `merchantFee` and `amountCredited`.
- Dashboard: Profit page (earned all time, last 30 days, available to withdraw, by source, every earning) and Your fees; `GET /v1/merchant/profit`, `GET/PUT/DELETE /v1/merchant/fees/{operation}`.
- The ceilings on card and UPI fees are removed.

## 4.0.0 — Fees are now the live network cost plus a flat 0.50

**2026-09-04** · :breaking:

One rule for every fee. The network part is what the send actually costs us on-chain, priced at the moment you ask; the platform part is a flat 0.50. A payout to an address that already holds USDT quotes about 1.45–2.10 today, one to an address that does not 2.10–3.05, and a user's first send from a new address adds about 0.80 once. Deposits are 0.50 plus the cost of taking it (about 1.05–1.30). Swaps are 2.00 flat.

- GET /v1/withdrawal-fee-quote prices the exact send: `network_fee` is live (`dynamic: true`), takes an optional `user_id` so a first send's activation is included, and returns `network_cost` (energy, bandwidth, activation in TRX, the TRX price, the number of transfers) so you can explain the figure.
- GET /v1/fees: `withdrawal.network_fee` is today's figure for a recipient that holds the token; `network_fee_fresh_recipient`, `first_send_activation_fee` and `total` were added; `deposit.network_fee` and `deposit.total` were added.
- Deposits: the fee is the 0.50 platform part plus the network cost of taking it; deposit rows carry `network_fee`.
- Swaps and exchanges: 2.00 flat (0.50 + a fixed 1.50 network figure).
- When a live price cannot be read, a payout is charged the old flat 1.50 and the quote says `dynamic: false`; a deposit's network part falls back to 0.50.

**What you need to do:** Never carry a fee figure in your own code. Call GET /v1/withdrawal-fee-quote with `to_address` and `user_id` before every payout and show the customer `total_fee`; the figure it returns is the one charged. Read deposit fees from the `deposit.credited` webhook or the deposit row (`fee`, and `network_fee` for the network part). GET /v1/fees now carries today's figures, not a constant.

## 3.0.1 — A card still being issued answers 409 card_issuing

**2026-09-03**

For the minute or two after POST /v1/cards, the balance and reveal calls used to surface the rail's own refusal — a 409 with a message nobody could act on, or a 200 holding neither pan nor reveal_url. They now answer 409 card_issuing until the card is active.

- GET /v1/cards/{id}/balance and POST /v1/cards/{id}/reveal answer 409 card_issuing while the card's status_normalized is issuing; details.card_id names the card. Poll GET /v1/cards/{id} or wait for the card.status webhook, then retry.

## 3.0.0 — Card pricing: fees on top of the amount, $2.00 to issue, $1.50 to close

**2026-09-03** · :breaking:

Every card fee is charged on top of the amount: a $100 load puts $100.00 on the card and debits $102.90; the opening balance on an issue lands in full. Our issue fee on a virtual card is $2.00 and our close fee is $1.50 — the issuer's own $2.00 stays on the card, so closing costs the cardholder $3.50 all in.

- POST /v1/cards/{id}/topup puts exactly `amount` on the card and debits amount + fee + gas + merchant_fee from the cardholder.
- On GET /v1/cards/topup-quote, `total` is that debit and `credited` equals `amount`.
- On GET /v1/cards/issue-quote, `total` is our issue fee + yours + initial_amount + its load fee and gas, and `opening_credited` equals initial_amount.
- Our issue fee on Core-1 BIN 493 Virtual is $2.00. Issuing at the $5.00 minimum costs the cardholder $8.57. The physical issue fee and the load rate are unchanged.
- Our close fee is $1.50 on every BIN, from the cardholder's balance when the card terminates; the close_fee order on DELETE /v1/cards/{id} carries it. A card holding exactly the issuer's $2.00 needs $1.50 in the balance to close.
- GET /v1/cards/products returns close_fee and close_fee_base per product, beside issue_fee and load_fee_bps.

**What you need to do:** Read `total` from GET /v1/cards/topup-quote, GET /v1/cards/issue-quote and GET /v1/cards/{id}/close-quote before every operation — it is the cardholder's debit, and a balance short of it is refused with 409 insufficient_balance. `credited` on a top-up quote equals `amount`. Never carry a fee amount in your own code: GET /v1/cards/products has issue_fee, issue_total, load_fee_bps and close_fee per product.

## 2.1.0 — A payout leaves your customer's own deposit address

**2026-09-03**

The address a payout is sent FROM has changed. Money used to leave a shared platform wallet; it now leaves the deposit address of the customer whose balance is being spent, when that address holds enough, and the shared wallet otherwise. Nothing about the API, the amounts or the fees changes — only the sender you see on-chain.

- A withdrawal, and the send leg of a swap, an exchange or a card load, leaves the deposit address of the customer whose balance pays for it when that address holds enough; otherwise it leaves the shared wallet as before. The txid, the amounts and every fee are unchanged.

**What you need to do:** If you reconcile by the sending address, or if anyone downstream allow-lists one, stop expecting a single fixed sender. Match on the txid you already get back from GET /v1/withdrawals instead. The same applies to the on-chain leg of a swap, an exchange and a card load.

## 1.9.0 — One flat fee, and no percentages

**2026-09-03** · :breaking:

Every fee is a flat amount per asset — no percentages anywhere. Deposits and swaps carry one flat fee each; the per-asset swap minimums are gone with them.

- Deposit fee is a flat 1.00 USDT / 1.00 USDC, charged out of the incoming amount: a 100 USDT deposit credits 99.00. TRX and BTC carry their own flat amounts — GET /v1/fees.
- Swap and exchange fees are flat amounts per asset; the per-asset minimums (1.6 USDT and friends) are removed.
- min_deposit is 2 USDT / 2 USDC / 2 TRX / 2000 sat. Below it a deposit is ignored_dust and never credited.

**What you need to do:** Read the effective schedule from GET /v1/fees rather than computing a percentage. A deposit below min_deposit is ignored_dust and never credited.

## 1.8.1 — A card must hold its own close fee

**2026-09-03** · :breaking:

Closing a card that holds less than the issuer's cancellation fee is refused with 409 card_close_underfunded instead of closing anyway. The close quote now says the least the card must hold.

- DELETE /v1/cards/{id} answers 409 card_close_underfunded when the card holds less than the issuer's cancellation fee. No card cost is paid by anyone but the cardholder.
- GET /v1/cards/{id}/close-quote carries minimum_card_balance and minimum_card_balance_base.

**What you need to do:** Before DELETE /v1/cards/{id}, read minimum_card_balance from GET /v1/cards/{id}/close-quote and load the card to it if it is short. Handle 409 card_close_underfunded — details carry minimum_card_balance and card_balance.

## 1.8.0 — Every movement of money is on-chain

**2026-09-03** · :breaking:

Internal transfers are retired. POST /v1/transfers answers 410 transfers_removed to every call; the transfers you made before today are still readable. Pay out with POST /v1/withdrawals — to one of your own users or to any address — priced with our fee and the network's gas. The card programme read no longer carries a deposit address, and reading it no longer opens a programme.

- POST /v1/transfers answers 410 transfers_removed and creates nothing. GET /v1/transfers and GET /v1/transfers/{id} still return the transfers made before 2026-09-03. The dashboard's Transfers page is gone.
- GET /v1/merchant/card-program no longer returns address and no longer opens a programme as a side effect; it carries a boolean opened, and describes an unopened programme with a zero balance. Opening one is POST /v1/merchant/card-programs, as before.
- The inbound chain-data webhook is no longer listed in the API reference; it was never a merchant endpoint.

**What you need to do:** Replace every POST /v1/transfers with a POST /v1/withdrawals to the recipient's deposit address, and budget the withdrawal fee where a transfer used to be free. If you read address from GET /v1/merchant/card-program, stop — it is gone, and nothing needs it.

## 1.7.2 — Which assets can be swapped, a pausable buy rail, and straighter answers

**2026-09-02** · :breaking:

GET /v1/assets now says which assets can be swapped or exchanged, and a swap or exchange that names one that cannot is refused before anything is charged — USDC on TRON is the one today. The operator can pause the INR buy rail; while it is paused a new quote or order answers 403 buy_disabled and an order already paid still settles. A reused idempotency_key with a different payload on a swap or exchange order is a 409 now, GET /v1/users says when its count was cut off, and setting your UPI fee from the dashboard asks for both codes like every other change there.

- GET /v1/assets carries a boolean swappable. POST /v1/swap/quote, /v1/swap/orders, /v1/exchange/quote and /v1/exchange/orders answer 400 invalid_pair for an asset marked false, or one that is disabled; usdc_trc20 is false today because no route carries it. Deposits and withdrawals of it are unchanged.
- The INR buy rail can be paused by the operator. POST /v1/buy/quote and POST /v1/buy/orders answer 403 buy_disabled while it is paused; an order already paid is settled regardless.
- Replaying an idempotency_key on POST /v1/swap/orders or /v1/exchange/orders with a different payload answers 409 idempotency_conflict. The same payload still returns the existing order.
- GET /v1/users returns a boolean truncated. The list is scanned in one pass of at most 2000 users; when truncated is true, total is that cap, not your count.
- PUT /v1/merchant/upi/fee from a dashboard session takes totp and email_code, like every other dashboard change. With an API key it sets the fee directly, as before.
- When the operator rotates your API key, every owner login is emailed. The previous key stops working at that moment.

**What you need to do:** Drop usdc_trc20 from any swap or exchange picker — better, read swappable from GET /v1/assets and offer only what is true. Treat 403 buy_disabled as a pause, not a broken integration: back off and try again later. Handle 409 idempotency_conflict on POST /v1/swap/orders and /v1/exchange/orders the way you already do on withdrawals. If you show total from GET /v1/users, check truncated first.

## 1.7.1 — A user ID is a number

**2026-09-02** · :breaking:

POST /v1/users now takes a numeric external_user_id of 5 to 10 digits and refuses anything else with 400 validation_error. Users you already created keep their ids and keep working; this is the rule for new ones.

- external_user_id on POST /v1/users must match ^[0-9]{5,10}$.

**What you need to do:** If your integration sends letters or symbols in external_user_id, change it to a 5 to 10 digit number before creating new users.

## 1.7.0 — Accounts are approved before they go live

**2026-09-02**

A new account signs up with an email, a password and a Telegram username, verifies the email, and can then sign in, add webhooks and generate its API key from the dashboard — signup never shows one. Teammates can be invited once the account is approved. The API and every money feature open once our team has approved the account; until then an API key answers `403 account_pending`. An operator can also pause, reject or suspend an account; each decision is emailed to the owner. Password reset sends its code to your verified email address, and changes made on the dashboard ask for an emailed code alongside the authenticator.

- Every request on an API key of an account that is not yet approved answers 403 account_pending; an account on hold answers 403 account_paused, one that was not approved 403 account_rejected. Do not retry; the dashboard shows the state.
- A suspended account's key answers 403 account_suspended. It was 401 unauthorized.
- Signup asks for a Telegram username and verifies the email before the first sign-in. No API key is shown at signup — an owner or developer login generates it from the dashboard, with the authenticator code and the emailed code.
- Password reset sends its code to your verified email address. SMS is used only if email is switched off platform-wide.

## 1.6.2 — A rail that cannot pay says so first, and a close takes a moment

**2026-09-02**

A card issue or load the rail cannot fund is refused with 503 rail_unfunded before anything is charged. The minimum opening balance is quoted per product, with the product's own activation minimum folded in. Closing a card is its own order, close_fee, and DELETE /v1/cards/{id} answers 202 card_close_in_progress while it runs.

- POST /v1/cards and POST /v1/cards/{id}/topup answer 503 rail_unfunded when the rail has no funding wallet on file or the hot wallet's balance is not known. Nothing is charged; it is an operator matter, not a request to correct.
- GET /v1/cards/products and GET /v1/cards/issue-quote return min_opening_balance per product — a physical card's activation minimum differs from a virtual one's — and the figure is the one the charge path uses.
- A card close creates an order of kind close_fee. DELETE /v1/cards/{id} answers 202 card_close_in_progress with card_id and order_id while the residual is being returned; call it again once the order settles.

**What you need to do:** Handle 503 rail_unfunded like any 503 — nothing was charged, come back later — and treat 202 card_close_in_progress on DELETE /v1/cards/{id} as pending, not failure.

## 1.6.1 — A card is issued for a user, from what that user holds

**2026-09-02**

GET /v1/cards/issue-quote previews what issuing a card costs the cardholder, priced by the same code that charges. A card is issued FOR one of your users and only from that user's own balance; when it is short, POST /v1/cards says so before anything is charged. The dashboard's Issue a card checks the cardholder's balance at every step, and the "Top up" a programme used to have is gone from the Card Issuing overview.

- GET /v1/cards/issue-quote returns initial_amount, fee, gas, merchant_fee and total. Read total.
- 409 insufficient_balance now carries a message saying whose balance is short.

## 1.6.0 — The cardholder pays, and every load goes on-chain

**2026-09-02** · :breaking:

Card issue, every load and the close now come out of the cardholder's own balance. A load is sent on-chain to the card rail and the card is funded once that send is confirmed — minutes, not seconds. You can add your own fee on top of ours; it is charged to the cardholder and lands in your own balance.

- payer_user_id is gone from POST /v1/cards, /topup and /withdraw. Sending it is a 400 payer_removed. The holder always pays.
- Orders have a new first stage, funding, while the money is on its way to the rail on-chain; then requested, sent, confirmed as before. Expect minutes between 202 and confirmed.
- GET /v1/cards/topup-quote returns fee, gas, merchant_fee and total; total is the cardholder's debit. Pass card_id and user_id to price a specific card.
- GET /v1/cards/products returns issue_total (everything the holder pays to issue at the minimum opening balance) and merchant_issue_fee.
- New GET /v1/cards/{id}/close-quote, and GET/PUT/DELETE /v1/merchant/card-fees/{issue|load|close} to set your own percent and/or flat fee on top of ours.
- card.* webhooks carry gas, merchantFee and total; card orders carry gas, merchant_fee, total and sent_on_chain.
- POST /v1/merchant/card-program/fund is gone; card programme balances are no longer drawn on. What was prepaid is returned to your own balance.

**What you need to do:** Stop sending payer_user_id and stop moving money from your customer to your treasury before a load — charge nothing yourself; mirror the card.topup / card.status webhooks, which carry the full breakdown. Treat funding like sent: pending, never a failure. Read total from the quotes.

## 1.5.0 — Merchant markups are gone; UPI keeps its commission

**2026-09-02** · :breaking:

You price your own customers on your side; cryptoinfra charges only its own fee. The per-operation markups, the Pricing page, the Profit page and its payout were removed. Your UPI buy commission now lands straight in your own balance.

- GET/PUT/DELETE /v1/merchant/pricing and GET/POST /v1/merchant/profit* are gone (404).
- The merchant_fee field is gone from deposit.credited, from card.* webhook payloads and card orders, from GET /v1/withdrawal-fee-quote and from GET /v1/cards/topup-quote. merchant_issue_fee is gone from GET /v1/cards/products. Swap orders no longer carry merchant_fee_base.
- amount_credited on a deposit is amount minus our fee again, nothing else.
- UPI buy keeps its markup, but the commission is credited to your own balance at settlement instead of a separate earnings account. A tenant with a markup and no own-balance account is refused at order time (409 no_own_account) rather than having the money land anywhere else.

**What you need to do:** If you parse any of the removed fields, drop them. If you set a markup, it no longer applies — your customers pay exactly the rates on the fees page. Everything else is unchanged.

## 1.2.0 — One flat withdrawal fee on TRON — 1.50, gas included

**2026-09-01** · :breaking:

The TRON withdrawal fee is now a flat 1.50 USDT/USDC and it covers the network gas. It was 3.50, priced as a worst-case gas estimate.

- The fee deducted from a TRC20 withdrawal is 1.50 USDT or 1.50 USDC.
- It includes the TRON energy and bandwidth the transfer consumes. There is nothing to add on top and no TRX to hold.
- It does not vary with the recipient. A first-time recipient costs us about twice the energy of an existing holder; that difference is absorbed rather than passed on.
- There is no prepaid gas pot and nothing to switch on: gas is inside the fee.

**What you need to do:** If you hard-coded 3.50 as the withdrawal fee, change it, or better, read withdraw_fee from GET /v1/assets at runtime. If you used the Gas Saver endpoints under /v1/merchant/gas, they now return 404 — you no longer need them.

## 1.1.0 — A merchant can hold more than one card programme

**2026-09-01**

Card programmes ("cores") let one merchant run several card balances, each with its own funding, pricing and cards.

- Every card carries core_id and bin. Read them as a PAIR — a BIN number is unique only inside its issuer.
- POST /v1/cards accepts core_id. With one programme open it is optional; with two, omitting it is refused with card_core_required rather than guessed.
- GET /v1/cards/products accepts ?core_id= and returns each row priced for that programme.

**What you need to do:** Nothing today if you hold one programme. Send core_id on POST /v1/cards before you open a second one.
