API changelog
All changes to the public API: new routes, fields, webhook events, and error codes. New entries are added at the top; dates are in UTC. A change that breaks compatibility is marked with the word breaking and explains what the integrator must do.
2026-10-14, an amount in a stablecoin is not requoted, and the previous amount counts for 10 minutes
- A token pegged to the US dollar (
USDT_*,USDC_*): the amount of an invoice priced in dollars, fixed at the choice, holds until the invoice's deadline and is no longer requoted after the quote window, nor is the rest after a partial payment.quote.expires_atof such an invoice is the invoice'sexpires_at, also for an invoice chosen earlier. The amount used to move by cents every 30 minutes, and a customer who paid exactly what was shown gotpartially_paid. - Other tokens are requoted after the window as before, but a transfer in a block no later than 10 minutes after the end of the previous quote's window is judged against that quote's amount as well. If it covers it, the invoice becomes
confirmed, andamount_requested,amount_expectedandquote(with itsnumber) are the previous quote's again. See quote window. - No field or error code changed.
2026-10-14, a rest below the minimum deposit pays the invoice
- A transfer below the minimum deposit of its asset is no longer lost when it is the rest of a
per_invoiceinvoice: once a payment to the invoice has arrived andamount_expectedis not reached yet, a transfer in the invoice's asset to its address is a payment (kind: payment) of any amount above zero. It used to be dust, and an invoice a few cents short stayedunderpaid. See partial payment. - The first transfer to an invoice, a transfer after the amount is reached, a zero transfer, and a transfer to a
per_useraddress below the minimum deposit are still dust. - No field or error code changed.
2026-10-14, native coins sent by smart contracts are credited
- A native coin of an EVM network sent by a smart contract is now credited — from a smart-contract wallet, a multisig, an exchange's withdrawal: it pays an invoice, is listed by
GET /v1/deposits/{tx_hash}and sendsdeposit.*webhooks like a plain transfer from a wallet. The service finds it by tracing the block and credits it only when the trace accounts for the whole rise of the address's balance; one it cannot account for is still resolved by the operator — see what arrives at an address. - Such a transfer has
log_index=-2,-3and so on, as a transfer a contract made on TRON already has. One transaction can carry several of them and its own value with-1. Check your integration if it assumes thatlog_indexof a native coin is always-1, or that a transaction pays an address once. - Routes, fields, webhook bodies and error codes have not changed. The decision — ADR-0055.
2026-10-14, the payment page estimates the wait for confirmations
- New field
confirmation_estimateinGET /checkout/v1/invoices/{id}and in the answer ofPOST /checkout/v1/invoices/{id}/select: while a transfer waits for confirmations, how many blocks it still needs, how many seconds that is by the block time of the network, and whether the network also waits for a final block (payment page). The page shows it as "About N minutes left" instead of promising about a minute on every network.
2026-10-14, a native coin has no token standard
token_standardisnullfor a native coin inGET /checkout/v1/invoices/{id}and in the answer ofPOST /checkout/v1/invoices/{id}/select, both for the invoice and for each of itschoices: BNB is not a BEP-20 token, SOL is not an SPL token (payment page). Before, it named the standard of the network for every asset.
2026-10-06, a price in dollars in the asset you name
- New field
price_currencyinPOST /v1/invoices. The one value isUSD, and only together withasset. With it,amountis a price in dollars rather than a number of coins: the service quotes it into the named asset at its own rate (ADR-0070, a price in dollars in the asset you name). - The answer to the creation is ready to be paid:
address,amount_expected,price,quote.rateandexpires_atcome back from thePOSTitself, with no checkout page and no choice by the customer. - The field is optional and the old meaning stands: without
price_currency,amountis still an amount in units of the asset. Existing integrations need no change. price_currencywithoutassetis refused (400 validation_failed): there theamountis a price in dollars already.- No rate, no invoice:
503 rate_unavailable, and nothing is left in the database. - New route
GET /v1/rates: for every asset of the project, the rate an invoice would use (the market rate less the service markdown), the market rate itself, the markdown percent, when it was observed and whether it is fresh. So that «≈ 0.45 SOL» beside a price matches the amount of the invoice.
2026-10-05, an integration configures its project with its own key
- New routes
GETandPATCH /v1/project,GETandPUT /v1/project/assets. An integration — or an assistant given nothing but an API key — sets the webhook URL, the return addresses and the tokens it accepts itself. Nobody has to open the dashboard (ADR-0067, authentication). - These routes never return the webhook signing secret. It is shown once, when the project is created; an API key is not access to the secret.
- The webhook URL can be set and replaced, never removed:
nullanswers400. Events already queued need somewhere to go. - The address mode and the custody mode are not changed here: an operator allows them.
2026-10-05, the address mode is allowed per project
- A new error code
422 mode_not_allowedonPOST /v1/addresses,POST /v1/invoicesandPOST /checkout/v1/invoices/{id}/select: the address mode of this project is not allowed to it. An operator allows a restricted mode per project rather than for the whole platform at once (ADR-0066). The older422 address_mode_disabledstays and means something else: the platform paused the mode for everybody. - The address mode of a project can now be changed. It used to be chosen once, at creation. Now an operator allows the mode and the project's owner switches to it in the cabinet. The change decides where the next payment is collected, never where an earlier one went: addresses already issued keep their own mode, keep being credited and keep being swept.
- A project cannot be created in
per_userfrom the cabinet:422 mode_not_allowed. Create it inper_invoiceand switch once an operator has allowed the mode.
2026-10-05, Solana
Solana is the fourth family of networks the service works with. No route, field or webhook event changed shape; what changed is the set of networks and assets the existing ones can carry.
- Payments work on ten networks:
solanajoinstron,ethereum,bsc,polygon,base,arbitrum,optimism,avalancheandton. Its assets areUSDT_SOLANA,USDC_SOLANAand the native coinSOL_SOLANA, each needing 1 confirmation; the mints and decimals are in assets on Solana. - There are no payouts on Solana, as there are none on the EVM networks or TON:
422 asset_not_payable. - A
tx_hashof a Solana transfer is not a hexadecimal hash. It is the first signature of the transaction: 86 to 88 base58 characters, whose case carries meaning and must be preserved. A column sized for 64 hexadecimal digits will not hold it, and lower-casing it destroys the value. This affectsGET /v1/deposits/{tx_hash}, thedepositsof an invoice and thedeposit.*webhooks — see addresses on Solana. - A Solana address is base58 of 32 to 44 characters and is case-sensitive too. Store, compare and display it exactly as returned; never normalise its case.
wallet_urlon the checkout page staysnullon Solana: the service builds a wallet deep link on TON only — see read an invoice for the page.- A token on Solana is held in an associated token account rather than at the address itself, which the service creates and watches. A customer's wallet pays to the address as usual; code that checks a payment by reading the chain must know that
getSignaturesForAddresson the address shows no SPL transfer.
2026-10-05, which networks and tokens actually work
This entry corrects the documentation, not the behaviour of the API: no route and no field changed. Earlier entries said the EVM networks were in development and TON was planned; that stopped being true before the documentation caught up.
- Payments work on nine networks:
tron,ethereum,bsc,polygon,base,arbitrum,optimism,avalanche,ton. Their assets are listed byGET /v1/assets; the contracts and decimals are in assets. - Native coins are accepted like tokens, at an exchange rate:
TRX_TRON,GRAM_TON,ETH_ETHEREUM,BNB_BSC,POL_POLYGON,ETH_BASE,ETH_ARBITRUM,ETH_OPTIMISM,AVAX_AVALANCHE. Only a plain transfer from a wallet is credited. - USDT on Polygon, Arbitrum and OP Mainnet is USDT0, a separate asset with its own contract:
USDT0_POLYGON,USDT0_ARBITRUM,USDT0_OPTIMISM. USDC_TRONis switched off by the operator entirely, not only for new invoices: payouts in it are off as well and are refused with422 asset_payouts_disabled. Until this entry the documentation promised that payouts in it worked, which was wrong.- Payouts are still made on TRON only, and only in
USDT_TRON. There are none on the EVM networks or TON:422 asset_not_payable. - The list of networks and tokens is no longer described with the statuses "works / in development / planned": networks and tokens says what the service does on each network, and what is available to a project at a given moment is the answer of
GET /v1/assets. An asset switched off by the operator or by the project's settings does not appear in that answer.
2026-10-05, the settlement wallet is readable
- Cabinet API: the project object gained
custody_mode—custodial(the service keeps the money) orpass_through(the money goes to the project's own wallet). Only the operator changes it. - Cabinet API:
GET /cabinet/v1/merchants/{id}/settlement-walletsreturns the wallets the project's accepted money is forwarded to, one per asset: the address in full, from which moment it is in force, and whether it is in use already. The list is empty for a custodial project. See where the money goes. - Admin API:
GET /admin/v1/merchants/{id}/settlement-walletsreturns the same plus who entered the row and the fingerprint of the key that signed it. - The merchant API and the bodies of webhooks did not change. A wallet is still entered by a command an operator signs, never through the API.
2026-10-05, pass-through custody in the admin API
- Admin API: the merchant object gained
custody_mode—custodial(our treasury) orpass_through(the merchant's own settlement wallet).PATCH /admin/v1/merchants/{id}takes it; switching topass_throughwithout a settlement wallet in force answers422 settlement_wallet_missing. - Admin API:
GET /admin/v1/custody-modesandPATCH /admin/v1/custody-modes/{mode}— the platform switch of the pass-through mode. The custodial mode cannot be switched off:422 custody_mode_last_enabled. - The merchant API, the cabinet API and webhook bodies did not change. The wallet a merchant's money is forwarded to is entered by an operator with a signature made outside the service, not through an API.
- Documented for integrations: what
settledmeans for a project settled directly, why its balance stays at zero, and that small payments accumulate on the deposit address until a transfer is worth its cost — see where the money goes.
2026-10-04, balance of the project by API
- Merchant API:
GET /v1/balancereturns the ledger balance of every asset the project holds something in, what is still unconfirmed beside it, and the same money in dollars at the rates the service has accepted. An asset without a fresh rate is listed and named intotal.unpriced_assetsrather than valued at a stale one. The answer carries anETag. See balance. - No existing route or field changed.
2026-10-04, QR code of an invoice and conditional reads
- Merchant API:
POST /v1/invoicesandGET /v1/invoices/{id}take the query parameterinclude=qrand then carryqr.address— the QR code of the payment address as an SVG image in adata:URL, about 11 KB. It is the image the checkout page draws, so a product that draws its own payment screen needs no QR library. Without the parameter the field is absent, and the change feed does not take it. See QR code. - Merchant API:
GET /v1/assets,GET /v1/invoices,GET /v1/invoices/{id}andGET /v1/deposits/{tx_hash}answer with anETag. A request that returns it inIf-None-Matchgets304 Not Modifiedwith no body while the answer has not changed;*always matches. Errors are not tagged. See conditional reads. - Breaking for a client that sent query parameters of its own:
POST /v1/invoicesandGET /v1/invoices/{id}now acceptincludeonly, and refuse any other query parameter with400 validation_failed. Nothing documented used one; a client that appended its own tracking parameters to these two URLs must stop.
2026-10-04, invoice change feed
- Merchant API:
GET /v1/invoicesreturns the invoices of a project in the order their state changed — the catch-up path after a missed webhook. Parametersupdated_since,status,limit(1–100, 50 by default) andcursor; the response carriesinvoiceswithout thedepositsfield, andnext_cursor. Pages are keyset: an invoice that changes during the walk does not shift a page. See change feed. - No existing route or field changed.
2026-10-04, the step of an amount in a token pegged to the dollar
- Merchant API: the invoice amount in a token pegged to the dollar is rounded up to the cent of that token, not to its last decimal. A price of $229.00 at a rate of 0.9999 asks for
229.03, not229.024488. Rounding stays up: the customer pays at least the price, and the difference is under a cent and in the merchant's favour. A token pegged to nothing keeps its old step of 0.00000001 of a unit. The rest of an invoice repriced after a quote window steps the same way. See price and exchange rate and ADR-0053. - No field or error code changed.
2026-10-04, payment details for your own page
- Merchant API: the invoice response gained
payment_uri, a link that opens a wallet with the transfer filled in. It is there while the exact amount is asked for: the invoice waits for money, its deadline has not passed and its quote still holds. Today only TON gives one; other networks returnnull, because they have no link scheme this service trusts. - Merchant API:
GET /v1/assetsgainedconfirmations, how many confirmations a transfer of the asset needs, andicon, the mark of the asset in the catalog. A merchant draws «2 of 20» and its own list of coins from them instead of inventing the values. - Compatible: no existing field changed or disappeared.
- The first-run setup routes
/setup/*are no longer inGET /v1/openapi.json: the document describes the merchant's own routes only.
2026-09-20, marking a withdrawal line paid by hand
- Admin API:
POST /admin/v1/withdrawals/{id}/lines/{line_id}/force-paidmarks a line of a request paid without waiting for the chain, with a reason that is required. It is the only decision of staff on a request that moves money: it posts what the check would have posted, with the same keys. Lines of a request gainedforced_by,forced_atandforce_reason. See merchant withdrawals and ADR-0058. - The merchant API, the cabinet API and webhook bodies did not change.
2026-09-20, the fee of withdrawals in dollars
- Admin API:
POST /admin/v1/withdrawal-feesandPOST /admin/v1/merchants/{id}/withdrawal-feestakeamount_usdin place ofamount— the fixed part in dollars. The service turns it into the coin at a fresh rate on every request and freezes it in the line; the amount of the coin is kept beside it and stands when no rate is fresh. The fee in effect and every row of the tariff gained anamount_usdfield. Writing such a row without a fresh rate answers503 rate_unavailable. See the fee of withdrawals and ADR-0059. - The merchant API, the cabinet API and webhook bodies did not change. The admin API stays compatible:
amountis still accepted.
2026-09-20, a fee of its own for withdrawals
- Admin API: the fee a merchant pays to withdraw its own money has its own tariff —
GET /admin/v1/withdrawal-fees,POST /admin/v1/withdrawal-fees,GET /admin/v1/merchants/{id}/withdrawal-fees,POST /admin/v1/merchants/{id}/withdrawal-fees— and can be overridden for one project. New error codeswithdrawal_fee_confirmation_requiredandwithdrawal_fee_valid_from_in_past— see the fee of withdrawals. The decision — ADR-0059. - Breaking for clients of the admin API that set the fee of withdrawals through
POST /admin/v1/payout-fees: that is now the tariff of payouts to third parties alone, and the fee of withdrawals is set by the routes above. The history of the old tariff was carried into the new one, so the numbers in effect did not change. - The merchant API, the cabinet API and webhook bodies did not change.
2026-09-19, merchant withdrawals
- Cabinet API: requests to withdraw a project's money to the owner's wallets —
GET /cabinet/v1/withdrawals/assets,GET /cabinet/v1/withdrawals,POST /cabinet/v1/withdrawals/preview,POST /cabinet/v1/withdrawals,GET /cabinet/v1/withdrawals/{id},POST /cabinet/v1/withdrawals/{id}/cancel. A request holds several tokens and wallets, reserves the amounts with their fees at once, and needs a second-factor code the cabinet has checked — see withdrawals. The decision — ADR-0058. - Admin API:
GET /admin/v1/withdrawals/summary,GET /admin/v1/withdrawals,GET /admin/v1/withdrawals/{id}, entering the hash of a manual transfer withPOST /admin/v1/withdrawals/{id}/lines/{line_id}/tx, and refusals withPOST /admin/v1/withdrawals/{id}/lines/{line_id}/rejectandPOST /admin/v1/withdrawals/{id}/reject— see merchant withdrawals. The payout tariffPOST /admin/v1/payout-feestakes any asset of the catalog, not only one with payouts: it is also the fee of a withdrawal;GET /admin/v1/payout-feeslists every active asset. - New error codes of these routes:
withdrawal_not_found,withdrawal_insufficient_balance,withdrawal_not_cancelable,withdrawal_line_not_found,withdrawal_line_not_open,withdrawal_tx_in_use,withdrawal_code_required. - The merchant API and webhook bodies did not change.
2026-09-19, funds in dollars in the cabinet
- Cabinet API:
GET /cabinet/v1/balancesreturns the new fieldsassets,projectsandtotalnext tobalances: the balances per asset across all projects, per project and in total, valued in US dollars at the market rate. An asset without a fresh rate is left out of the sums and named inunpriced_assets— see cabinet API. - The merchant API, webhook bodies and error codes did not change.
2026-09-18, TON
- A new network,
ton— the TON mainnet, with the assetsUSDT_TON(6 decimals) andGRAM_TON, Gram, the native coin of TON (9 decimals).USDT_TONpays invoices;GRAM_TONpays them once the platform enables payments in it, likeTRX_TRON. There are no payouts on TON.GET /v1/assetslists the TON assets once the operator enables the network — see networks and tokens and assets. - An address on TON is returned in the user-friendly form, 48 base64url characters, non-bounceable:
UQ…,0Q…on a test network. This is the form ofaddressinPOST /v1/addresses, in invoices, inGET /v1/deposits/{tx_hash}and indeposit.*webhooks, and the form to show the customer as it is — see addresses on TON. Thecontractof a TON token inGET /v1/assetsis the jetton master in the raw form,0:and 64 hexadecimal digits. Check your integration if it validates an address against the forms of TRON and EVM networks: accept the TON form. - A TON transaction hash is 64 hexadecimal characters without
0x, as on TRON;GET /v1/deposits/{tx_hash}accepts it. On TON,log_indexis0for a token transfer and-1for GRAM — see verify a transfer. - The checkout page routes return a new field,
wallet_url: on TON, aton://transferlink that opens a wallet application with the address, the amount still to pay and the token;nullelsewhere. The page shows it as an "Open in wallet" button — see read an invoice for the page. Merchants do not need these routes. - Admin API: the family
ton, whosechain_idis the global id of the network;chain_symbolof a token preview can benull, when the jetton keeps its symbol off chain; a TON jetton master is accepted in the raw form or a user-friendly one; the gas-wallet threshold andgas_walletapply to TON too — see admin API. - Error codes and the bodies of
invoice.*webhooks have not changed.
2026-09-16, payments in native coins of EVM networks
- The platform can accept the native coins of EVM networks for payments —
ETH_ETHEREUM,BNB_BSC,POL_POLYGON,ETH_BASE,ETH_ARBITRUM,ETH_OPTIMISM,AVAX_AVALANCHE— when it enables their network and payments in them. They behave likeTRX_TRON: priced at an exchange rate, listed byGET /v1/assetswhile an invoice can be created in them, 18 decimals, no payouts — see networks and tokens and assets. The EVM networks are still in development. - Only a plain transfer from a wallet is credited. A coin sent by a smart contract — a smart-contract wallet, some exchanges — is not credited automatically, is not listed by
GET /v1/deposits/{tx_hash}, and sends no webhook — see what arrives at an address. - A transfer of such a coin has
log_index=-1inGET /v1/deposits/{tx_hash}and indeposit.*webhooks, as a TRX transfer already does. Check your integration if it assumeslog_indexis never negative. - Routes, fields, webhook bodies and error codes have not changed. Admin API:
PATCH /admin/v1/assets/{id}can switch onaccepts_paymentsof the native coin of an EVM network together with itsrate_symbol, andpayments_supportedistruefor it — see admin API. The decision — ADR-0055.
2026-09-16, payments in TRX
- The platform can accept
TRX_TRON, the native coin of TRON, for payments, when the platform enables payments in it.GET /v1/assetslistsTRX_TRONwhile an invoice can be created in it: take the list from there — see list the assets and assets. - Such invoices and choices behave like those of any asset priced at an exchange rate. An invoice created without
assetand paid inTRX_TRONis converted at the rate lowered by the platform's markdown, and the rest still owed is quoted again after the quote window — see price and exchange rate. An invoice created withasset=TRX_TRONhas its amount in TRX, set by the merchant. - Payouts in
TRX_TRONare not possible:POST /v1/payoutsanswers422 asset_not_payable. - Check your integration if it checks
assetof an invoice, a transfer or a webhook against a fixed list: acceptTRX_TRON. Take its decimals fromGET /v1/assets, not from your code. - Routes, fields, webhook bodies and error codes have not changed. Admin API:
PATCH /admin/v1/assets/{id}can switch onaccepts_paymentsofTRX_TRON, and answers409 asset_flag_lockedfor the native coin of an EVM network and for removingrate_symbolfrom an asset accepted without a peg to the US dollar — see admin API. The decision — ADR-0054.
2026-09-16, prices in US dollars at exchange rates
- An invoice created without
assetis converted into the chosen token at an exchange rate that the service collects, stablecoins included: 1 USDT is no longer taken as 1 USD. The amount is rounded up to the last decimal of the token, and to 8 decimals for a token with more, so the customer pays at least the price. A token not pegged to the US dollar is converted at its rate lowered by the platform's markdown. See price and exchange rate; the decision — ADR-0053. - Breaking for code that compares its price with
amount_requestedof an invoice created withoutasset: the price converted at a rate is not equal toprice.amount. Compare withprice.amount. - Breaking for code that reads
amount_expectedoramount_remainingof such an invoice once. After the quote window, while the invoice iscreatedorpartially_paidand before its deadline, the rest still owed is quoted again at the current rate, and these amounts andamount_requestedchange. No webhook is sent for that: read the amounts fromGET /v1/invoices/{id}. See quote window. - A price that cannot be written exactly in a token is no longer refused with
400 validation_failed: it is rounded up. - A new field
quotein the responses ofPOST /v1/invoicesandGET /v1/invoices/{id}:rate,market_rate,markdown_percent,currency,numberandexpires_at. It isnullfor an invoice created withasset, before the choice, and for an invoice whose token was chosen before this change. See the quote. - Check your integration for a new error code:
POST /v1/invoiceswithoutassetanswers503 rate_unavailablewhen none of the allowed tokens has a fresh rate, and no invoice is created. Retry later with the sameorder_id.422 asset_not_offerednow means that a token fromcurrencieshas no exchange rate, and422 no_payable_assetsthat the merchant accepts no token with one. See error codes. - The checkout page routes:
GET /checkout/v1/invoices/{id}returnsrate,rate_idandrate_expires_atin each element ofchoices, andrates_unavailableandquotewithrate,currencyandexpires_aton the invoice.POST /checkout/v1/invoices/{id}/selecttakes an optionalrate_idand can answer503 rate_unavailable. See save the token choice. Merchants do not need these routes. - Cabinet API:
quoteon invoices;POST /cabinet/v1/invoicescan answer503 rate_unavailable. Admin API:quoteon invoices, new routesGET /admin/v1/ratesandPATCH /admin/v1/rates/settings, and the asset fieldrate_symbol— see admin API. - Webhook bodies have not changed. Invoices created with
assetbehave as before.
2026-09-14, the gas wallet in the ledger
- Admin API: a new ledger account kind
gas_walletinGET /admin/v1/balancesand in the postings ofGET /admin/v1/ledger/transactions— the gas wallet of an EVM network, in its native coin. The gas of a move to the treasury is posted withreason=feeand the keysweep_gas:{network}:{tx_hash}: debitnetwork_fees, creditgas_wallet. See admin API. - The public API, webhooks and error codes have not changed.
2026-09-14, managing networks and tokens
- The admin API manages networks and tokens — admin API. New routes:
POST /admin/v1/networks,PATCH /admin/v1/networks/{id},GET /admin/v1/networks/{id}/tokens/{contract},POST /admin/v1/assetsandDELETE /admin/v1/assets/{id}.PATCH /admin/v1/assets/{id}also changes minimums, confirmations, the sweep threshold, the flags of payments and payouts, archiving and the mark, andGET /admin/v1/assetsaccepts thenetworkparameter. - New fields in admin API responses:
icon_slugandrpc_configuredon a network;pegged_to,sweep_min_amount,icon_slugandpayouts_supportedon an asset. New error codes:asset_exists,asset_in_use,asset_referenced,asset_confirmations_below_finality,asset_symbol_required,contract_invalid,contract_not_token,network_not_found,network_exists,network_without_assets,network_family_not_supported,network_rpc_not_configured,network_chain_id_mismatch,network_rpc_unavailable. - The checkout page routes
GET /checkout/v1/invoices/{id}andPOST /checkout/v1/invoices/{id}/selectreturnnetwork_icon_slug, the slug of the network's mark, on the invoice and in each element ofchoices— see read an invoice for the page. The merchant API has not changed. The new EVM networks appear in the registry switched off, and their assets are not offered to merchants.
2026-09-14, list of assets, hashes of EVM networks, explorer links
New fields with the name of the network and links to its explorer. Invoices (
POST /v1/invoices,GET /v1/invoices/{id}):network_name,address_explorer_url, andexplorer_urlin each element ofdeposits.GET /v1/deposits/{tx_hash}:explorer_url. Payouts:network_nameandexplorer_url. A link is built from the explorer template of the network and isnullif the network has none — see create an invoice.The checkout page route
GET /checkout/v1/invoices/{id}hasaddress_explorer_urlandexplorer_urlindeposits, and givesaddressin the form shown to the customer: on EVM networks, with the EIP-55 checksum — see read an invoice for the page. The merchant API keeps EVM addresses in lower case.New route
GET /v1/assets: the assets the merchant can create an invoice in now, withnetwork,network_name,token_standard,contract,decimalsandmin_amount. Use it to decide which tokens to offer instead of a list in your code — see list the assets.GET /v1/deposits/{tx_hash}also accepts a hash in the form of EVM networks:0xand 64 hexadecimal characters. The hash is looked up exactly in the form given, in any case; a TRON hash is still written without0x— see verify a transfer.EVM networks are still in development: until the operator enables one,
GET /v1/assetslists only TRON assets, and nothing changes for a TRON integration. Error codes have not changed.
2026-09-14, the network in webhook events
- A new field
networkindataof theinvoice.*anddeposit.*events: the id of the network of the asset, such astron, the same value asnetworkof the invoice and of the transfer in the API. Ininvoice.expiredof an invoice whose customer did not choose a token, it isnull. See webhooks. - The field is added; nothing is removed or renamed. Check your integration if it rejects unknown fields in a webhook body: accept
network. - The service is getting ready to accept payments in EVM networks, where one transaction hash can exist in several networks. A transfer is identified by
network,tx_hashandlog_indextogether. - Routes and error codes have not changed.
2026-09-14, USDC on TRON for payouts only
- Check your integration if it creates invoices in
USDC_TRONor passes it incurrencies. Circle has stopped minting USDC on TRON, and the operator has disabled payments inUSDC_TRON:POST /v1/invoicesin this asset, or with it incurrencies, answers422 asset_payments_disabled. Remove the asset from your requests or handle this code. See assets. - The checkout page does not offer
USDC_TRONto the customer, andPOST /v1/addressesdoes not include it inassets. - Payouts in
USDC_TRONwork. Invoices inUSDC_TRONthat are already open are still paid, and a USDC transfer to aper_userpermanent address is credited as before. - Routes, fields and error codes have not changed. The rationale is in ADR-0026, amendment of 2026-09-14.
2026-09-14, partial payment
- A new invoice status
partially_paid: before the deadline, confirmed transfers cover part ofamount_expected, and no transfer is waiting for confirmations. The customer can send the rest to the same address before the deadline. There is no tolerance for an underpayment:confirmedstill means the whole amount, andunderpaidarrives after a deadline without the rest. See partial payment. - A new webhook event
invoice.partially_paid, on every transition to this status. See events. - A new field
amount_remainingin the responses ofPOST /v1/invoicesandGET /v1/invoices/{id}:amount_expectedminusamount_received, not below zero;nulluntil the customer has chosen the token. The checkout page routeGET /checkout/v1/invoices/{id}has the same field. - Breaking for code that parses
statusor the event type against a strict list. A partially paid invoice used to stayseenbefore the deadline; now it ispartially_paid, andseenmeans a transfer is waiting for confirmations. Addpartially_paidto the list and do not fulfill the order on it. If your handler does not processinvoice.partially_paid, respond2xxto it. - Check your integration. The "Return to the shop" button leads to
cancel_urlwithstatus=partially_paidtoo: the invoice is open and can still be paid — see return before an outcome. - On
partially_paidthe checkout page asks the customer to sendamount_remainingto the same address, and onseenit no longer says that nothing more needs to be sent. See checkout page. - Cabinet API and admin API:
partially_paidamong the invoice statuses and in thestatusfilter. Error codes have not changed.
2026-09-14, verifying a transfer and pausing an address mode
- New route
GET /v1/deposits/{tx_hash}: the transfers of a transaction to the merchant's addresses, withkind,status,confirmationsandcredited. Aper_usertop-up without an invoice can now be checked through the API, as the main rule of webhooks requires — see verify a transfer. New code404 deposit_not_found. - Check your integration. The platform can pause an address mode. While a mode is paused, its projects get
422 address_mode_disabledfromPOST /v1/addresses,POST /v1/invoicesandPOST /checkout/v1/invoices/{id}/select, and a new project cannot choose the mode — see a paused mode. Transfers to addresses already issued are credited as usual. Handle the code like any other422: tell the customer that payment is not available now. - Cabinet API:
GET /cabinet/v1/address-modes;POST /cabinet/v1/merchantsandPOST /cabinet/v1/invoicescan answer422 address_mode_disabled. Admin API:GET /admin/v1/address-modesandPATCH /admin/v1/address-modes/{mode}, the code422 address_mode_last_enabled— admin API.
2026-09-13, the failed payout event
- The
payout.failedwebhook arrives on every transition of a payout tofailed. Before, it arrived only when the watcher found the payout transaction unsuccessful. Now it also arrives when the signer refused to sign the payout, when attempts ran out, and when the signer itself found the transaction of an earlier attempt unsuccessful. There is one event per payout. See events. - Breaking for code that parses
payout.failedstrictly: itstx_hashcan benull— for a payout nothing was ever signed for. Thereasonfield is text for a human. - A payout no longer becomes
failedwhen the node answered the broadcast of its transaction with an error or did not answer at all: the money may have left. It stays inqueuedwith the sametx_hashuntil the transaction is found in the chain or expires without reaching a block. See payout statuses. - Routes and error codes have not changed.
2026-09-13, documentation in English
- The documentation is published in two languages. English is the primary one, at the existing addresses:
/docs,/docs/<section>.md,/llms.txt, and/llms-full.txt. Russian is under/ru:/ru/docs,/ru/docs/<section>.md,/ru/llms.txt, and/ru/llms-full.txt. Integrations that read the Russian Markdown at the old addresses should switch to the/ruaddresses. - The checkout page opens in the customer's language: the
langparameter ofcheckout_url, then the choice the customer made on the page, then the browser language, otherwise English. See checkout page. - The API, error codes, and webhook events have not changed.
2026-09-13, the customer chooses the token
assetinPOST /v1/invoicesis now optional. Without it, the customer chooses the token and the network on the checkout page, andamountis the price in US dollars. A new optional fieldcurrencieslists the assets to choose from. See the customer chooses the token; the decision — ADR-0042.An invoice has new fields
price,currencies, andasset_selected_at.Breaking for code that parses invoices strictly. For an invoice without a chosen token, these are
null:asset,network,address;amount_requested,amount_expected,amount_received,amount_credited;fees.service.fixedandfees.service.amount.
Integrations that always pass
assetdo not create such invoices and need no changes. The samenullvalues appear in theinvoice.expiredevent of an invoice whose deadline passed without a choice, and in the responses of the cabinet API and the console.A retry of
POST /v1/invoiceswithoutassetcompares the price and the set ofcurrencies: a different set gets409 order_id_conflict.New codes:
422 asset_required,422 no_payable_assets,422 asset_not_offered; the checkout page route also has409 asset_already_selectedand409 invoice_expired.The checkout page route
GET /checkout/v1/invoices/{id}: the fieldspriceandchoices; the asset and amount fields can benull. A new routePOST /checkout/v1/invoices/{id}/select. Merchants do not need them.POST /cabinet/v1/invoices:assetis optional; new fieldcurrencies.Admin API: the invoice has the fields
price,currencies, andasset_selected_at; the asset, the address, and the amounts can benull.
2026-09-13, invoice lifetime and return URLs
expires_ininPOST /v1/invoicesis now optional: without it, the invoice waits for the payment for 3600 seconds (1 hour).- Breaking for requests with a lifetime shorter than 5 minutes or longer than 72 hours. The bounds of
expires_inare now 300 to 259200 seconds, inPOST /v1/invoicesand inPOST /cabinet/v1/invoices. They used to be 60 to 604800 (7 days). A request outside the bounds gets400 validation_failed, including a retry of a request accepted earlier: the request shape is validated before the invoice is looked up. If your integration sets a lifetime longer than 72 hours, reduce it. Invoices already created keep their lifetime. - Overrides of
return_urlandcancel_urlinPOST /v1/invoicesare now also allowed within the project site from its application (site_url): the origin must match the project's URL of the same kind or the project site. Ont.meandtelegram.methe bot or channel name — the first path segment, case-insensitive — must match too; this rule now also applies to the project's URL of the same kind. Previously, without a URL of the same kind in the project settings, an override was rejected with422 redirect_url_not_allowed. The format requirements for the URL have not changed — see return URLs. - Check your integration. While an invoice can be paid, the checkout page shows a "Return to the shop" button if the invoice or the project has a cancel URL. The button leads to
cancel_urlwith a signature andstatus=createdorstatus=seen: the invoice is still open and can still be paid. If your cancel URL handler cancels the order, have it check the status withGET /v1/invoices/{id}first — see return before an outcome. While the invoice is open, the page itself does not redirect anywhere. POST /cabinet/v1/invoices: optionalreturn_urlandcancel_urlunder the same rules.- The checkout page route
GET /checkout/v1/invoices/{id}: a new fieldback_url, the signed URL for the "Return to the shop" button. - The mapping of Heleket parameters is in checkout page.
2026-09-13, fee percentage and fixed amount
- The service fee of an invoice now has a fixed amount per asset. The new field
fees.service.fixedis the amount recorded in the invoice at creation;fees.service.amountis still everything withheld, which is now the percentage and the fixed amount together. The fields are only added. The same applies to the data ofinvoice.*events and to the responses of the cabinet API and the checkout page. See fees. - Check your integration. If the customer pays the service fee,
amount_expected= price + percentage + fixed amount. Compare your price withamount_requested, as before. Until the operator sets a fixed amount, it is zero, and nothing changes. - The fixed amount is withheld once per invoice and never more than what was received: a payment smaller than the fixed amount is withheld in full. A
per_usertop-up without an invoice is a separate operation with its own fixed amount. - The payout fee now has a percentage of the amount: new fields
fees.network.percentandfees.network.fixed, andfees.network.amountis the whole fee, the fixed amount plus the percentage ofamountrounded down. The same applies to the data of thepayout.confirmedandpayout.failedevents. See payout fee. - Admin API: the routes
GET /admin/v1/invoice-feesandPOST /admin/v1/invoice-fees, the codesinvoice_fee_confirmation_requiredandinvoice_fee_valid_from_in_past— admin API. Breaking for admin API clients:POST /admin/v1/payout-feesrequires thepercentfield and acceptsconfirm_high_rate, and in responses the current fee and the tariff rows have a newpercentfield. The console was updated in the same change.
2026-09-13, network fee
- Breaking for code that checks the invoice's
fees.network.statusagainst the listquoted,pending,settled. An invoice has no separate network fee: the network costs of accepting payments are part of the service fee, andfees.network.statusis now alwaysincluded. The former values no longer arrive. The keys are kept:quote,quote_rate, andactualare alwaysnull,payeris alwaysmerchant. If your integration checks the list strictly, addincluded. See fees. The same applies toinvoice.*events and to the responses of the cabinet API and the checkout page. - The codes
422 network_fee_payer_not_supportedand503 network_fee_quote_unavailableremain: the customer does not bear the network fee. Only their messages have changed. - A payout has a new field
fees.network:payeris alwaysmerchant,amountis the fixed network fee recorded in the payout at creation,chargedis how much was charged to the project balance. The same is in the data of thepayout.confirmedandpayout.failedevents. See payout fee. - The network fee is charged to the project balance when the payout is confirmed, as a separate ledger posting; a payout that failed on-chain does not carry it. The recipient still receives exactly
amount. - Admin API: the tariff of the payout network fee —
GET /admin/v1/payout-feesandPOST /admin/v1/payout-fees, the codespayout_fee_confirmation_requiredandpayout_fee_valid_from_in_past; a new ledger account kindnetwork_cost_recoveryinGET /admin/v1/balancesand in postings — admin API.
2026-09-13, service fee
- An invoice has new fields:
amount_requested— the merchant's price,amount_credited— the amount received minus the fees withheld,fees— the service fee and the network fee. The fields are only added, and the existing ones keep their meaning:amount_expectedis still the amount the customer must pay. See fees. - Check your integration. If the customer pays the service fee in a project,
amount_expectedis greater than the price by that fee. Code that comparesamount_expectedwith its order price must compareamount_requestedinstead. In projects where the merchant pays the fee (the default),amount_expectedequals the price, as before. - Idempotency of
POST /v1/invoicesbyorder_idcompares theamountof the request with theamount_requestedprice: a retry sends the price, not the amount with the fee. - The service fee is withheld from each confirmed payment as a separate ledger posting and reduces the merchant's balance. The rate is recorded in the invoice at creation: a tariff change does not change invoices already issued.
- The same fields are in the data of
invoice.*events (webhooks) and in the response of the checkout page route; the checkout page shows the breakdown "Price · Service fee · Total" when the customer pays the fee (checkout page). - New code
503 network_fee_quote_unavailable: the project is configured so that the customer pays the network fee, but it cannot be calculated right now. The cabinet API no longer accepts such a setting —422 network_fee_payer_not_supported. - Admin API: service fee tariffs —
GET /admin/v1/service-fees,POST /admin/v1/service-fees/platform,GET /admin/v1/merchants/{id}/service-fees,POST /admin/v1/merchants/{id}/service-fees, the codesservice_fee_confirmation_requiredandservice_fee_valid_from_in_past— admin API.GET /admin/v1/feesremains the network fee report.
2026-09-13, project review and project settings
- A project that a business creates by itself accepts payments only after review. Until it is approved, or if it is rejected,
POST /v1/invoicesandPOST /v1/addressesrespond with the new code403 merchant_not_approved. API keys still work, and so does reading invoices. All merchants created earlier are approved: nothing changes for existing integrations. - The owner can disable accepting payments in an asset for their project. Then
POST /v1/invoicesin that asset responds with the new code422 asset_disabled_by_merchant, andPOST /v1/addressesdoes not include the asset in theassetslist. Invoices that are already open are paid and closed, and a retry of an accepted request returns the invoice. 403 merchant_disablednow means that the merchant was disabled either by the operator or by the project owner.- A project without a webhook URL receives no events: they are not queued and are not sent after a URL appears (webhooks).
- Admin API: a merchant has new fields —
review_status,review_reason,reviewed_at,reviewed_by,site_url,description,network_fee_payer,service_fee_payer,disabled_by. There is a new filterGET /admin/v1/merchants?review_status=and new routesPOST /admin/v1/merchants/{id}/approveandPOST /admin/v1/merchants/{id}/reject— admin API. Breaking for admin API clients: a merchant'swebhook_urlcan benull. The console was updated in the same change. - The
/cabinet/v1routes for the cabinet application: internal, not reachable from outside, and not included inGET /v1/openapi.json.
2026-09-13, payment and payout switches
- The operator can temporarily disable accepting payments or making payouts in an asset. While payments are disabled,
POST /v1/invoicesin that asset responds with the new code422 asset_payments_disabled, andPOST /v1/addressesdoes not include the asset in theassetslist. While payouts are disabled,POST /v1/payoutsresponds with the new code422 asset_payouts_disabled. - Retries of requests accepted earlier respond as before: the invoice for the same
order_idand the payout for the samereferenceare returned. - Invoices that are already open are paid and closed, and payouts that are already created are executed: a switch affects only new ones.
- Admin API:
PATCH /admin/v1/assets/{id}, thepayments_enabledandpayouts_enabledfields in the asset object, new codesasset_not_foundandasset_flag_locked— admin API.
2026-09-13, console admin API
- The
/admin/v1routes for the operator console: merchants and their keys, invoices, balances of ledger accounts, fund movements, network fees, the asset catalog, webhook deliveries, and the audit log — admin API. Access requires the console tokenADMIN_API_TOKENand the operator headerX-Operator; without the token the routes do not exist (ADR-0033). - The merchant's return URLs
return_urlandcancel_urlare visible in the merchant object and are changed withPATCH /admin/v1/merchants/{id}under the rules of the checkout page. - New error codes, only for these routes:
merchant_not_found,api_key_not_found,delivery_not_found,delivery_not_failed. - The
/v1routes and theGET /v1/openapi.jsondescription have not changed: the admin routes are not included in it.
2026-09-13, checkout page and return URLs
- An invoice has a new field
checkout_url: a link to the checkout page if the operator has set it up, otherwisenull. - Optional
return_urlandcancel_urlfields inPOST /v1/invoices: where the checkout page sends the customer back. They are allowed only within the origin of the URLs that the operator set for the merchant; otherwise the new code422 redirect_url_not_allowed. The same fields appeared in the invoice response. - A retry of
POST /v1/invoiceswith a differentreturn_urlorcancel_urlreturns409 order_id_conflict, like a retry with a different amount. Requests without these fields behave as before. - The customer's return is signed: the parameters
invoice_id,order_id,status,ts,signature, with an HMAC-SHA256 signature made with the webhook secret. The format and the verification code are described in checkout page. The decision — ADR-0032. GET /checkout/v1/invoices/{id}is an internal route of the checkout page server, without an API key, on the private network. Merchants do not need it.
2026-09-12, USDC on TRON
- New asset
USDC_TRON: invoices and payouts in USDC are accepted on a par with USDT, 6 decimal places, 20 confirmations. The contractTEkxiTehnzSmSe2XqrBj4w32RUN966rdz8was read from the network, not taken from an article:symbol() = USDC,name() = USD Coin,decimals() = 6. The rationale and the limits of the decision are in ADR-0026. - USDC on a deposit address no longer counts as an unexpected asset by itself.
unexpected_assetis an asset from the catalog that the address does not accept: at an invoice address, an asset other than the invoice's; at a permanent address, an asset not accepted for payments (TRX). A token that is not in the catalog is not recorded at all. The first version of this item calledunexpected_asseta token outside the catalog; that was wrong and was corrected on 2026-09-14. - TRX has not become a payment asset yet: in energy burn mode (
SWEEP_ENERGY_MODE=burn) the deposit address itself pays the sweep fee, so the operator puts TRX on it — and with the flag enabled, this operational money would be credited as someone's payment. First a rule that tells transfers from our own addresses apart, and a test; then the flag. TRX payouts will not appear even after that: daily limits add up amounts in a single unit, which is valid only for dollar-pegged assets. - The assets of the test networks have not changed: there is no verifiable USDC contract on Nile or Shasta.
2026-09-12, domain in the installer
POST /setup/domain: the panel finds the zone at the DNS provider that the given name belongs to, creates or fixes an A record pointing to the address of this server, and returns what public DNS shows right now. What is checked is not our own record but the network's answer: what was written and what is visible are different statements, and only the second one works.- If the provider has no such zone, the panel creates it by itself and shows the NS servers that must be set at the domain registrar: without delegation nobody sees the zone, however correct its records are. The zone is created under the registrable name — the last two labels.
- The route's response says whether the provider sees the delegation (
delegated), whether the zone was created (zone_created), and which NS servers are needed (nameservers). - The "Domain" step comes first and is optional: without a domain the service is reachable at the server's address. The panel itself writes the
SETUP_DOMAINandBUNNY_API_KEYvariables; the provider key is used only to write and check records and is never shown back. - The first-run panel now opens in a browser at the server's address — the API port is published for the duration of setup by a single variable and goes back to loopback afterwards (ADR-0025). The
/v1routes have not changed.
2026-09-12, the installer writes the configuration itself
- The first-run page no longer prints a block of variables to paste by hand: it writes the environment file on the server —
POST /setup/env. The values are checked by the same parsers that the service uses to read its configuration at startup, so the written file is the file the service starts with. The/v1routes have not changed. POST /setup/signer-valuesandGET /setup/signer-env: the owner's choices for the signer and a ready environment file for it, together with the commands in order. The password of the database role and the two seed values are not in it: the first is already on this server, and the other two are created on that one.- The "Network and nodes" and "Environment file" steps are new and required. The second one is closed only when the values are not just written but also applied: variables are read once, at startup.
- A secret that the page accepted is never returned: in the
GET /setup/stateresponse it has only an "is set" flag. An empty field means "do not change". - A field into which 12 or 24 short words were pasted is rejected: a mnemonic is not entered on this page in any field. The rationale and the limits are in ADR-0024.
2026-09-12, first-run page
- While the service setup is not complete, the API serves the
GET /setuppage, which guides you step by step and checks each step. When all the required steps are closed, its routes respond404. This does not affect merchants: the/v1routes have not changed. - The internal routes of the page:
GET /setup/state,POST /setup/verify,POST /setup/steps/{key},POST /setup/merchant. They are available only from the private network and only with a one-time code from the service log. See first run. - The page does not accept or create the mnemonic, the passphrase, or the private half of the operator key — the rationale is in ADR-0024.
2026-09-12, sending payouts
- The
payout.confirmedandpayout.failedevents: the network confirmed the payout, or its transaction landed in a block as unsuccessful. The fields are in webhooks. As before, the webhook body is a "go and check" signal: get the payout state fromGET /v1/payouts/{id}. - A payout in
failedis not retried automatically: a new attempt requires a newreference. - The rest of this update is internal: signing payouts from the hot wallet, a second independent whitelist check together with the operator signature, and a circuit breaker that halts payouts by itself. The public routes have not changed.
2026-09-12, payouts
POST /v1/payouts: creating a payout, idempotent byreference. The response is202for a created payout and200for a retry. See payouts.GET /v1/payouts/{id}: the current state of a payout with its status and review reasons.- A payout is possible only to an address on the merchant's whitelist, and only after the delay before its first use. Exceeding a limit does not reject a payout: it is accepted with the status
pending_reviewand waits for human approval, andreview_reasonsnames the limits exceeded. See limits. - New error codes:
payout_not_found,reference_conflict,asset_not_payable,address_not_whitelisted,payouts_halted. See error codes. - Sending payouts to the network and the
payout.confirmedandpayout.failedwebhook events will come in the next update: for now a payout reaches thequeuedstatus and waits for execution.
2026-09-12, sweeps
- An invoice reaches the
settledstatus: its funds have been moved to the cold treasury. For the merchant this is the same asconfirmed, and the transition has no webhook — the order is fulfilled onconfirmed, as before. See statuses. - The rest of this update is internal: signing and sending sweeps, confirming them on-chain, and ledger postings. The public routes and webhook events have not changed.
2026-09-11, operational routes
GET /admin/health: the state of the service for the operator and monitoring — the watcher lag in blocks and the depth of the webhook queue. See operational state.GET /metrics: the same indicators in the Prometheus text format.- Both routes are internal: they require no authentication, contain no merchant data, and are reachable only on the private network.
2026-09-11, webhook delivery
- Events from the queue go to the merchant's URL with the
X-Signaturesignature and theX-Timestampheader, with retries on the schedule from the webhook documentation. Before this, events were only recorded. - The
User-Agentheader of outgoing webhooks contains the product name. - A
3xxresponse does not count as a successful delivery and is not followed.
2026-09-11, payment watcher
- Invoices change status based on transfers on the network:
seen,confirmed,underpaid,expired,paid_late. See statuses. - In the
GET /v1/invoices/{id}response, thedepositsfield is filled with the payments for the invoice. - The service produces the webhook events
invoice.seen,invoice.confirmed,invoice.overpaid,invoice.underpaid,invoice.expired,invoice.paid_late,deposit.confirmed, anddeposit.orphaned. See events. Sending events to the merchant's URL will be enabled in the next update.
2026-09-11, addresses and invoices
- Authentication with the
X-Api-Keyheader, and request limits per key and per IP address — see authentication. POST /v1/addresses: a permanent user address for merchants inper_usermode. See addresses.POST /v1/invoices: creating an invoice, idempotent byorder_id. See invoices.GET /v1/invoices/{id}: the current state of an invoice with its payments.GET /v1/openapi.json: a machine-readable description of the API in OpenAPI 3.1 format.- New error codes:
validation_failed,unauthorized,merchant_disabled,invoice_not_found,address_mode_mismatch,order_id_conflict,asset_not_supported,amount_below_minimum,rate_limited,addresses_exhausted. See error codes.
2026-09-11, internal routes
- Added the internal routes
GET /healthzandGET /readyz— see monitoring. - Introduced a single error format
{ "error": { "code", "message" } }and the codesbad_request,route_not_found,internal_error,service_unavailable— see error codes. - Every response includes the
X-Request-Idheader for finding the request in the logs.