Developers
API overview
One JSON REST API for Crypto Billing and the Crypto Splitter: Bearer API keys, cursor pagination, idempotent money movements and HMAC-signed webhooks. Start with the quick start, then create your first invoice.
Product 1 of 2
Crypto Billing API #
Issue invoices priced in crypto or fiat, let the payer choose one of the allowed assets on the hosted checkout (or in the widget on your site), and get your share settled to your balance or your wallets. Partial, late and over payments wait for your decision; refunds go back to the address that paid; payouts pay your customers from a separate payout wallet.
Share this section: https://cryptonera.com/developers#crypto-billing
1. Quick start #
Everything both products share: where to send requests, how to authenticate, the limits, how to retry safely and how lists page.
Base URL #
https://cryptonera.com/api/v1
JSON in and out (Content-Type: application/json). Amounts are decimal strings in the asset's own unit ("59.400000"), never numbers. Ids are ULIDs; an invoice also has a transaction_id (TRX-… live, TST-… sandbox). Timestamps are ISO 8601 in UTC. New keys are added to answers without notice: ignore the ones you do not know.
Sandbox and live #
The API key chooses the environment, never the URL. A ck_sandbox_ key works on test networks with test coins; a ck_live_ key with real money. Nothing is shared between them: keys, ids, transaction ids, balances, webhooks and settings are separate, and a sandbox key never sees a live object. See sandbox (test mode).
Authentication and scopes #
Create API keys in the dashboard (Developers → API keys). A key looks like this:
ck_<env>_<prefix>_<secret>
e.g. ck_sandbox_Ab12Cd34Ef56_<40–64 alphanumeric characters>
Send it as a Bearer token from your server, never from a browser. Lost it? Show key in the dashboard displays it again after you confirm your password; revoke a key at once if it leaks.
Authorization: Bearer ck_sandbox_Ab12Cd34Ef56_…
Each key carries scopes; a request outside them gets 403 forbidden (with required_scope). Give a key only the scopes it needs:
| Scope | Allows | Endpoints |
|---|---|---|
| assets:read | Read the supported assets. | GET /assets |
| fees:read | Read your commission per asset. | GET /fee-policy |
| wallets:read | Read wallets. | GET /wallets |
| wallets:write | Create wallets. | POST /wallets |
| balances:read | Read balances and withdrawals. | GET /withdrawals GET /withdrawals/{id} GET /balances |
| balances:withdraw moves money |
Withdraw your balance at once, without an approval: to your settlement wallets (any address while the settlement lock is off). | POST /withdrawals POST /withdrawals/{id}/cancel |
| splitters:read | Read splitters. | GET /splitters GET /splitters/{id} |
| splitters:write | Create, change, pause and resume splitters. | POST /splitters PATCH /splitters/{id} POST /splitters/{id}/pause POST /splitters/{id}/resume |
| sessions:read | Read split sessions. | GET /split-sessions GET /split-sessions/{id} |
| sessions:write | Create and close split sessions. | POST /split-sessions POST /split-sessions/{id}/close |
| invoices:read | Read invoices and the checkout designs. | GET /checkout-themes GET /invoices GET /invoices/{id} |
| invoices:write moves money |
Create and cancel invoices; accept partial, late or extra payments. | POST /invoices POST /invoices/{id}/accept POST /invoices/{id}/cancel |
| activity:read | Read deposits, and settlements with their transfers. | GET /settlements GET /settlements/{id} GET /deposits |
| refunds:read | Read refunds. | GET /refunds GET /refunds/{id} |
| refunds:write moves money |
Refund invoices to the paying customer; cancel queued refunds. | POST /refunds POST /refunds/{id}/cancel |
| webhooks:read | Read webhook endpoints and events. | GET /webhook-endpoints GET /webhook-endpoints/{id} GET /webhook-events |
| webhooks:write | Manage webhook endpoints; replay events. | POST /webhook-endpoints PATCH /webhook-endpoints/{id} DELETE /webhook-endpoints/{id} POST /webhook-events/{id}/replay |
| payouts:read | Read payouts and payout wallets. | GET /payout-wallets GET /payouts GET /payouts/{id} |
| payouts:write moves money |
Pay customers from the payout wallet; create payout wallets. | POST /payout-wallets POST /payouts POST /payouts/{id}/cancel |
Store-restricted keys. A key restricted to one store creates, lists, shows and cancels only that store's invoices, split sessions and refunds (another store: 403 store_not_allowed), and is refused on the account-wide money endpoints (payouts, payout wallets, withdrawals). Balances, settlements, deposits and webhooks stay readable account-wide. IP allowlist: a key may be limited to listed addresses; requests from elsewhere are 401.
Rate limits. Over a limit the answer is 429 with Retry-After:
- 120 requests per minute per key, and 600 per minute per account and environment over all its keys (
rate_limited); - 60 requests per minute per IP address with a key that does not work (a valid key is never refused by it);
- 120 requests per minute per key refused for a missing scope (
403 forbidden), counted on their own; - new invoices: 60 per minute live, 30 sandbox, API and dashboard together (
invoice_rate_limited); - open invoices: at most 2000 live, 200 sandbox (
open_invoice_limit). The operator can change them per account.
Idempotency #
Idempotency-Key is required on these endpoints, which move money (400 idempotency_key_required without it), and optional on the other endpoints that create or act (the Idempotency-Key column of the endpoint tables; — there means the header is ignored, e.g. on PATCH and DELETE /webhook-endpoints/{id}, so a retry there runs again instead of being replayed). Required on:
POST /invoices/{id}/acceptPOST /refundsPOST /payoutsPOST /withdrawals
Use a new UUID for every request and send the same key only to retry that request after a timeout. The same key with the same body returns the stored answer (Idempotent-Replayed: true) instead of running twice; the same key with another body is 409 idempotency_key_reused; while the first request still runs, 409 request_in_progress. Keys are kept 48 hours per account and environment (and per store for a store-restricted key: another store's key sending the same key and body gets 409 idempotency_key_reused, never that store's answer); a malformed key is 400 idempotency_key_invalid. Some answers are never stored (the key stays free: fix the cause, then send the same request again): the Key column of the errors table says which. Never use your order number as the key: send it as order_id (duplicate protection).
Idempotency-Key: 6f0b5c1e-2d4a-4b8e-9a37-1c5e8f2d7a90
Lists and pagination #
List endpoints return a page and a cursor, newest first. Pass ?limit= (at most 100) and ?cursor= to page; filters are exact.
{
"object": "list",
"data": [ … ],
"next_cursor": "eyJpZCI6…",
"has_more": true,
"request_id": "req_…"
}
Every answer carries request_id (and the X-Request-Id header): quote it to support.
Your first payment in four steps #
- Switch the dashboard to Sandbox and create a key with
invoices:write(Developers → API keys). - Save your settlement wallets and tick the store's payment assets (simple integration).
POST /invoices(create an invoice) and send the payer tocheckout_url, or show it in the widget.- Ship the order when your server receives the signed
invoice.paidorinvoice.overpaidwebhook (an overpaid invoice sends onlyinvoice.overpaid; webhooks).
2. Create an invoice #
invoices:write · Idempotency-Key optional · answers 201 (created) or 200 (the open invoice of the same order_id)Headers #
| Header | Required | Value |
|---|---|---|
| Authorization | yes | Bearer ck_… (API keys) |
| Content-Type | yes | application/json |
| Idempotency-Key | no | A new UUID per request, the same one only to retry it (idempotency). Your order number goes in order_id. |
| Accept | no | application/json |
Parameters #
Only pricing_mode, currency and amount are required; with the dashboard set up (simple integration) that and order_id are all you send. merchant_reference was renamed to order_id (sending it is a 422).
| Field | Allowed values | Default | Notes |
|---|---|---|---|
pricing_mode
string · Required |
crypto, fiat |
— | crypto: priced in one asset. fiat: priced in a currency, converted per allowed asset at creation. |
currency
string · Required |
crypto: an asset identifier; fiat: an ISO-4217 code the price source quotes (fiat currencies) | — | Case-insensitive. Crypto pricing: on an invoice that settles to a wallet, for a store with payment assets one of them that can be paid in this environment (another payment option: asset_not_in_store); a split-settled invoice is paid in the splitter's asset. |
amount
string · Required |
A positive decimal string, e.g. "49.99" |
— | A JSON string, never a number. Crypto pricing: at most the asset's decimals. At least the asset's minimum deposit and more than twice the fixed fee per invoice. |
order_id
string · Optional |
Any text up to 120 characters after trimming; control characters are removed | null | Your order number (duplicate protection). While an invoice of this order is open in the store, the same order with the same terms (pricing_mode, currency, amount, and the allowed assets only when the request chose them) returns that invoice unchanged (200, "created": false; every other field is ignored); other terms: 409 order_id_in_use; an order whose invoice received money: 409 order_already_paid. |
customer_id
string · Optional |
Any text up to 120 characters | null | Your customer reference: find the customer's invoices and refunds by it. |
customer_email
string · Optional |
An email address up to 160 characters | null | Stored in lower case. Never shown to the payer. |
description
string · Optional |
Up to 255 characters; HTML tags are removed | null | Shown to the payer. |
lines
array · Optional |
Up to 100 objects {description, quantity, amount} |
null | Shown on the payer's receipt; display only, never added up. |
allowed_assets
array · Optional |
Asset identifiers | The store's Payment assets (fiat), [currency] (crypto), the splitter's asset |
Ignored on an invoice that settles to a wallet when its store has payment assets (still checked to be a list of 1-20 strings): those are enforced. A store without them lets the request decide: crypto pricing exactly one, equal to currency; fiat pricing needs it (allowed_assets_missing). An empty list is an error. |
expires_in_seconds
integer · Optional |
300–2592000 (clamped, never refused) | 3600 | The store's Invoice expiry wins when it sets one; expires_at in the response is the expiry that applies. |
return_url
string · Optional |
An absolute https:// URL | null | Link on the checkout after payment. |
cancel_url
string · Optional |
An absolute https:// URL | null | Link on the checkout to go back. |
metadata
object · Optional |
A JSON object up to 4096 bytes | null | Returned as sent; the keys settlement_address, settlement_addresses and settlement_asset_addresses are reserved. |
settlement_type
string · Optional |
wallet, splitter |
The store's default: its saved settlement wallets (wallet), or its splitter |
splitter when settlement_target_id is sent, wallet when an address is sent. Wallet invoices settle to your settlement wallets on the per_invoice and daily schedules, into your balance on on_request (the default). |
settlement_target_id
string · Optional |
An active splitter id of this account | The store's default splitter | Refused while the settlement lock is on (422 settlement_address_locked). |
settlement_address
string · Optional |
An address valid for every allowed asset's network; never an address of this platform | The saved settlement wallets | Refused while the settlement lock is on (422 settlement_address_locked). Used by the per_invoice and daily schedules; on on_request (the default) the share goes to your balance. |
settlement_addresses
object · Optional |
Keys: evm, bitcoin, bitcoincash, tron (network families); never an address of this platform |
The saved settlement wallets | Refused while the settlement lock is on (422 settlement_address_locked). Used by the per_invoice and daily schedules; on on_request (the default) the share goes to your balance. |
store_id
string · Optional |
A store id of this account | The API key's store, else your first store | A key restricted to a store may only use that store (403 store_not_allowed). |
checkout_theme
string · Optional |
modern, classic, minimal, midnight, vivid |
The store's design, else modern |
Only the look changes (designs). |
Asset identifiers (allowed_assets) #
Send these exact identifiers in allowed_assets (and in currency when the invoice is priced in crypto). An identifier names the coin and its network, so USDT alone is refused: say USDT_TRON or USDT_ETHEREUM. Upper or lower case both work, and the same identifier is used in sandbox and live. The list below is read from this platform as you open the page: when the operator switches an asset off it disappears here and POST /invoices answers 422 for it.
A store's payment assets, ticked once in the dashboard (Stores → Edit → Payment assets), are enforced on every invoice of that store that settles to a wallet, from the API and the dashboard: a fiat-priced invoice offers exactly those that can be paid in your key's environment, and any allowed_assets the request sends is ignored. A crypto-priced invoice must name one of them as its currency; another coin gets 422 with "reason": "asset_not_in_store". A splitter invoice is paid in the splitter's asset. For a store without payment assets each request chooses: send allowed_assets (a crypto-priced invoice uses its currency, a splitter invoice the splitter's asset). If no payment asset is left for the invoice, POST /invoices answers 422 with "reason": "allowed_assets_missing" and a link to the store page.
"allowed_assets": ["USDT_TRON", "USDC_BASE", "BTC_BITCOIN"]
| Identifier | Asset | Network (live / sandbox) | settlement_addresses key | Decimals | Minimum | Sandbox | Live |
|---|---|---|---|---|---|---|---|
| BTC_BITCOIN | BTC · Bitcoin Native coin |
Bitcoin Bitcoin Testnet |
bitcoinBitcoin |
8 | 0.0001 BTC | Available | Available |
| BCH_BITCOINCASH | BCH · Bitcoin Cash Native coin |
Bitcoin Cash Bitcoin Cash Testnet |
bitcoincashBitcoin Cash |
8 | 0.0001 BCH | Available | Available |
| ETH_ETHEREUM | ETH · Ether Native coin |
Ethereum Ethereum Sepolia |
evmEthereum-type (0x…) |
18 | 0.001 ETH | Available | Available |
| TRX_TRON | TRX · Tron Native coin |
Tron Tron Nile Testnet |
tronTron (T…) |
6 | 1 TRX | Available | Available |
| USDT_ETHEREUM | USDT · Tether USD (ERC-20) ERC-20 token |
Ethereum Ethereum Sepolia |
evmEthereum-type (0x…) |
6 | 1 USDT | No public test token: test it in live | Available |
| USDT_TRON | USDT · Tether USD (TRC-20) TRC-20 token |
Tron Tron Nile Testnet |
tronTron (T…) |
6 | 1 USDT | No public test token: test it in live | Available |
| USDC_ETHEREUM | USDC · USD Coin (Ethereum) ERC-20 token |
Ethereum Ethereum Sepolia |
evmEthereum-type (0x…) |
6 | 1 USDC | Available | Available |
| USDC_BASE | USDC · USD Coin (Base) ERC-20 token |
Base Base Sepolia |
evmEthereum-type (0x…) |
6 | 1 USDC | Available | Available |
| USDC_ARBITRUM | USDC · USD Coin (Arbitrum One) ERC-20 token |
Arbitrum One Arbitrum Sepolia |
evmEthereum-type (0x…) |
6 | 1 USDC | Available | Available |
| USDC_POLYGON | USDC · USD Coin (Polygon PoS) ERC-20 token |
Polygon PoS Polygon Amoy |
evmEthereum-type (0x…) |
6 | 1 USDC | Available | Available |
- Decimals: the most digits after the point an
amountmay have for a crypto-priced invoice. Minimum: the smallest amount an option may ask for. - The
settlement_addresseskey is the kind of network: one address per key covers every asset with that key (one0x…for allevmassets), see where settlements go. - Live also needs your account's live access. Fiat-priced invoices need the operator's price source. Contracts, confirmations and fees: supported assets. Your key's own view:
GET /assets(assets:read), entries withcapabilities.invoice_payment_option: true.
Fiat currencies (currency) #
For "pricing_mode": "fiat" send an ISO-4217 code in currency (USD, EUR, JPY …; upper or lower case). The amount is converted at a locked quote from the operator's price source, so the code must also be one that source quotes; any other code is refused with 422 on currency before a quote is taken.
- Sandbox: Coinbase, 149 of the 155 currencies below.
- Live: Coinbase, 149 of the 155 currencies below.
All 155 currencies
| Code | Currency | Used in | Sandbox | Live |
|---|---|---|---|---|
| AED | UAE Dirham | United Arab Emirates | Priced | Priced |
| AFN | Afghani | Afghanistan | Priced | Priced |
| ALL | Lek | Albania | Priced | Priced |
| AMD | Armenian Dram | Armenia | Priced | Priced |
| AOA | Kwanza | Angola | Priced | Priced |
| ARS | Argentine Peso | Argentina | Priced | Priced |
| AUD | Australian Dollar | Australia | Priced | Priced |
| AWG | Aruban Florin | Aruba | Priced | Priced |
| AZN | Azerbaijan Manat | Azerbaijan | Priced | Priced |
| BAM | Convertible Mark | Bosnia and Herzegovina | Priced | Priced |
| BBD | Barbados Dollar | Barbados | Priced | Priced |
| BDT | Taka | Bangladesh | Priced | Priced |
| BHD | Bahraini Dinar | Bahrain | Priced | Priced |
| BIF | Burundi Franc | Burundi | Priced | Priced |
| BMD | Bermudian Dollar | Bermuda | Priced | Priced |
| BND | Brunei Dollar | Brunei | Priced | Priced |
| BOB | Boliviano | Bolivia | Priced | Priced |
| BRL | Brazilian Real | Brazil | Priced | Priced |
| BSD | Bahamian Dollar | Bahamas | Priced | Priced |
| BTN | Ngultrum | Bhutan | Priced | Priced |
| BWP | Pula | Botswana | Priced | Priced |
| BYN | Belarusian Ruble | Belarus | Priced | Priced |
| BZD | Belize Dollar | Belize | Priced | Priced |
| CAD | Canadian Dollar | Canada | Priced | Priced |
| CDF | Congolese Franc | DR Congo | Priced | Priced |
| CHF | Swiss Franc | Switzerland | Priced | Priced |
| CLP | Chilean Peso | Chile | Priced | Priced |
| CNY | Yuan Renminbi | China | Priced | Priced |
| COP | Colombian Peso | Colombia | Priced | Priced |
| CRC | Costa Rican Colon | Costa Rica | Priced | Priced |
| CUP | Cuban Peso | Cuba | Priced | Priced |
| CVE | Cabo Verde Escudo | Cabo Verde | Priced | Priced |
| CZK | Czech Koruna | Czechia | Priced | Priced |
| DJF | Djibouti Franc | Djibouti | Priced | Priced |
| DKK | Danish Krone | Denmark | Priced | Priced |
| DOP | Dominican Peso | Dominican Republic | Priced | Priced |
| DZD | Algerian Dinar | Algeria | Priced | Priced |
| EGP | Egyptian Pound | Egypt | Priced | Priced |
| ERN | Nakfa | Eritrea | Priced | Priced |
| ETB | Ethiopian Birr | Ethiopia | Priced | Priced |
| EUR | Euro | Euro area | Priced | Priced |
| FJD | Fiji Dollar | Fiji | Priced | Priced |
| FKP | Falkland Islands Pound | Falkland Islands | Priced | Priced |
| GBP | Pound Sterling | United Kingdom | Priced | Priced |
| GEL | Lari | Georgia | Priced | Priced |
| GHS | Ghana Cedi | Ghana | Priced | Priced |
| GIP | Gibraltar Pound | Gibraltar | Priced | Priced |
| GMD | Dalasi | Gambia | Priced | Priced |
| GNF | Guinean Franc | Guinea | Priced | Priced |
| GTQ | Quetzal | Guatemala | Priced | Priced |
| GYD | Guyana Dollar | Guyana | Priced | Priced |
| HKD | Hong Kong Dollar | Hong Kong | Priced | Priced |
| HNL | Lempira | Honduras | Priced | Priced |
| HTG | Gourde | Haiti | Priced | Priced |
| HUF | Forint | Hungary | Priced | Priced |
| IDR | Rupiah | Indonesia | Priced | Priced |
| ILS | New Israeli Sheqel | Israel | Priced | Priced |
| INR | Indian Rupee | India | Priced | Priced |
| IQD | Iraqi Dinar | Iraq | Priced | Priced |
| IRR | Iranian Rial | Iran | Priced | Priced |
| ISK | Iceland Krona | Iceland | Priced | Priced |
| JMD | Jamaican Dollar | Jamaica | Priced | Priced |
| JOD | Jordanian Dinar | Jordan | Priced | Priced |
| JPY | Yen | Japan | Priced | Priced |
| KES | Kenyan Shilling | Kenya | Priced | Priced |
| KGS | Som | Kyrgyzstan | Priced | Priced |
| KHR | Riel | Cambodia | Priced | Priced |
| KMF | Comorian Franc | Comoros | Priced | Priced |
| KPW | North Korean Won | North Korea | No | No |
| KRW | Won | South Korea | Priced | Priced |
| KWD | Kuwaiti Dinar | Kuwait | Priced | Priced |
| KYD | Cayman Islands Dollar | Cayman Islands | Priced | Priced |
| KZT | Tenge | Kazakhstan | Priced | Priced |
| LAK | Lao Kip | Laos | Priced | Priced |
| LBP | Lebanese Pound | Lebanon | Priced | Priced |
| LKR | Sri Lanka Rupee | Sri Lanka | Priced | Priced |
| LRD | Liberian Dollar | Liberia | Priced | Priced |
| LSL | Loti | Lesotho | Priced | Priced |
| LYD | Libyan Dinar | Libya | Priced | Priced |
| MAD | Moroccan Dirham | Morocco | Priced | Priced |
| MDL | Moldovan Leu | Moldova | Priced | Priced |
| MGA | Malagasy Ariary | Madagascar | Priced | Priced |
| MKD | Denar | North Macedonia | Priced | Priced |
| MMK | Kyat | Myanmar | Priced | Priced |
| MNT | Tugrik | Mongolia | Priced | Priced |
| MOP | Pataca | Macao | Priced | Priced |
| MRU | Ouguiya | Mauritania | Priced | Priced |
| MUR | Mauritius Rupee | Mauritius | Priced | Priced |
| MVR | Rufiyaa | Maldives | Priced | Priced |
| MWK | Malawi Kwacha | Malawi | Priced | Priced |
| MXN | Mexican Peso | Mexico | Priced | Priced |
| MYR | Malaysian Ringgit | Malaysia | Priced | Priced |
| MZN | Mozambique Metical | Mozambique | Priced | Priced |
| NAD | Namibia Dollar | Namibia | Priced | Priced |
| NGN | Naira | Nigeria | Priced | Priced |
| NIO | Cordoba Oro | Nicaragua | Priced | Priced |
| NOK | Norwegian Krone | Norway | Priced | Priced |
| NPR | Nepalese Rupee | Nepal | Priced | Priced |
| NZD | New Zealand Dollar | New Zealand | Priced | Priced |
| OMR | Rial Omani | Oman | Priced | Priced |
| PAB | Balboa | Panama | Priced | Priced |
| PEN | Sol | Peru | Priced | Priced |
| PGK | Kina | Papua New Guinea | Priced | Priced |
| PHP | Philippine Peso | Philippines | Priced | Priced |
| PKR | Pakistan Rupee | Pakistan | Priced | Priced |
| PLN | Zloty | Poland | Priced | Priced |
| PYG | Guarani | Paraguay | Priced | Priced |
| QAR | Qatari Rial | Qatar | Priced | Priced |
| RON | Romanian Leu | Romania | Priced | Priced |
| RSD | Serbian Dinar | Serbia | Priced | Priced |
| RUB | Russian Ruble | Russia | Priced | Priced |
| RWF | Rwanda Franc | Rwanda | Priced | Priced |
| SAR | Saudi Riyal | Saudi Arabia | Priced | Priced |
| SBD | Solomon Islands Dollar | Solomon Islands | Priced | Priced |
| SCR | Seychelles Rupee | Seychelles | Priced | Priced |
| SDG | Sudanese Pound | Sudan | Priced | Priced |
| SEK | Swedish Krona | Sweden | Priced | Priced |
| SGD | Singapore Dollar | Singapore | Priced | Priced |
| SHP | Saint Helena Pound | Saint Helena | Priced | Priced |
| SLE | Leone | Sierra Leone | No | No |
| SOS | Somali Shilling | Somalia | Priced | Priced |
| SRD | Surinam Dollar | Suriname | Priced | Priced |
| SSP | South Sudanese Pound | South Sudan | Priced | Priced |
| STN | Dobra | São Tomé and Príncipe | No | No |
| SVC | El Salvador Colon | El Salvador | Priced | Priced |
| SYP | Syrian Pound | Syria | Priced | Priced |
| SZL | Lilangeni | Eswatini | Priced | Priced |
| THB | Baht | Thailand | Priced | Priced |
| TJS | Somoni | Tajikistan | Priced | Priced |
| TMT | Turkmenistan New Manat | Turkmenistan | Priced | Priced |
| TND | Tunisian Dinar | Tunisia | Priced | Priced |
| TOP | Pa’anga | Tonga | Priced | Priced |
| TRY | Turkish Lira | Türkiye | Priced | Priced |
| TTD | Trinidad and Tobago Dollar | Trinidad and Tobago | Priced | Priced |
| TWD | New Taiwan Dollar | Taiwan | Priced | Priced |
| TZS | Tanzanian Shilling | Tanzania | Priced | Priced |
| UAH | Hryvnia | Ukraine | Priced | Priced |
| UGX | Uganda Shilling | Uganda | Priced | Priced |
| USD | US Dollar | United States | Priced | Priced |
| UYU | Peso Uruguayo | Uruguay | Priced | Priced |
| UZS | Uzbekistan Sum | Uzbekistan | Priced | Priced |
| VED | Bolívar Soberano | Venezuela | No | No |
| VES | Bolívar Soberano | Venezuela | Priced | Priced |
| VND | Dong | Vietnam | Priced | Priced |
| VUV | Vatu | Vanuatu | Priced | Priced |
| WST | Tala | Samoa | Priced | Priced |
| XAF | CFA Franc BEAC | Central Africa | Priced | Priced |
| XCD | East Caribbean Dollar | Eastern Caribbean | Priced | Priced |
| XCG | Caribbean Guilder | Curaçao, Sint Maarten | No | No |
| XOF | CFA Franc BCEAO | West Africa | Priced | Priced |
| XPF | CFP Franc | French Pacific territories | Priced | Priced |
| YER | Yemeni Rial | Yemen | Priced | Priced |
| ZAR | Rand | South Africa | Priced | Priced |
| ZMW | Zambian Kwacha | Zambia | Priced | Priced |
| ZWG | Zimbabwe Gold | Zimbabwe | No | No |
Duplicate protection: order_id #
Send your order number as order_id. Per store and environment, compared exactly (case-sensitive). Only the terms of the order are compared:
pricing_mode;currency, in any case (usd=USD);amount, as a number (25=25.00);allowed_assets, as a set (order and case do not matter), and only when your request chose them. The store's payment assets and its default splitter are settings: changing them never refuses the same order.
| The order already has… | Answer |
|---|---|
| no invoice in this store | 201, a new invoice, "created": true |
an open invoice with the same terms. Open means awaiting payment and not past its expires_at, confirming, or partially paid. | 200 with that invoice, "created": false: nothing new, no webhook |
| an open invoice with other terms, partially paid ones included | 409 order_id_in_use with existing_invoice and differences |
| an invoice that received money: paid, overpaid, paid late, underpaid, refund pending, partially refunded or refunded, or expired or cancelled with confirmed money | 409 order_already_paid: do not charge the order twice |
only invoices that expired or were cancelled without confirmed money, or one still awaiting payment after its expires_at | 201, a new invoice |
On a repeat every other field is ignored: description, lines, metadata, customer ID and email, return and cancel URLs, expiry, checkout theme and settlement (type, splitter, addresses). You get the original invoice back unchanged, and its expiry is not extended. A splitter you name counts only when it decides the allowed assets (fiat pricing without allowed_assets): then its asset is compared. To change anything else, cancel the open invoice and create a new one.
So a timeout is harmless: send the same request again. Without order_id every request is a new invoice (an Idempotency-Key then protects the retry).
Example #
A 49.99 USD invoice for order 1042, payable in the store's payment assets and settled the way your settlement schedule says (into your balance on on_request, the default; to your settlement wallets on per_invoice and daily):
curl -X POST https://cryptonera.com/api/v1/invoices \
-H "Authorization: Bearer $CRYPTONERA_KEY" \
-H "Content-Type: application/json" \
-d '{
"pricing_mode": "fiat",
"currency": "USD",
"amount": "49.99",
"order_id": "order-1042",
"customer_email": "[email protected]",
"expires_in_seconds": 3600
}'
<?php
$body = [
'pricing_mode' => 'fiat',
'currency' => 'USD',
'amount' => '49.99', // a string, never a float
'order_id' => 'order-1042', // your order number
'customer_email' => '[email protected]',
];
$ch = curl_init('https://cryptonera.com/api/v1/invoices');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 20,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('CRYPTONERA_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode($body, JSON_THROW_ON_ERROR),
]);
$raw = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
if ($raw === false) {
// network error: send the same request again (order_id makes it safe)
}
$invoice = json_decode((string) $raw, true);
if ($status === 201 || $status === 200) {
// 200: this order already has an open invoice with the same terms ("created": false)
header('Location: '.$invoice['checkout_url']);
} else {
error_log($invoice['error']['code'].': '.$invoice['error']['message']);
}
// Node.js 18+ (server side only: never put your API key in a browser)
const res = await fetch('https://cryptonera.com/api/v1/invoices', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CRYPTONERA_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
pricing_mode: 'fiat',
currency: 'USD',
amount: '49.99', // a string, never a number
order_id: 'order-1042',
customer_email: '[email protected]',
}),
});
const invoice = await res.json();
if (res.status === 201 || res.status === 200) {
// send the payer to invoice.checkout_url; keep invoice.id and invoice.transaction_id
} else {
console.error(invoice.error.code, invoice.error.message);
}
Response 201 Created:
{
"id": "01J9Z3A1B2C3D4E5F6G7H8J9KM",
"object": "invoice",
"transaction_id": "TST-7K3M-9QX2-H4PD",
"number": "INV-MAIN-000123",
"order_id": "order-1042",
"store_id": "01J9Z2K7QH4M8N3P5R6S7T8V9W",
"environment": "sandbox",
"status": "awaiting_payment",
"settlement_status": "not_started",
"pricing_mode": "fiat",
"currency": "USD",
"amount": "49.99",
"amount_asset": null,
"amount_paid": "0",
"amount_pending": "0",
"amount_due": null,
"needs_decision": false,
"decision": null,
"description": null,
"lines": null,
"customer_id": null,
"customer_email": "[email protected]",
"allowed_assets": ["USDT_TRON", "USDC_BASE"],
"settlement_type": "wallet",
"rate_source": "coingecko",
"payment_options": [
{
"id": "01J9Z3A1B2C3D4E5F6G7H8J9KN",
"asset": "USDT_TRON",
"status": "pending_address",
"required_amount": "49.990000",
"received_confirmed": "0.000000",
"received_unconfirmed": "0.000000",
"address": null,
"quote_expires_at": "2026-10-12T11:00:00+00:00"
},
{
"id": "01J9Z3A1B2C3D4E5F6G7H8J9KP",
"asset": "USDC_BASE",
"status": "pending_address",
"required_amount": "49.990000",
"received_confirmed": "0.000000",
"received_unconfirmed": "0.000000",
"address": null,
"quote_expires_at": "2026-10-12T11:00:00+00:00"
}
],
"payer_addresses": [],
"refund": null,
"metadata": {},
"return_url": null,
"cancel_url": null,
"checkout_url": "https://cryptonera.com/pay/i/<public-token>",
"checkout_theme": "modern",
"expires_at": "2026-10-12T11:00:00+00:00",
"paid_at": null,
"cancelled_at": null,
"created_at": "2026-10-12T10:00:00+00:00",
"created": true,
"request_id": "req_01J9Z3A1B2C3D4E5F6G7H8J9KQ"
}
The same order again while this invoice is open, with the same terms: 200, the same object and "created": false. With other terms:
{
"error": {
"code": "order_id_in_use",
"message": "Order \"order-1042\" already has open invoice INV-MAIN-000123 (TST-7K3M-9QX2-H4PD) in this store with a different amount. Cancel it first, or use another order_id.",
"retryable": false,
"request_id": "req_01J9Z3A1B2C3D4E5F6G7H8J9KR",
"order_id": "order-1042",
"existing_invoice": {
"id": "01J9Z3A1B2C3D4E5F6G7H8J9KM",
"transaction_id": "TST-7K3M-9QX2-H4PD",
"number": "INV-MAIN-000123",
"status": "awaiting_payment",
"amount": "49.99",
"currency": "USD",
"allowed_assets": ["USDT_TRON", "USDC_BASE"],
"checkout_url": "https://cryptonera.com/pay/i/<public-token>",
"created_at": "2026-10-12T10:00:00+00:00"
},
"differences": ["amount"]
}
}
Send the payer to checkout_url (or show it in the widget); keep id and transaction_id with your order. A payment option's address is allocated when the payer chooses it, so payment_options[].address is null until then. To settle through a splitter instead of your wallets, send "settlement_type": "splitter" and "settlement_target_id".
Response: the invoice object #
Every key is always present (null when empty), in this order. POST /invoices adds created; every answer adds request_id.
| Field | Type | Meaning |
|---|---|---|
| id | string | Payment ID (ULID, 26 characters). |
| object | string | Always invoice. |
| transaction_id | string or null | Platform reference TRX-XXXX-XXXX-XXXX (sandbox TST-…): quote it to support; GET /invoices/{id} takes it too. |
| number | string | Invoice number shown to the payer (INV-MAIN-000123), unique per store and environment. |
| order_id | string or null | Your Merchant order ID, as sent (cleaned). |
| store_id | string or null | The store (also data-store of the website widget). |
| environment | string | The environment of the object (the API key's). |
| status | string | See invoice statuses. |
| settlement_status | string | Where your share is: see settlement status. |
| pricing_mode | string | crypto: priced in one asset; fiat: priced in a currency and quoted per option. |
| currency | string | The asset identifier (crypto) or ISO-4217 code (fiat) of amount. |
| amount | string | The price as sent (decimal string in currency). |
| amount_asset | string or null | The asset of the four amounts below: the option paid, else the only option; null while an invoice of several assets is unpaid. |
| amount_paid | string | Confirmed money received in amount_asset (late, excess and held payments included; "0" without an asset). Decimal string in the asset's units. |
| amount_pending | string | Payments seen, not confirmed yet. Decimal string in the asset's units. |
| amount_due | string or null | Still missing while open: 0 once paid or accepted; null without amount_asset. Decimal string in the asset's units. |
| needs_decision | boolean | true while held money waits for your accept (POST /invoices/{id}/accept) or refund (POST /refunds). |
| decision | object or null | The decision about held money; null when it never needed one. |
| description | string or null | Shown to the payer. |
| lines | array of objects or null | Receipt lines as sent (display only). |
| customer_id | string or null | Your customer reference, as sent. |
| customer_email | string or null | Lower-cased; never shown to the payer. |
| allowed_assets | array of strings | The assets the payer may choose from. |
| settlement_type | string or null | wallet: to your settlement wallets (or balance); splitter: through a splitter. |
| rate_source | string or null | Fiat invoices: the price source of the quotes. |
| payment_options | array of PaymentOption | One per allowed asset: see payment option. |
| payer_addresses | array of PayerAddress | Addresses its confirmed payments came from (largest first): where a refund goes back to. |
| refund | object or null | The invoice's refund (one per invoice), or null. |
| metadata | object | Returned as sent (reserved settlement keys removed). |
| return_url | string or null | Link on the checkout after payment. |
| cancel_url | string or null | Link on the checkout to go back. |
| checkout_url | string | The payer's page (/pay/i/{token}): keep it out of analytics and shared URLs. |
| checkout_theme | string | The design the payer sees: see checkout designs. |
| expires_at | string | The expiry that applies (the store's Invoice expiry, else expires_in_seconds, else one hour); later payments are held as late. |
| paid_at | string or null | When it became paid. |
| cancelled_at | string or null | When it was cancelled. |
| created_at | string | When it was created. |
The payment option object #
| Field | Type | Meaning |
|---|---|---|
| id | string | Payment option id. |
| asset | string | The asset the payer may pay with (identifier, e.g. USDT_TRON). |
| status | string | The option's state. |
| required_amount | string | What the payer must send with this option. Decimal string in the asset's units. |
| received_confirmed | string | Confirmed so far. Decimal string in the asset's units. |
| received_unconfirmed | string | Seen, not confirmed yet. Decimal string in the asset's units. |
| address | string or null | The deposit address; null until the payer chooses this option on the checkout. |
| quote_expires_at | string or null | Fiat invoices: when the quoted rate expires. |
Which payment is which #
Every invoice is one customer payment. Keep these identifiers next to your order:
| Identifier | Where you get it | Use it for |
|---|---|---|
Transaction ID transaction_id (TRX-7K3M-9QX2-H4PD; sandbox TST-…) | POST /invoices; every invoice, deposit, settlement and refund object and their webhooks; a payout to a past payer carries the paid invoice's as matched_transaction_id (its payout.* events carry matched_invoice_id); shown to the payer on the checkout and the receipt | Quote it to support and to your customer; search it anywhere in the dashboard; GET /invoices/{transaction_id}; ?transaction_id= (any case, dashes optional) |
Payment ID: the invoice id (26 characters) | The response; resource.id of invoice.*; invoice_id elsewhere; widget events | API look-ups; the widget's data-invoice-id |
Merchant order ID order_id | You send it | Matching to your orders; duplicate protection; ?order_id= |
Invoice number (INV-MAIN-000123) | The checkout and the receipt | Talking to the payer; unique per store and environment |
customer_id, customer_email | You send them | Finding refunds; allowing payouts to known customers |
payer_address | Deposits, the invoice's payer_addresses (when the network source names the sender; not on Bitcoin Cash) | Where refunds go; payouts to past payers |
Payment tx_hash + explorer_url | Deposits, deposit.* | Proof the customer paid (several partial payments are possible) |
Transfer tx_hash + explorer_url | transfers[] of settlements; tx_hash and explorer_url of refunds and payouts; settlement.transfer_confirmed, refund.completed, payout.completed | Proof the money left: a daily settlement or a withdrawal pays many invoices in one transfer |
In the dashboard, Payments → Invoices and Money → Settlements, Refunds and Payouts find a payment by any of these.
Where settlements go (settlement wallets) #
A wallet-settled invoice ("settlement_type": "wallet") on the per_invoice and daily settlement schedules pays the confirmed amount, minus the platform fee, to your address on the network the customer paid on (daily: with the next daily settlement). On on_request, the default schedule, your share is credited to your balance instead and this address is not used: a withdrawal goes to the settlement wallet saved when you withdraw (or, while the lock is off, to the destination_address you send). Either way an invoice needs one address for every allowed asset. Save them once in the dashboard (Payments → Settlement wallets) and send none: with the settlement lock on (the default) those are the only addresses used. The address is the first of these that has one (1 and 2 only while the lock is off; on on_request an address sent in the request is used only if a payment has to be settled on its own because your collection address is not ready yet):
| # | Source | Example |
|---|---|---|
| 1 | lock off settlement_addresses in the request: one address per network key of the allowed assets | "settlement_addresses": {"evm": "0x…", "tron": "T…", "bitcoin": "bc1…"} |
| 2 | lock off settlement_address in the request: one address for every allowed asset (one kind of network) | "settlement_address": "T…" |
| 3 | Your saved address for that asset: Dashboard → Payments → Settlement wallets → "a different address for one asset" | USDC on Base to an exchange account |
| 4 | Your saved address for that kind of network (same page) | one 0x… for every EVM asset |
Settlement wallets are kept separately for sandbox and live: switch the dashboard to the environment of your API key (sidebar → Environment) before saving. The address is copied into the invoice when it is created, so on the per_invoice and daily schedules later changes never move that invoice's settlement; on on_request money already in your balance goes wherever your withdrawal goes. An address of this platform (a payment, collection or payout wallet address it holds the keys for) is never accepted as a settlement address, saved or sent: the money would stay in the platform's custody instead of reaching you. A request that sends no settlement_type (and no splitter or address) gets the store's default: its saved settlement wallets, or its splitter (Dashboard → Stores → Edit → Default settlement for invoices: "Settlement wallets" or "Splitter rule"). "settlement_type": "wallet" settles to the saved wallets even when the store's default is a splitter. "settlement_type": "splitter" without an id uses the store's default splitter (a store set to Settlement wallets has none: 422 store_settlement_invalid); naming one with "settlement_target_id" works only while the lock is off. Invoices created in the dashboard always use the saved settlement wallets (they never name an address, lock on or off), or a splitter chosen on the form.
When an allowed asset has no address from any of these, POST /invoices answers 422 with the fields and a hint saying exactly where to add it (the same in sandbox, plus a note about test networks there):
{
"error": {
"code": "validation_failed",
"message": "The request is invalid.",
"retryable": false,
"request_id": "req_…",
"fields": {
"settlement_addresses.tron": [
"No settlement wallet is saved for USDT_TRON: save a Tron (T…) address under Settlement wallets."
]
},
"reason": "settlement_destination_missing",
"environment": "sandbox",
"settlement_lock": true,
"missing": [
{
"family": "tron",
"label": "Tron (T…)",
"assets": ["USDT_TRON"]
}
],
"hint": "This invoice has no settlement address for USDT_TRON … open Payments → Settlement wallets and save the address …",
"dashboard_url": "https://cryptonera.com/dashboard/settlement-wallets",
"docs_url": "https://cryptonera.com/developers#billing-settlement-lock"
}
}
This error does not use up the Idempotency-Key: save the address, then retry the same request with the same key. (With the lock off the message also offers sending settlement_addresses, and docs_url ends in #billing-settlement.)
Settlement lock #
Every account starts with the settlement lock on, separately for sandbox and live (Dashboard → Payments → Settlement wallets). While it is on, invoices are settled only to the settlement wallets saved in the dashboard, and a request that names a settlement address or a splitter is refused. So if someone changes the code of your website, or gets hold of an API key, they still cannot send your payments to their own address. Only the account owner can turn the lock off (password confirmed; recorded in the audit log).
While the lock is on, POST /invoices refuses settlement_address, settlement_addresses and settlement_target_id (empty values count as not sent) with 422, before anything is created. missing lists the allowed assets that also have no saved wallet yet (never the saved addresses):
{
"error": {
"code": "validation_failed",
"message": "The request is invalid.",
"retryable": false,
"request_id": "req_…",
"fields": {
"settlement_address": [
"Not accepted: this account pays out only to its saved settlement wallets (Dashboard → Payments → Settlement wallets). Remove this field."
]
},
"reason": "settlement_address_locked",
"environment": "live",
"settlement_lock": true,
"missing": [],
"hint": "Settlements of this account are locked to the settlement wallets saved in the dashboard, so \"settlement_address\" cannot be sent. Remove it: …",
"dashboard_url": "https://cryptonera.com/dashboard/settlement-wallets",
"docs_url": "https://cryptonera.com/developers#billing-settlement-lock"
}
}
Remove the field and retry; the Idempotency-Key is not used up. The lock covers invoices: PATCH /splitters/{id} is refused the same way (field recipients) for a splitter that settles invoices (a store's default settlement, or the target of an invoice not settled yet), and a store's default splitter is used only for invoices of its own environment. Withdrawals of your balance (POST /withdrawals) go only to the saved settlement wallets while the lock is on. Refunds created through the API go back only to an address that paid the invoice (another address only when no paying address is known and Refunds → Refund settings → "Allow a refund address from the API" is on), and they are sent at once unless "Refunds from the API need approval" is on. Payouts from the API go only to the destinations Payouts → Payout settings allow (by default, addresses that paid you before), and they wait for a finance member's approval only while "API payouts need approval" is on. Split sessions and new splitters created through the API name their own recipients: give an API key only the scopes it needs (an invoice integration needs invoices:write, not refunds:write, payouts:write, splitters:write or sessions:write).
Checkout designs (checkout_theme) #
The hosted checkout (checkout_url) comes in 5 designs. Only the look changes: every design shows the same amounts, address, QR code, warnings and live status. Choose one per invoice with "checkout_theme" when you create it; without it the invoice uses its store's default design (Dashboard → Stores), else modern. The invoice response echoes the design in checkout_theme.
"checkout_theme": "modern"Clean, centred payment card with a progress bar and steps. The default.
Preview: choose coin · pay · confirming · paid · expired
"checkout_theme": "classic"Conservative and wide, with a solid header in your brand colour.
Preview: choose coin · pay · confirming · paid · expired
"checkout_theme": "minimal"Plain white page, thin lines and plenty of space.
Preview: choose coin · pay · confirming · paid · expired
"checkout_theme": "midnight"Dark mode with soft glows, easy on the eyes at night.
Preview: choose coin · pay · confirming · paid · expired
"checkout_theme": "vivid"Your brand colours as a bold gradient behind a glass card.
Preview: choose coin · pay · confirming · paid · expired
List the designs with their preview links from the API (any key with invoices:read):
GET https://cryptonera.com/api/v1/checkout-themes
Authorization: Bearer ck_sandbox_Ab12Cd34Ef56_…
{
"object": "list",
"default": "modern",
"data": [
{
"object": "checkout_theme",
"key": "modern",
"name": "Modern",
"description": "Clean, centred payment card with a progress bar and steps. The default.",
"default": true,
"preview_url": "https://cryptonera.com/pay/preview/modern/pay",
"preview_urls": {
"choose": "…",
"pay": "…",
"confirming": "…",
"paid": "…",
"expired": "…"
}
},
{
"object": "checkout_theme",
"key": "classic",
"name": "Classic",
"description": "Conservative and wide, with a solid header in your brand colour.",
"default": false,
"preview_url": "https://cryptonera.com/pay/preview/classic/pay",
"preview_urls": {
"choose": "…",
"pay": "…",
"confirming": "…",
"paid": "…",
"expired": "…"
}
},
…
],
"request_id": "req_…"
}
To show one invoice in another design without creating it again, add ?theme=<key> to its checkout_url (display only; an unknown key is ignored). The same designs are available in the embedded checkout widget.
3. Simple integration with the widget #
Embedded checkout widget #
4. Other endpoints #
Every Crypto Billing endpoint and the ones both products share. The scope and the Idempotency-Key rule come from the platform's routes.
| Endpoint | Scope | Idempotency-Key | Purpose |
|---|---|---|---|
| Invoices | |||
| GET /checkout-themes | invoices:read | — | List the checkout designs |
| GET /invoices | invoices:read | — | List invoices |
| POST /invoices | invoices:write | Optional | Create an invoice |
| GET /invoices/{id} | invoices:read | — | Read an invoice |
| POST /invoices/{id}/accept | invoices:write | Required | Accept held money |
| POST /invoices/{id}/cancel | invoices:write | Optional | Cancel an invoice |
| Refunds | |||
| GET /refunds | refunds:read | — | List refunds |
| POST /refunds | refunds:write | Required | Refund an invoice |
| GET /refunds/{id} | refunds:read | — | Read a refund |
| POST /refunds/{id}/cancel | refunds:write | Optional | Cancel a refund |
| Payouts | |||
| GET /payout-wallets | payouts:read | — | List the payout wallets |
| POST /payout-wallets | payouts:write | Optional | Create a payout wallet |
| GET /payouts | payouts:read | — | List payouts |
| POST /payouts | payouts:write | Required | Pay a customer |
| GET /payouts/{id} | payouts:read | — | Read a payout |
| POST /payouts/{id}/cancel | payouts:write | Optional | Cancel a payout |
| Settlements | |||
| GET /settlements | activity:read | — | List settlements |
| GET /settlements/{id} | activity:read | — | Read a settlement |
| GET /withdrawals | balances:read | — | List withdrawals |
| POST /withdrawals | balances:withdraw | Required | Withdraw your balance |
| GET /withdrawals/{id} | balances:read | — | Read a withdrawal |
| POST /withdrawals/{id}/cancel | balances:withdraw | Optional | Cancel a withdrawal |
| Balances | |||
| GET /balances | balances:read | — | Read your balances |
| Activity | |||
| GET /deposits | activity:read | — | List deposits |
| Assets | |||
| GET /assets | assets:read | — | List the assets |
| GET /fee-policy | fees:read | — | Read your fee policy |
| Webhooks | |||
| GET /webhook-endpoints | webhooks:read | — | List webhook endpoints |
| POST /webhook-endpoints | webhooks:write | Optional | Create a webhook endpoint |
| GET /webhook-endpoints/{id} | webhooks:read | — | Read a webhook endpoint |
| PATCH /webhook-endpoints/{id} | webhooks:write | — | Change a webhook endpoint |
| DELETE /webhook-endpoints/{id} | webhooks:write | — | Disable a webhook endpoint |
| GET /webhook-events | webhooks:read | — | List webhook events |
| POST /webhook-events/{id}/replay | webhooks:write | Optional | Replay a webhook event |
| Wallets | |||
| GET /wallets | wallets:read | — | List wallets |
| POST /wallets | wallets:write | Optional | Create a wallet |
Find, cancel and accept invoices #
GET /invoices lists invoices newest first; GET /invoices/{id} and the actions take the Payment ID or the transaction_id (any case, dashes optional). Filters of the list:
| Filter | Type | Allowed values | Notes |
|---|---|---|---|
| ?status= | string | awaiting_payment, confirming, partially_paid, paid, overpaid, expired, underpaid, paid_late, cancelled, refund_pending, partially_refunded, refunded | Only this status. |
| ?store_id= | string | Only this store (a key restricted to a store lists only its own; another store: 403 store_not_allowed). | |
| ?order_id= | string | Merchant order ID (exact). | |
| ?transaction_id= | string | Transaction ID (any case, dashes optional; not a transaction id: no results). | |
| ?number= | string | Invoice number. | |
| ?customer_id= | string | Customer ID. | |
| ?customer_email= | string | Customer email (any case). | |
| ?needs_decision= | boolean | true: only invoices whose held money waits for your decision; false: the others (1/0 too). |
POST /invoices/{id}/cancel cancels an open invoice (a partly paid one becomes underpaid, its confirmed money held for your decision):
| Field | Allowed values | Default | Notes |
|---|---|---|---|
reason
string · Optional |
up to 255 characters | — | Why (audit log, invoice.cancelled). Default: api. |
Partial, late and extra payments. Only confirmed money counts. An invoice that closes with a partial payment becomes underpaid; money that arrives after it closed is held and the invoice becomes underpaid or paid_late; an excess, or a payment at a second option, is held too. Held money waits for your decision (needs_decision: true, decision.status: needed, webhooks invoice.underpaid, invoice.late_payment, invoice.paid_late, invoice.payment_held): accept it with POST /invoices/{id}/accept, or refund it. Accepting settles it like a payment with the invoice's fee (an underpaid or paid-late invoice becomes paid: invoice.accepted, then invoice.paid); it answers 202 and is planned within about a minute (invoice.accept_failed when it cannot be). An open, partly paid invoice can be accepted early (it closes at once). Body:
| Field | Allowed values | Default | Notes |
|---|---|---|---|
asset
string · Optional |
up to 40 characters | — | The asset; required when money is held in several assets. |
expected_amount
string · Optional |
up to 64 characters | — | The held amount you saw (decision.held[].amount); 409 held_amount_changed when it differs. Recommended. |
reason
string · Optional |
up to 500 characters | — | Your note (audit log). |
The decision object #
| Field | Type | Meaning |
|---|---|---|
| status | string | Where the decision stands. |
| reason | string or null | Why money is held. |
| needed_since | string or null | Since when it needs your decision. |
| held | array of HeldAmount | What an accept or a refund would move, per asset. |
| actions | array of strings | What you can do now (empty unless needed). |
| acceptance | object or null | The latest accept, or null. |
Refunds #
- Only to an address that paid the invoice (by default the payer of its largest confirmed payment); another address only when none is known and the owner allows it.
- Once per invoice, at most what was paid. A fiat-priced invoice is refunded in its currency, converted at the current rate (rounded down; the quote is stored); another token sent to its address (no payment option of the invoice) in that token.
- Paid from the invoice's held money when it covers the refund and its network fee, else from your balance (or, if you ask, your payout wallet). The customer receives the exact amount; the network fee is charged to you on top. No commission.
- Not enough in your balance:
202with statusqueued(refund.queuedand an email); it is sent automatically, oldest first, once the balance suffices. You may cancel it until then. If the invoice's confirmed payment no longer covers it by then (a payment reorganized away), it fails withpaid_amount_changedand nothing is taken. - With "Refunds from the API need approval" on, an API refund waits as
pending_approvalfor a finance user in the dashboard.
| Field | Allowed values | Default | Notes |
|---|---|---|---|
invoice_id
string · Optional |
up to 64 characters | — | The invoice (Payment ID); or transaction_id. |
transaction_id
string · Optional |
up to 64 characters | — | The invoice's Transaction ID (any case, dashes optional); or invoice_id. |
amount
string · Optional |
up to 64 characters | — | In the invoice currency (fiat: its minor unit; crypto: the asset's decimals); an asset a fiat invoice has no payment option for (another token sent to its address) in that asset, up to what was received. Default: the most refundable. |
asset
string · Optional |
up to 40 characters | — | Which payment, when the invoice was paid in several assets. |
destination
string · Optional |
up to 128 characters | — | An address that paid the invoice (payer_addresses). Default: the payer of its largest payment. |
destination_memo
string · Optional |
up to 64 characters | — | Memo or tag. |
funding_source
string · Optional |
balance, payout_wallet |
— | Pay it from your balance (default) or your payout wallet; ignored when the invoice's held payment pays it. |
reason
string · Optional |
up to 500 characters | — | Your note. |
# a new UUID for every refund; the same one only to retry it
curl -X POST https://cryptonera.com/api/v1/refunds \
-H "Authorization: Bearer $CRYPTONERA_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 3f1c2a9e-8d4b-4c6f-9e21-7a5b0c3d4e6f" \
-d '{
"transaction_id": "TST-7K3M-9QX2-H4PD",
"amount": "20.00",
"reason": "Order 1042 returned"
}'
Response 201 Created:
{
"id": "01J9Z4B2C3D4E5F6G7H8J9KMNP",
"object": "refund",
"status": "processing",
"status_reason": null,
"environment": "sandbox",
"invoice_id": "01J9Z3A1B2C3D4E5F6G7H8J9KM",
"transaction_id": "TST-7K3M-9QX2-H4PD",
"invoice_number": "INV-MAIN-000123",
"order_id": "order-1042",
"customer_id": null,
"customer_email": "[email protected]",
"asset": "USDT_TRON",
"amount": "20.000000",
"fiat_currency": "USD",
"fiat_amount": "20.00",
"quote": {
"rate": "1.000000",
"source": "coingecko",
"fetched_at": "2026-10-14T09:00:00+00:00"
},
"network_fee": "1.100000",
"network_fee_final": true,
"total_charged": "21.100000",
"funding_source": "balance",
"destination": "<payer-tron-address>",
"destination_memo": null,
"destination_type": "payer_address",
"reason": "Order 1042 returned",
"created_via": "api",
"settlement_id": "01J9Z4B2C3D4E5F6G7H8J9KMNQ",
"tx_hash": null,
"explorer_url": null,
"funds_returned": false,
"created_at": "2026-10-14T09:00:00+00:00",
"approved_at": null,
"funded_at": "2026-10-14T09:00:00+00:00",
"completed_at": null,
"failed_at": null,
"cancelled_at": null,
"request_id": "req_01J9Z4B2C3D4E5F6G7H8J9KMNR"
}
The refund object #
| Field | Type | Meaning |
|---|---|---|
| id | string | Refund id. |
| object | string | Always refund. |
| status | string | See refund statuses. |
| status_reason | string or null | Why it waits, failed or was cancelled: insufficient_balance, network_fee_unavailable, no_collection_address, payout_wallet_missing, held_funds_on_several_addresses, held_funds_changed, paid_amount_changed (at funding or approval the invoice's confirmed payment no longer covered it, e.g. a payment reorganized away; nothing was taken), transfer_failed, transfer_failed_funds_returned, cancelled_by_merchant, superseded (an older refund of the invoice from before one refund per invoice), or a transfer wait. |
| environment | string | The environment of the object (the API key's). |
| invoice_id | string | The invoice refunded. |
| transaction_id | string or null | Its Transaction ID. |
| invoice_number | string or null | Its number. |
| order_id | string or null | Its Merchant order ID. |
| customer_id | string or null | Its customer ID. |
| customer_email | string or null | Its customer email. |
| asset | string | The asset sent back. |
| amount | string | What the payer receives (exactly). Decimal string in the asset's units. |
| fiat_currency | string or null | Fiat invoices: the invoice currency. |
| fiat_amount | string or null | Fiat invoices: the amount refunded in that currency (amount = fiat_amount / quote.rate, rounded down). |
| quote | object or null | Fiat invoices: the current rate it was converted at when created. |
| network_fee | string or null | Charged to you on top (null before funding): a token's fee, or a coin's reserve until the transfer confirmed, then its actual cost. Decimal string in the asset's units. |
| network_fee_final | boolean | network_fee is final. |
| total_charged | string or null | amount + network_fee: what left the funding account. Decimal string in the asset's units. |
| funding_source | string | Where the money comes from. |
| destination | string | Where it goes: an address that paid the invoice. |
| destination_memo | string or null | Memo or tag of the destination. |
| destination_type | string | Why this destination. |
| reason | string or null | Your note. |
| created_via | string | Where it was created. |
| settlement_id | string or null | The settlement that sends it (GET /settlements/{id}, kind refund). |
| tx_hash | string or null | The transfer's transaction hash, once sent. |
| explorer_url | string or null | Block explorer link of tx_hash. |
| funds_returned | boolean | A failed refund whose money went back where it came from: the invoice can be refunded again. |
| created_at | string or null | When it was created. |
| approved_at | string or null | When it was approved. |
| funded_at | string or null | When its money was set aside. |
| completed_at | string or null | When its transfer confirmed. |
| failed_at | string or null | When it failed. |
| cancelled_at | string or null | When it was cancelled. |
GET /refunds filters (exact):
| Filter | Type | Allowed values | Notes |
|---|---|---|---|
| ?status= | string | pending_approval, queued, processing, completed, failed, cancelled | Only this status. |
| ?asset= | string | up to 40 characters | Asset identifier. |
| ?invoice_id= | string | The invoice (Payment ID). | |
| ?transaction_id= | string | The invoice's Transaction ID (any case, dashes optional). | |
| ?order_id= | string | Merchant order ID. | |
| ?customer_id= | string | Customer ID. | |
| ?customer_email= | string | Customer email (any case). | |
| ?destination= | string | up to 128 characters | Destination address. |
| ?tx_hash= | string | up to 128 characters | The refund transfer's hash. |
POST /refunds/{id}/cancel cancels a refund that is still queued or pending_approval.
Payouts to your customers #
A payout pays one of your customers (a cashback, winnings, a refund outside an invoice) from your payout wallet: one address per chain family and environment (one for every EVM network), created with POST /payout-wallets and funded by sending money to it or with "Move from balance" in the dashboard. On EVM and Tron it holds tokens only: ETH, POL or TRX sent there fund your gas budget. Your settings (Payouts → Settings) decide where payouts may go: by default only addresses that paid you before; also customers with a paid invoice by customer_id or customer_email, or any address; payouts from the API on or off; approval in the dashboard. The customer receives the exact amount; the network fee is charged on top. Not enough in the payout wallet: 409 insufficient_balance and nothing is created (payouts are never queued).
| Field | Allowed values | Default | Notes |
|---|---|---|---|
asset
string · Required |
up to 40 characters | — | Asset identifier (tokens on EVM and Tron; BTC, BCH). |
amount
string · Required |
up to 64 characters | — | What the customer receives (at most the asset's decimals). |
destination
string · Required |
up to 128 characters | — | The customer's address. |
destination_memo
string · Optional |
up to 64 characters | — | Memo or tag. |
customer_id
string · Optional |
up to 120 characters | — | Your customer's id (allows any address of a customer with a paid invoice, when that basis is on). |
customer_email
string · Optional |
up to 160 characters; an email address | — | Your customer's email (the same rule by email). |
order_id
string · Optional |
up to 120 characters | — | Your Merchant order ID for this payout. |
note
string · Optional |
up to 255 characters | — | Your note. |
# a new UUID for every payout; the same one only to retry it
curl -X POST https://cryptonera.com/api/v1/payouts \
-H "Authorization: Bearer $CRYPTONERA_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 9b2e4c71-5a3d-4f08-b6e2-1c7d9a0f3e54" \
-d '{
"asset": "USDT_TRON",
"amount": "15.00",
"destination": "<customer-tron-address>",
"customer_id": "cust-77",
"note": "Cashback October"
}'
Response 201 Created:
{
"id": "01J9Z5C3D4E5F6G7H8J9KMNPQR",
"object": "payout",
"status": "processing",
"status_reason": null,
"environment": "sandbox",
"asset": "USDT_TRON",
"amount": "15.000000",
"network_fee": "1.100000",
"network_fee_final": true,
"total_charged": "16.100000",
"destination": "<customer-tron-address>",
"destination_memo": null,
"destination_basis": "past_payer",
"matched_invoice_id": "01J9Z3A1B2C3D4E5F6G7H8J9KM",
"matched_transaction_id": "TST-7K3M-9QX2-H4PD",
"customer_id": "cust-77",
"customer_email": null,
"order_id": null,
"note": "Cashback October",
"created_via": "api",
"settlement_id": "01J9Z5C3D4E5F6G7H8J9KMNPQS",
"tx_hash": null,
"explorer_url": null,
"funds_returned": false,
"created_at": "2026-10-14T10:00:00+00:00",
"approved_at": null,
"funded_at": "2026-10-14T10:00:00+00:00",
"completed_at": null,
"failed_at": null,
"cancelled_at": null,
"request_id": "req_01J9Z5C3D4E5F6G7H8J9KMNPQT"
}
The payout object #
| Field | Type | Meaning |
|---|---|---|
| id | string | Payout id. |
| object | string | Always payout. |
| status | string | See payout statuses. |
| status_reason | string or null | Why it failed or waits: transfer_failed, transfer_failed_funds_returned, cancelled_by_merchant, or a transfer wait. |
| environment | string | The environment of the object (the API key's). |
| asset | string | The asset paid. |
| amount | string | What the customer receives (exactly). Decimal string in the asset's units. |
| network_fee | string or null | Charged on top: a token's fee (0 when your own gas budget pays), a coin's reserve until completed, then its actual cost. Decimal string in the asset's units. |
| network_fee_final | boolean | network_fee is final. |
| total_charged | string or null | amount + network_fee. Decimal string in the asset's units. |
| destination | string | Where it goes. |
| destination_memo | string or null | Memo or tag of the destination. |
| destination_basis | string | Why the destination was allowed (Payouts → Settings). |
| matched_invoice_id | string or null | The paid invoice that made it eligible. |
| matched_transaction_id | string or null | Its Transaction ID. |
| customer_id | string or null | Your customer's id, as sent. |
| customer_email | string or null | Your customer's email (lower case). |
| order_id | string or null | Your Merchant order ID for this payout. |
| note | string or null | Your note. |
| created_via | string | Where it was created. |
| settlement_id | string or null | The settlement that sends it (kind payout); null while it waits for approval. |
| tx_hash | string or null | The transfer's transaction hash, once sent. |
| explorer_url | string or null | Block explorer link of tx_hash. |
| funds_returned | boolean | A failed payout whose money went back to the payout wallet. |
| created_at | string or null | When it was created. |
| approved_at | string or null | When it was approved. |
| funded_at | string or null | When its money was set aside. |
| completed_at | string or null | When its transfer confirmed. |
| failed_at | string or null | When it failed. |
| cancelled_at | string or null | When it was cancelled. |
The payout wallet object #
GET /payout-wallets lists one per chain family with an enabled network; POST /payout-wallets with network_family creates one (202, allocating; 200 when it exists).
| Field | Type | Meaning |
|---|---|---|
| object | string | Always payout_wallet. |
| network_family | string | The chain family (one address for every EVM network). |
| status | string | Whether it exists. |
| address | string or null | Its address (send money here to top it up); null until created. |
| networks | array of strings | The networks it receives on. |
| environment | string | The environment of the object (the API key's). |
| balances | array of PayoutWalletBalance | Its money per asset. |
Settlements and withdrawals #
A settlement sends you your share of a payment. When depends on your settlement schedule (the platform's default, or your choice under Balances when the operator lets merchants choose):
| Schedule | What happens to your share |
|---|---|
| on_request | The default. It goes into your balance (credited, "In balance"); you withdraw when you want, and may keep part for refunds and payouts. |
| per_invoice | Sent to your settlement wallet for each invoice. |
| daily | Collected and sent once a day, at the hour the operator sets. |
The platform commission and the fixed fee per invoice are taken from every invoice whatever the schedule. On EVM and Tron, on request roughly halves the network cost per invoice (one transfer into your balance instead of two; commissions are collected in batches); a withdrawal is one more transfer. On Bitcoin and Bitcoin Cash it costs about the same as per invoice.
GET /settlements lists settlements with their transfers (refunds and payouts are listed under their own endpoints). Filters:
| Filter | Type | Allowed values | Notes |
|---|---|---|---|
| ?status= | string | planned, funds_reserved, awaiting_resources, submitting, settling, pooled, completed, partially_completed, reconciliation_required, held, credited, requested, cancelled | Only this status. |
| ?kind= | string | invoice, daily, split_session, splitter, balance_release, withdrawal | Only this kind. |
| ?asset= | string | up to 40 characters | Asset identifier. |
| ?owner_id= | string | up to 26 characters | The owner's id. |
| ?invoice_id= | string | up to 26 characters | One invoice's settlements and the daily settlement that paid it. |
| ?transaction_id= | string | up to 40 characters | The invoice or split session with this Transaction ID. |
| ?order_id= | string | up to 120 characters | Invoices and split sessions with this Merchant order ID. |
| ?tx_hash= | string | up to 128 characters | A customer payment's or a transfer's hash. |
The settlement object #
| Field | Type | Meaning |
|---|---|---|
| id | string | Settlement id. |
| object | string | Always settlement. |
| kind | string | What is settled: see settlement kinds. |
| status | string | See settlement statuses. |
| hold_reason | string or null | Why it waits or was cancelled: a code (e.g. below_minimum_payout, fee_estimate_unavailable, insufficient_native_gas_budget, settlement_destination_missing, cancelled_by_merchant, unwound), or replanned: <id of the new settlement> / replanned: nothing left / replanned: settled outside; liability_short: the platform operator reviews money that is no longer in your pending balance. Never the provider's own error text; an unknown code means the platform operator reviews it. |
| environment | string | The environment of the object (the API key's). |
| asset | string | The asset. |
| owner | object | What the settlement belongs to. |
| invoice_id | string or null | The invoice (Payment ID) this belongs to; null for splitters, withdrawals and daily settlements. |
| invoice_number | string or null | Its invoice number. |
| transaction_id | string or null | The Transaction ID of that invoice or split session (TRX-…, sandbox TST-…). |
| order_id | string or null | The Merchant order ID of that invoice or split session. |
| gross | string | What is settled. Decimal string in the asset's units. |
| platform_fee | string | The platform commission, the fixed fee included. Decimal string in the asset's units. |
| fixed_fee | string | The fixed fee per invoice in platform_fee. Decimal string in the asset's units. |
| network_fee | string | Token gas of every transfer, charged in the token before the split (0 for coins and when your own gas budget pays). Decimal string in the asset's units. |
| network_fee_detail | object or null | How network_fee was estimated; null when none was charged. |
| execution_cost_reserve | string | Coins: the network cost reserved from the payment. Decimal string in the asset's units. |
| actual_network_cost | string or null | Coins: what the network charged. Decimal string in the asset's units. |
| residual_retained | string or null | The unused reserve kept (null until known). Decimal string in the asset's units. |
| distributable | string | What reaches you or the recipients: gross − platform_fee − network_fee − execution_cost_reserve. Decimal string in the asset's units. |
| cost_funding | string or null | separate_gas: tokens, gas from a gas budget; same_asset_reserve: coins, reserved from the payment. |
| fee_snapshot_id | string or null | The fee snapshot it was planned with. |
| gas_paid | object or null | Tokens: the gas its transfers actually spent (null until known). |
| settlement_schedule | string or null | Invoice settlements: the schedule it was planned under; null for the other kinds. |
| settlement_batch_id | string or null | Daily schedule: the batch that settles it. |
| withdrawal | object or null | Withdrawals only: what was asked for; null for every other kind. |
| transfers | array of SettlementTransfer | Its on-chain transfers, in order. |
| created_at | string | When it was created. |
| completed_at | string or null | When it completed (or was credited). |
The transfer object #
| Field | Type | Meaning |
|---|---|---|
| id | string | Transfer id. |
| object | string | Always settlement_transfer. |
| settlement_id | string | Its settlement. |
| kind | string | Where it goes: see transfer kinds. |
| status | string | See transfer statuses. |
| hold_reason | string or null | Why it waits: a code (e.g. deposit_reorged, below_minimum_payout_accumulating). |
| asset | string | The asset sent. |
| amount | string | What is sent. Decimal string in the asset's units. |
| destination | string or null | The receiving address. |
| memo | string or null | Memo or tag. |
| label | string or null | The destination for people. |
| network_fee | string or null | What the network charged, in network_fee_asset (decimal string); null until known, and on the 2nd+ output of a shared Bitcoin transaction. |
| network_fee_asset | string or null | The coin that pays the network fee (e.g. TRX_TRON for USDT_TRON). |
| tx_hash | string or null | Transaction hash once sent. |
| explorer_url | string or null | Block explorer link of tx_hash: proof the money left. |
| attempts | integer | Send attempts. |
| error | string or null | Why it waits or failed, in one short sentence in the platform's own words (e.g. waiting for the platform's network fee wallet; retried automatically). Never the provider's error text. Null when there is nothing to say. |
| submitted_at | string or null | When it was sent. |
| confirmed_at | string or null | When it confirmed. |
Withdrawals. POST /withdrawals takes an amount of one asset from your balance (default: everything available) for your saved settlement wallet; it answers 202 with a settlement of kind withdrawal, status requested, planned within about a minute (settlement.planned) and sent as one transfer. Its network cost comes out of the amount. Cancel it with POST /withdrawals/{id}/cancel while it is requested.
| Field | Allowed values | Default | Notes |
|---|---|---|---|
asset
string · Required |
up to 40 characters | — | Asset identifier. |
amount
string · Optional |
up to 64 characters | — | At most the asset's decimals and what is available. Default: everything available. |
destination_address
string · Optional |
up to 128 characters | — | Only while the settlement lock is off. Default: your saved settlement wallet. Never an address of this platform (a payment, collection or payout wallet address it holds the keys for: 422 validation_failed). |
destination_memo
string · Optional |
up to 64 characters | — | Memo or tag. |
note
string · Optional |
up to 255 characters | — | Your note (withdrawal.note). |
# a new UUID for every withdrawal; the same one only to retry it
curl -X POST https://cryptonera.com/api/v1/withdrawals \
-H "Authorization: Bearer $CRYPTONERA_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 5c8a1f3d-7e2b-4a96-8d10-2f6b9e4c7a31" \
-d '{
"asset": "USDT_TRON",
"amount": "90.00"
}'
Response 202 Accepted:
{
"id": "01J9Z6D4E5F6G7H8J9KMNPQRST",
"object": "settlement",
"kind": "withdrawal",
"status": "requested",
"hold_reason": null,
"environment": "sandbox",
"asset": "USDT_TRON",
"owner": {
"type": "withdrawal",
"id": "01J9Z6D4E5F6G7H8J9KMNPQRST"
},
"invoice_id": null,
"invoice_number": null,
"transaction_id": null,
"order_id": null,
"gross": "90.000000",
"platform_fee": "0.000000",
"fixed_fee": "0.000000",
"network_fee": "0.000000",
"network_fee_detail": null,
"execution_cost_reserve": "0.000000",
"actual_network_cost": null,
"residual_retained": "0.000000",
"distributable": "0.000000",
"cost_funding": "separate_gas",
"fee_snapshot_id": "01J9Z6D4E5F6G7H8J9KMNPQRSV",
"gas_paid": null,
"settlement_schedule": null,
"settlement_batch_id": null,
"withdrawal": {
"requested_amount": "90.000000",
"destination": {
"address": "<your-tron-settlement-wallet>",
"memo": null,
"label": "Tron wallet",
"source": "settlement_wallet"
},
"estimate": {
"network_fee": "1.100000",
"execution_cost_reserve": null,
"net": "88.900000",
"basis": "kept"
},
"channel": "api",
"note": null,
"cancellable": true,
"cancel_reason": null
},
"transfers": [],
"created_at": "2026-10-14T11:00:00+00:00",
"completed_at": null,
"request_id": "req_01J9Z6D4E5F6G7H8J9KMNPQRSW"
}
Balances #
GET /balances gives your money per asset, with a USD estimate at the rate the platform keeps (an estimate only: never used for any payment, and assets are never converted into each other). pending_detail says why pending waits, in parts that add up to it exactly (the codes).
{
"object": "list",
"environment": "sandbox",
"data": [
{
"object": "balance",
"asset": "USDT_TRON",
"symbol": "USDT",
"decimals": 6,
"available": "128.400000",
"pending": "24.250000",
"pending_detail": [
{
"code": "on_its_way",
"label": "On its way into your balance (network confirmations)",
"amount": "18.500000",
"amount_atomic": "18500000",
"count": 1
},
{
"code": "open_invoice",
"label": "Waiting for the rest of the payment",
"amount": "5.750000",
"amount_atomic": "5750000",
"count": 1
}
],
"held": "0.000000",
"reserved": "0.000000",
"payout_wallet": null,
"retained": "0.000000",
"deficit": "0.000000",
"total": "152.650000",
"minimum_withdrawal": "1.000000",
"usd": {
"rate": "1.00",
"rate_source": "coingecko",
"rate_at": "2026-10-14T11:50:00+00:00",
"stale": false,
"available": "128.40",
"total": "152.65"
}
}
],
"usd_totals": {
"available": "128.40",
"total": "152.65",
"oldest_rate_at": "2026-10-14T11:50:00+00:00",
"complete": true,
"missing": [],
"note": "Estimate at the listed rates; never used for any payment. Assets are never converted."
},
"request_id": "req_01J9Z7E5F6G7H8J9KMNPQRSTVW"
}
The balance object #
| Field | Type | Meaning |
|---|---|---|
| object | string | Always balance. |
| asset | string | Asset identifier. |
| symbol | string | Its symbol. |
| decimals | integer | Its decimals. |
| available | string | In your balance and not set aside: what POST /withdrawals can take. Decimal string in the asset's units. |
| pending | string | Confirmed payments on their way into the balance (or to your wallet on the other schedules); pending_detail says why each part waits. Decimal string in the asset's units. |
| pending_detail | array of PendingPart | What pending is made of and why it waits, one part per reason in a fixed order; the amounts add up to pending exactly. Empty when nothing is pending. From stored records only (no network call). |
| held | string | Waiting for your decision (late, underpaid, extra payments). Decimal string in the asset's units. |
| reserved | string | Set aside for withdrawals and refunds on their way. Decimal string in the asset's units. |
| payout_wallet | string or null | What the payout wallet can pay out now; null without payout-wallet money in this asset. Decimal string in the asset's units. |
| retained | string | Unused network-fee reserves. Decimal string in the asset's units. |
| deficit | string | What you owe after a payment was reversed; taken from the next payments. Decimal string in the asset's units. |
| total | string | available + pending + held + reserved + payout_wallet + retained − deficit (signed decimal string). |
| minimum_withdrawal | string | The least a withdrawal must deliver after its network cost. Decimal string in the asset's units. |
| usd | object or null | USD estimate; null without a usable rate. |
Deposits #
GET /deposits lists the payments seen on the network (both products). Filters:
| Filter | Type | Allowed values | Notes |
|---|---|---|---|
| ?status= | string | Only this status. | |
| ?asset= | string | Asset identifier. | |
| ?owner_id= | string | The owner's id. | |
| ?tx_hash= | string | Transaction hash. | |
| ?invoice_id= | string | One invoice's payments. | |
| ?order_id= | string | Invoices with this Merchant order ID. | |
| ?transaction_id= | string | The invoice with this Transaction ID (a split session's payments: ?owner_id= the session id). |
The deposit object #
| Field | Type | Meaning |
|---|---|---|
| id | string | Deposit id. |
| object | string | Always deposit. |
| status | string | See deposit statuses. |
| hold_reason | string or null | Why it is held. |
| classification | string or null | What kind of money it is. |
| environment | string | The environment of the object (the API key's). |
| asset | string or null | The asset. |
| amount | string or null | The amount. Decimal string in the asset's units. |
| tx_hash | string | The payment's transaction hash. |
| explorer_url | string or null | Block explorer link of tx_hash: proof the customer paid. |
| block_number | integer or null | Its block (null while unconfirmed). |
| confirmations | integer or null | Confirmations so far. |
| address | string or null | The receiving address. |
| payer_address | string or null | Who sent it (canonical form), where a refund goes back to; null when the source names no sender (Bitcoin Cash, block scans). |
| owner | object | What the address belongs to. |
| settlement_id | string or null | The settlement that took it. |
| invoice_id | string or null | The invoice (Payment ID) this belongs to; null for splitters, withdrawals and daily settlements. |
| invoice_number | string or null | Its invoice number. |
| transaction_id | string or null | The Transaction ID of that invoice or split session (TRX-…, sandbox TST-…). |
| order_id | string or null | The Merchant order ID of that invoice or split session. |
| observed_at | string or null | First seen. |
| confirmed_at | string or null | Confirmed. |
| accepted_at | string or null | Accepted into a settlement. |
Assets, the fee policy and wallets: see the endpoint table above. Webhook endpoints are explained under webhooks.
5. Statuses #
Every status the Crypto Billing objects can have. The split session's are under Crypto Splitter.
Invoice status (Invoice.status) #
Ship the order on paid or overpaid only. underpaid and paid_late need your decision: accept or refund.
| Value | Meaning |
|---|---|
| awaiting_payment | Open: waiting for the payer; payments seen before expires_at count. |
| confirming | Payments that cover the invoice wait for the network's confirmations; it does not expire meanwhile. |
| partially_paid | Open: part of the amount is confirmed; the payer may send the rest before expires_at. |
| paid | Paid in full with confirmed money, or a partial or late payment you accepted (decision.status: accepted). Ship the order. |
| overpaid | Paid, with more than asked: ship the order; the excess is held for you to refund or accept. |
| expired | Closed at expires_at without any confirmed payment. |
| underpaid | Closed (expired or cancelled) with a confirmed partial payment: held until you accept it (the invoice becomes paid) or refund it. |
| paid_late | It had expired or been cancelled when confirmed money covering it arrived: held until you accept (paid) or refund it. |
| cancelled | Cancelled by you before anything was paid. |
| refund_pending | Its refund is being sent. |
| partially_refunded | Part of the payment was refunded. |
| refunded | The payment was refunded in full. |
Invoice settlement status (Invoice.settlement_status) #
| Value | Meaning |
|---|---|
| not_started | No accepted payment yet. |
| pending | Paid; the settlement is being prepared. |
| processing | Its transfers are being sent. |
| scheduled | Daily schedule: collected, sent with the next daily settlement. |
| settled | Your share reached your settlement wallet or splitter. |
| held | Waiting: see the settlement's hold_reason. |
| credited | On-request schedule: your share is in your balance ("In balance"), to withdraw when you want. |
Invoice decision status (InvoiceDecision.status) #
| Value | Meaning |
|---|---|
| needed | Held money waits for you: accept or refund it. |
| accepting | An accept waits to be planned. |
| accepted | You accepted it (the latest accept planned). |
| refunded | It was refunded. |
| resolved | Nothing is held any more (for example a held payment was reorganised away). |
Payment option status (PaymentOption.status) #
| Value | Meaning |
|---|---|
| pending_address | No deposit address yet: it is allocated when the payer chooses this option. |
| available | The address is ready; the payer may pay with this option. |
| locked | The option the payer chose (or paid with). |
| paid | Paid in full with this option. |
| superseded | Another option was paid. |
Refund status (Refund.status) #
| Value | Meaning |
|---|---|
| pending_approval | Created from the API while "Refunds from the API need approval" is on: approve or cancel it in the dashboard. |
| queued | Waits for money (status_reason); sent automatically, oldest first, when the money suffices. You may cancel it. |
| processing | Funded; its transfer is on its way. |
| completed | Sent: tx_hash and explorer_url prove it. |
| failed | The transfer failed (status_reason); the operator retries it or returns the money (funds_returned). |
| cancelled | Cancelled before anything was sent. |
Payout status (Payout.status) #
| Value | Meaning |
|---|---|
| pending_approval | Created from the API while "API payouts need approval" is on; its money is set aside in the payout wallet. |
| processing | Funded; its transfer is on its way. |
| completed | Sent: tx_hash and explorer_url prove it. |
| failed | The transfer failed (status_reason); the operator retries it or returns the money to the payout wallet (funds_returned). |
| cancelled | Cancelled before anything was sent. |
Settlement status (Settlement.status) #
| Value | Meaning |
|---|---|
| planned | Recorded with its fees; not funded yet. |
| funds_reserved | Funded; its transfers will be sent. |
| awaiting_resources | Waits for gas or another resource (hold_reason); continues by itself. |
| submitting | Its transfers are being sent. |
| settling | Transfers broadcast, waiting for confirmations. |
| pooled | Daily schedule: on your collection address, sent with the next daily settlement. |
| completed | Every transfer confirmed. |
| partially_completed | Some transfers confirmed, others failed: the operator reviews it. |
| reconciliation_required | Needs operator review (a failed transfer or a reversed payment). |
| held | Waits (hold_reason), for example below the minimum payout. |
| credited | On-request schedule: your share is in your balance. |
| requested | A withdrawal you asked for: the amount is set aside and planned within about a minute. |
| cancelled | A withdrawal (or another transfer of yours) cancelled or refused before anything was sent (hold_reason), or a held invoice settlement replaced by a new plan of the same payment (hold_reason replanned: <id of the new settlement>, or replanned: nothing left), or recorded by the platform operator as paid to you outside the platform (replanned: settled outside). |
Settlement kind (Settlement.kind) #
| Value | Meaning |
|---|---|
| invoice | An invoice payment. |
| daily | A daily settlement: one transfer for the invoices collected that day. |
| split_session | A split session payment. |
| splitter | A deposit to a splitter. |
| balance_release | A balance release (before 2026-10-12; no longer created). |
| refund | A refund's transfer (listed under refunds, not among settlements). |
| withdrawal | A withdrawal of your balance. |
| payout | A payout's transfer (listed under payouts, not among settlements). |
| payout_wallet_funding | A move from your balance into the payout wallet. |
Transfer status (SettlementTransfer.status) #
| Value | Meaning |
|---|---|
| planned | Recorded, not sent yet. |
| reserved | Funds set aside for it. |
| submitted | Handed to the wallet provider. |
| broadcast | On the network, not in a block yet. |
| confirming | In a block, waiting for confirmations. |
| confirmed | Confirmed: final. |
| rejected | Refused by the provider before it was sent. |
| failed | Failed on the chain. |
| replaced | Replaced by a new transfer (fee bump). |
| unknown_outcome | Sent; its outcome is being verified with the provider. Never sent twice. |
| held | Waiting: see hold_reason. |
Transfer kind (SettlementTransfer.kind) #
| Value | Meaning |
|---|---|
| recipient | To you (your settlement wallet) or a splitter recipient. |
| platform_fee | The platform commission (shown for transparency). |
| consolidation | To your collection address (daily schedule, or on request: into your balance). |
| refund | A refund to the paying customer. |
| balance_release | A balance release (before 2026-10-12). |
| withdrawal | A withdrawal of your balance to your settlement wallet. |
| customer_payout | A payout to your customer. |
| payout_funding | From your collection address into your payout wallet. |
Deposit status (Deposit.status) #
| Value | Meaning |
|---|---|
| observed | Seen on the network, not confirmed yet. |
| confirming | In a block, waiting for confirmations. |
| confirmed | Confirmed: counted. |
| dropped | Left the chain before it confirmed. |
| conflicted | Two sources disagree about it; checked again. |
| reorged | Undone by a chain reorganisation after it confirmed. |
| unsupported | An asset or contract the platform does not accept. |
| held | Kept aside (hold_reason): late, extra, below the minimum, or waiting for your decision. |
Why a balance is pending (PendingPart.code) #
The pending_detail of GET /balances: why each part of pending waits. The parts add up to pending exactly.
| Value | Meaning |
|---|---|
| waiting_for_gas | Planned; its token transfer waits for network fee (gas) funds: the platform's gas wallet (the operator is notified) or, when your own gas budget pays your transfers, that budget. It continues by itself once they are topped up. |
| paused | Planned; transfers are paused by the platform (all of them, a network, a provider or your account; live transfers not activated yet). It continues by itself when the pause is lifted. |
| splitter_paused | Planned; your own splitter is paused. Resume it (Splitters) and its transfers continue. |
| operator_review | Stopped: a transfer failed or needs a check, the money is held for a reason only the platform operator can clear (an archived splitter included), or the operator decides about money from before 2026-10-12. The operator reviews it. |
| on_its_way | Planned and sent, or about to be sent: waiting for network confirmations. Into your balance on the on-request schedule, to your wallet on the others. |
| replanning | Not planned yet (no network fee estimate at the time, unwound by the operator, below the minimum payout, or a settlement wallet saved since): the platform plans it again by itself. |
| too_small | Too small to move on its own (the network cost would take most or all of it, or, for an invoice paid out through a splitter, a recipient's share is below the minimum payout): it waits until the platform operator plans it again. |
| no_destination | A paid invoice with nowhere to settle to: save a settlement wallet for its network (an older invoice without an address of its own), or its splitter has no current version. It moves on by itself once there is one. |
| open_invoice | Confirmed part-payments of invoices that are still open: they wait for the rest of the payment. |
| split_waiting | Payments to a splitter or split session that wait for more: its threshold, the minimum payout or the next payment. |
| scheduled_daily | Daily schedule: collected, and paid to your wallet by the next daily payout. |
| other | Anything else not settled yet (for example money from an older release, or a settlement in a state not listed here). Ask support if it stays. |
6. Errors #
Every error has the same envelope:
{
"error": {
"code": "validation_failed",
"message": "The request is invalid.",
"retryable": false,
"request_id": "req_01J9Z3A1B2C3D4E5F6G7H8J9KS",
"fields": {
"amount": ["The amount field must be a string."]
}
}
}
retryable: true: retry later (honourRetry-After) with the same request.reason: a setting or a detail.hintexplains it;dashboard_urlanddocs_urlpoint to the fix.- The Key column says what happens to your
Idempotency-Key: stored (the same key replays this answer: fix the request and use a new key), free (nothing stored: send the same request with the same key once fixed), or — (answered before the key is read).
| HTTP | Code | Meaning | What to do | Key |
|---|---|---|---|---|
| 400 | idempotency_key_required |
This endpoint moves money and the request has no Idempotency-Key.
|
Send a new UUID as Idempotency-Key; send the same one only to retry that same request. |
— |
| 400 | idempotency_key_invalid |
Idempotency-Key is longer than 255 characters or has other characters than letters, digits and - _ : ..
|
Use a UUID. | — |
| 401 | unauthenticated |
No valid API key: missing, mistyped, revoked, expired, from an address outside its IP allowlist, or the account is not active (header WWW-Authenticate: Bearer).
|
Check the key and its environment (ck_sandbox_ or ck_live_) under Developers → API keys. |
— |
| 403 | forbidden |
The key lacks the scope this endpoint needs (required_scope).
Also: required_scope |
Create a key with that scope. | — |
| 403 | store_not_allowed |
The key is restricted to one store and the request names another store, a record of another store, or an account-wide money endpoint (payouts, payout wallets, withdrawals).
Also: store_id, hint |
Leave store_id out, or use a key without a store. |
stored |
| 403 | payouts_api_disabled |
"Payouts allowed from the API" is off for this account. | The owner turns it on (Payouts → Settings), or create the payout in the dashboard. | free |
| 404 | not_found |
No such object in this account and environment (a sandbox key never sees live objects). | Check the id (an invoice also takes its transaction_id). |
stored |
| 405 | http_405 |
The method is not allowed on this path (other transport errors answer http_<status> too).
|
Check the method in the endpoint table. | — |
| 409 | idempotency_key_reused |
The Idempotency-Key was used before with a different request (or by a key restricted to another store).
|
Send the new request with a new key. | — |
| 409 | request_in_progressretryable |
A request with this Idempotency-Key is still being processed (Retry-After: 2).
|
Retry after the delay with the same key. | — |
| 409 | order_id_in_use |
The order_id already has an open invoice in this store with other terms: another pricing mode, currency, amount or allowed assets (differences).
Also: order_id, existing_invoice, differences |
Use that invoice (existing_invoice.checkout_url), cancel it first, or send another order_id (duplicate protection). |
free |
| 409 | order_already_paid |
The order_id already has an invoice with money in this store.
Also: order_id, existing_invoice |
Do not charge the order twice; for a new payment use another order_id. |
free |
| 409 | insufficient_balance |
Not enough available money. A withdrawal: your balance must cover the amount itself (its network cost comes out of the amount); details asset and available. A payout: the payout wallet must cover the amount plus its network fee; details asset, available, amount, network_fee and needed. Refunds are queued instead (202).
Also: asset, available, amount, network_fee, needed |
Lower the amount (a withdrawal can take at most available), wait for more payments to reach your balance, or top up the payout wallet; then send the same request again. |
free |
| 409 | refund_exists |
The invoice already has a refund (one per invoice).
Also: refund |
Read it (refund.id); cancel it while it is queued or waits for approval. |
free |
| 409 | refund_not_cancellable |
Only a refund that is queued or pending_approval can be cancelled.
Also: status |
Read the refund: it is being sent or is finished. | free |
| 409 | payout_not_cancellable |
Only a payout that is pending_approval can be cancelled.
Also: status |
Read the payout: it is being sent or is finished. | free |
| 409 | withdrawal_not_cancellable |
Only a withdrawal that is requested can be cancelled.
Also: status |
Read the withdrawal: it is planned, sent or finished. | free |
| 409 | nothing_to_accept |
Nothing is held for this invoice, or it is still open and not partly paid: awaiting_payment, or confirming (a payment that covers it is still confirming; it becomes paid once that confirms).
Also: status, needs_decision |
Read the invoice (status, decision). |
free |
| 409 | payment_confirming |
The invoice is partly paid (partially_paid: an early accept) and another payment to it is still confirming.
Also: status |
Accept once the payment confirmed or the invoice closed. | free |
| 409 | accept_in_progress |
An accept of this invoice waits to be planned (within about a minute).
Also: acceptance |
Wait for invoice.accepted; to refund instead, cancel the accept in the dashboard first. |
free |
| 409 | refund_in_progress |
A refund of this invoice is waiting for approval, queued or being sent.
Also: refund |
Cancel the refund first, or let it finish. | free |
| 409 | held_amount_changed |
expected_amount is not what is held now.
Also: asset, amount, amount_atomic |
Read the invoice again (decision.held) and accept what is held now. |
free |
| 409 | operator_review_required |
Part of the money held in that asset cannot be accepted (another token sent to the address, a transfer mined again after a reorganization, money at another payment option); a transfer mined again after a reorganization cannot be refunded either.
Also: asset, reason |
Another token: refund it. A transfer mined again: ask the platform operator. | free |
| 422 | validation_failed |
The request is invalid: fields names each problem. With a reason (rows below) a setting is missing instead, and the key stays free.
Also: fields, reason, hint, dashboard_url, docs_url, environment |
Without reason: fix the request and send it with a new key. With reason: fix the setting (hint, dashboard_url), then send the same request again. |
stored |
| 422 | validation_failedreason: allowed_assets_missing |
The store has no payment assets that can be paid in the key's environment (none ticked, or none available there). | dashboard_url opens the store: tick its Payment assets. |
free |
| 422 | validation_failedreason: asset_not_in_store |
Only for a price in crypto: currency is not one of the store's Payment assets (store_assets lists them).
|
Send one of store_assets as currency, or open dashboard_url (the store) and tick the coin. |
free |
| 422 | validation_failedreason: settlement_address_locked |
Only when the request names settlement_address, settlement_addresses or settlement_target_id (a withdrawal: destination_address) while the settlement lock is on (example). It comes before the wallets are checked: its missing already lists the coins without a saved settlement wallet.
|
Remove those fields: the saved wallets are used (save the ones in missing first). |
free |
| 422 | validation_failedreason: settlement_wallet_invalid |
A saved settlement wallet is not a valid settlement address for a coin offered any more (or is an address of this platform, saved before those were refused): asset names the coin, environment where.
|
Save a valid address under Payments → Settlement wallets in that environment. | free |
| 422 | validation_failedreason: settlement_destination_missing |
A coin offered has no settlement wallet: missing names the kind of network and the coins, environment where (example).
|
Save an address under Payments → Settlement wallets in that environment, or untick the coin. | free |
| 422 | validation_failedreason: store_settlement_invalid |
Only for a store that settles through its default splitter, in place of the wallet reasons above: the store has no default splitter, it is not active (e.g. paused), it belongs to the other environment, or it is paid in another coin than the invoice asks for. | dashboard_url opens the store: choose an active splitter of the key's environment (or Settlement wallets) as its default settlement, or resume the splitter. |
free |
| 422 | validation_failedreason: price_source_missing |
A price in fiat, and the platform has no price source for the key's environment. | Price the invoice in crypto, or ask the platform operator. | free |
| 422 | invalid_argument |
A value the platform cannot use, for example an unknown asset identifier or no platform commission configured. | Fix the value; the message says which. | stored |
| 422 | asset_not_supported |
The asset cannot be used here (reason).
Also: asset, reason, hint |
Use an asset of GET /assets; pay customers in tokens on EVM and Tron. |
free |
| 422 | amount_below_minimum |
What would arrive after the network fee is below the asset's minimum transfer (on Bitcoin and Bitcoin Cash also the destination's dust limit).
Also: asset, minimum, estimated_network_cost, available, amount |
Send more (see minimum and estimated_network_cost). |
free |
| 422 | refund_not_allowed |
This invoice cannot be refunded now (reason).
Also: reason, invoice_status, asset, assets |
See reason; a confirming payment can be refunded once it is confirmed. |
free |
| 422 | refund_amount_too_large |
More than may be refunded: more than the customer paid (a fiat invoice: the fiat value paid), or more than the invoice's held payment covers with its network fee (reason: held_funds_changed).
Also: max_amount, max_crypto, asset, funding_source, reason |
Refund at most max_amount (invoice currency) or max_crypto. |
free |
| 422 | refund_destination_not_payer |
Refunds go to an address that paid this invoice (reason).
Also: reason, payer_addresses |
Leave destination out (the payer is used), or enter the address in the dashboard; see payer_addresses. |
free |
| 422 | payout_destination_not_allowed |
Your payout settings do not allow this destination (reason; allowed lists what is on).
Also: reason, allowed |
Change the destination, or the policy under Payouts → Settings. | free |
| 422 | payout_wallet_missing |
No payout wallet for this network family yet.
Also: network_family, hint |
POST /payout-wallets with network_family, then fund it. |
free |
| 422 | asset_not_acceptable |
The money is held in an asset that is none of the invoice's payment options.
Also: asset |
Refund it. | free |
| 422 | amount_too_small_to_settle |
The platform fee is not less than the held amount.
Also: asset, amount, platform_fee |
Refund it instead. | free |
| 429 | rate_limitedretryable |
Too many requests: per key, per account and environment, or invalid keys per IP address (Retry-After).
|
Wait Retry-After seconds. |
— |
| 429 | invoice_rate_limitedretryable |
Too many new invoices per minute in this account and environment (API and dashboard together; Retry-After).
Also: environment, limit |
Wait Retry-After seconds; the platform operator can raise the limit. |
free |
| 429 | open_invoice_limitretryable |
Too many open invoices in this account and environment (open of limit).
Also: environment, limit, open |
Cancel unused invoices or let them expire. | free |
| 500 | internal_errorretryable |
An unexpected error. | Retry with the same request and key; quote the request_id to support if it persists. |
free |
| 502 | price_source_error |
The price source cannot price an asset of this fiat invoice.
Also: fields |
Price the invoice in crypto, or in another currency. | free |
| 502 | provider_error |
The wallet or chain provider refused the request. | Contact the platform operator with the request_id. |
free |
| 503 | price_source_unavailableretryable |
The price source is down or its rate is stale (Retry-After).
Also: fields, hint |
Retry later with the same request. | free |
| 503 | provider_unavailableretryable |
The wallet or chain provider is unreachable (Retry-After when known).
|
Retry later with the same request. | free |
| 503 | network_fee_unavailableretryable |
No network fee estimate right now (Retry-After: 60).
|
Retry in a minute with the same request. | free |
| 503 | schema_update_pendingretryable |
This feature needs a database update the platform operator has not applied yet (Retry-After).
|
Retry later, or ask the operator to apply the update (Admin → System). | free |
| 503 | not_installedretryable |
The platform is not installed yet. | Ask the platform operator. | — |
Ignore keys you do not know; new detail keys may be added.
7. Webhooks #
Endpoints and subscriptions #
Register an https endpoint with POST /webhook-endpoints (or Developers → Webhooks in the dashboard). event_types takes *, a prefix such as invoice.* (prefixes: invoice, deposit, settlement, fee, refund, split_session, splitter, payout ) or exact event names. The signing secret is shown once; PATCH with rotate_secret creates a new one (the old one keeps signing for a while). Sandbox and live endpoints are separate.
Payload #
Every delivery is a POST with this body (schema_version 2026-10-12):
{
"id": "01J9Z8F6G7H8J9KMNPQRSTVWXY",
"schema_version": "2026-10-12",
"type": "invoice.paid",
"occurred_at": "2026-10-12T10:07:12+00:00",
"delivery_attempt_id": "01J9Z8F6G7H8J9KMNPQRSTVWXZ",
"environment": "sandbox",
"resource": {
"type": "invoice",
"id": "01J9Z3A1B2C3D4E5F6G7H8J9KM"
},
"data": {
"number": "INV-MAIN-000123",
"deposit_id": "01J9Z8F6G7H8J9KMNPQRSTVWY0",
"excess": "0.000000",
"excess_atomic": "0",
"accepted": false,
"accepted_kind": null,
"acceptance_id": null,
"status": "paid",
"asset": "USDT_TRON",
"amount_required": "49.990000",
"amount_required_atomic": "49990000",
"amount_paid": "49.990000",
"amount_paid_atomic": "49990000",
"amount_pending": "0.000000",
"amount_pending_atomic": "0",
"amount_due": "0.000000",
"amount_due_atomic": "0",
"amount_held": "0.000000",
"amount_held_atomic": "0",
"needs_decision": false,
"invoice_id": "01J9Z3A1B2C3D4E5F6G7H8J9KM",
"invoice_number": "INV-MAIN-000123",
"transaction_id": "TST-7K3M-9QX2-H4PD",
"order_id": "order-1042"
}
}
data carries the references invoice_id, invoice_number, transaction_id and order_id whenever the event concerns a payment (unknown ones are left out). Every invoice.* event except invoice.settlement_* carries the invoice's amount block: status, asset, amount_required, amount_paid, amount_pending, amount_due, amount_held (decimal strings, each with an *_atomic sibling in the smallest unit) and needs_decision. Amounts are decimal strings everywhere. Events are small: read the object again before acting. For invoice, refund, payout, settlement, split_session and splitter events that is GET /invoices|refunds|payouts|settlements|split-sessions|splitters/{resource.id}; a settlement_transfer (settlement.transfer_*, fee.collected) has no endpoint of its own: read GET /settlements/{data.settlement_id} and its transfers[]; a deposit neither: GET /deposits?tx_hash={data.tx_hash} (deposit.held has no tx_hash: list the payment's deposits with ?invoice_id=, or ?owner_id= the split session) and pick the one whose id is resource.id. GET /webhook-events is also a polling feed.
Events #
Only confirmed money is reported as paid: invoice.partially_paid, invoice.underpaid, invoice.late_payment and invoice.paid_late are sent at confirmation, never at first sight.
| Event | Sent when | resource.type | data |
|---|---|---|---|
| Invoices | |||
| invoice.created | An invoice was created (also from the dashboard). | invoice | number, amount, currency, expires_at + references |
| invoice.confirming | Payments that cover the invoice were seen and wait for confirmations. | invoice | deposit_id + references |
| invoice.payment_dropped | The payment it was confirming left the chain and nothing else covers it: open again until it expires. | invoice | deposit_id, tx_hash, reason + references |
| invoice.partially_paid | A confirmed payment covers part of the amount (sent at confirmation only). | invoice | deposit_id + references |
| invoice.paid | Paid in full with confirmed money, or your accept made it paid: ship the order. | invoice | number, deposit_id, excess, excess_atomic, accepted, accepted_kind, acceptance_id + references |
| invoice.overpaid | Paid with more than asked: ship the order; the excess is held for your decision. | invoice | number, deposit_id, excess, excess_atomic, accepted, accepted_kind, acceptance_id + references |
| invoice.underpaid | It closed with a confirmed partial payment (or its amounts changed): held for your decision. | invoice | trigger, expires_at, cancelled_at, deposit_id + references |
| invoice.late_payment | A payment that arrived after it closed confirmed and is held. | invoice | deposit_id, tx_hash, deposit_asset, deposit_amount, deposit_amount_atomic, held + references |
| invoice.paid_late | Confirmed money after it closed now covers it (or, already paid late, an on-time payment was held too): held for your decision (accept or refund). | invoice | deposit_id, trigger + references |
| invoice.payment_held | A payment was held for another reason (reason: after it was paid, on a second option, below the minimum, another asset). |
invoice | deposit_id, tx_hash, deposit_asset, deposit_amount, deposit_amount_atomic, reason + references |
| invoice.accepted | Your accept of held money was planned (then invoice.paid for an underpaid or paid-late invoice). |
invoice | acceptance_id, kind, accepted_asset, accepted_amount, accepted_amount_atomic, status_before, settlement_id, platform_fee, platform_fee_atomic, network_fee, network_fee_atomic, via + references |
| invoice.accept_failed | Your accept could not be planned; nothing moved and the decision is needed again. | invoice | acceptance_id, accepted_asset, accepted_amount, accepted_amount_atomic, reason, message + references |
| invoice.expired | It closed at expires_at without any confirmed payment. |
invoice | number, trigger + references |
| invoice.cancelled | You cancelled it. | invoice | reason + references |
| invoice.settlement_processing | Its settlement is sending your share. | invoice | settlement_id, status + references |
| invoice.settlement_scheduled | Daily schedule: collected, sent with the next daily settlement. | invoice | settlement_id, status + references |
| invoice.settlement_credited | On-request schedule: your share is in your balance. | invoice | settlement_id, status + references |
| invoice.settlement_settled | Your share reached your wallet or splitter. | invoice | settlement_id, status + references |
| invoice.settlement_held | Its settlement waits (see the settlement's hold_reason). |
invoice | settlement_id, status + references |
| Refunds | |||
| refund.created | A refund was created. | refund | refund_id, status, status_reason, asset, amount, fiat_currency, fiat_amount, funding_source, destination, destination_type, created_via + references |
| refund.queued | It waits for money (status_reason); sent automatically when the money suffices. |
refund | refund_id, status, status_reason, asset, amount, fiat_currency, fiat_amount, funding_source, destination, destination_type, created_via, needed, available + references |
| refund.processing | Funded; its transfer is on its way. | refund | refund_id, status, status_reason, asset, amount, fiat_currency, fiat_amount, funding_source, destination, destination_type, created_via, settlement_id, network_fee + references |
| refund.completed | Sent and confirmed. | refund | refund_id, status, status_reason, asset, amount, fiat_currency, fiat_amount, funding_source, destination, destination_type, created_via, tx_hash, explorer_url, network_fee + references |
| refund.failed | Its transfer failed. | refund | refund_id, status, status_reason, asset, amount, fiat_currency, fiat_amount, funding_source, destination, destination_type, created_via, tx_hash, funds_returned + references |
| refund.cancelled | Cancelled before anything was sent. | refund | refund_id, status, status_reason, asset, amount, fiat_currency, fiat_amount, funding_source, destination, destination_type, created_via + references |
| Payouts | |||
| payout.created | A payout was created (funded, or waiting for approval). | payout | payout_id, status, status_reason, asset, amount, destination, destination_basis, customer_id, customer_email, order_id, matched_invoice_id, created_via, network_fee |
| payout.completed | Sent and confirmed. | payout | payout_id, status, status_reason, asset, amount, destination, destination_basis, customer_id, customer_email, order_id, matched_invoice_id, created_via, tx_hash, explorer_url, network_fee |
| payout.failed | Its transfer failed. | payout | payout_id, status, status_reason, asset, amount, destination, destination_basis, customer_id, customer_email, order_id, matched_invoice_id, created_via, tx_hash, funds_returned |
| payout.cancelled | Cancelled before anything was sent. | payout | payout_id, status, status_reason, asset, amount, destination, destination_basis, customer_id, customer_email, order_id, matched_invoice_id, created_via |
| Settlements | |||
| settlement.requested | A withdrawal was recorded (its amount set aside). | settlement | kind, status, asset, amount, destination, estimated_network_cost + references |
| settlement.planned | A settlement was planned with its fees. | settlement | kind, gross, platform_fee, network_fee, execution_cost_reserve, distributable, asset + references |
| settlement.held | A settlement waits (reason, e.g. below the minimum payout). |
settlement | kind, reason, asset + references |
| settlement.awaiting_resources | A settlement waits for gas; it continues by itself. | settlement | kind, reason, needed, available, fee_asset, budget_owner + references |
| settlement.settling | Its transfers are broadcast. | settlement | kind, status, transfers_confirmed, transfers_total + references |
| settlement.pooled | Daily schedule: moved to your collection address. | settlement | kind, status, transfers_confirmed, transfers_total + references |
| settlement.completed | Every transfer confirmed. | settlement | kind, status, transfers_confirmed, transfers_total, settlement_batch_id + references |
| settlement.partially_completed | Some transfers confirmed, others failed. | settlement | kind, status, transfers_confirmed, transfers_total + references |
| settlement.reconciliation_required | It needs operator review (a failed transfer, or a payment reversed after it was credited: reason, reversed). |
settlement | kind, status, transfers_confirmed, transfers_total, reason, reversed + references |
| settlement.credited | On-request schedule: your share was credited to your balance. | settlement | kind, status, transfers_confirmed, transfers_total, credited + references |
| settlement.cancelled | A withdrawal was cancelled or could not be planned (reason; its amount is available again), or a held invoice settlement was replaced by a new plan of the same payment (reason replanned: <id> with replaced_by, or replanned: nothing left), or recorded by the platform operator as paid to you outside the platform (replanned: settled outside). |
settlement | kind, status, reason, replaced_by, asset, amount + references |
| settlement.transfer_confirmed | One on-chain transfer of a settlement confirmed (not sent for the platform fee or the collection address). | settlement_transfer | settlement_id, settlement_kind, kind, asset, amount, destination, memo, tx_hash, explorer_url, status + references |
| settlement.transfer_failed | One on-chain transfer of a settlement failed. | settlement_transfer | settlement_id, settlement_kind, kind, asset, amount, destination, memo, tx_hash, explorer_url, status, reason + references |
| fee.collected | The platform fee transfer of a settlement confirmed. | settlement_transfer | asset, amount, tx_hash, settlement_id + references |
| Activity | |||
| deposit.observed | A payment was seen on the network. | deposit | status, asset, amount, tx_hash, block_number, confirmations, classification, hold_reason, owner, address, payer_address + references |
| deposit.confirmed | A payment confirmed. | deposit | status, asset, amount, tx_hash, block_number, confirmations, classification, hold_reason, owner, address, payer_address + references |
| deposit.reorged | A confirmed payment was undone by a reorganisation. | deposit | status, asset, amount, tx_hash, block_number, confirmations, classification, hold_reason, owner, address, payer_address + references |
| deposit.held | A payment was kept aside (reason). |
deposit | reason, amount, asset + references |
| Splitters | |||
| splitter.created | A splitter was created. | splitter | status, asset |
| splitter.paused | A splitter was paused. | splitter | |
| splitter.resumed | A splitter was resumed. | splitter | |
| Split sessions | |||
| split_session.created | A split session was created. | split_session | status, asset, deposit_address + references |
| split_session.payment_detected | A payment was seen. | split_session | deposit_id, asset, amount + references |
| split_session.payment_dropped | The payment it detected left the chain before it confirmed. | split_session | deposit_id, tx_hash, reason, status + references |
| split_session.late_payment | A payment arrived after it closed; it is held. | split_session | deposit_id, asset, amount + references |
| split_session.closed | You closed it. | split_session | status + references |
| split_session.expired | It expired without a payment. | split_session | expired_at + references |
| split_session.processing | Its split is being sent. | split_session | status, asset, settled_gross + references |
| split_session.settled | Every recipient was paid. | split_session | status, asset, settled_gross + references |
| split_session.partially_settled | Some transfers confirmed, others not. | split_session | status, asset, settled_gross + references |
| split_session.held | It is held: late money, or its target cannot pay out as configured. | split_session | status, asset, settled_gross, reason, hold_reason, expected_amount, platform_fee, network_fee, minimum_payout + references |
Verify the signature #
| Header | Meaning |
|---|---|
| X-Webhook-Id | Event id (ULID): the same in every retry and replay. Dedupe on it. |
| X-Webhook-Timestamp | Unix seconds when this delivery was signed. Reject deliveries older than 300 seconds. |
| X-Webhook-Key-Id | Which endpoint secret signed it (k1, k2 after a rotation). |
| X-Webhook-Signature | v1=<hex HMAC-SHA256 of timestamp + "." + raw_body>. During a secret rotation two comma-separated signatures are sent (X-Webhook-Previous-Key-Id names the older key): accept the delivery when any of them matches. |
| X-Webhook-Event | The event type, e.g. invoice.paid. |
<?php
// Verify a Cryptonera webhook. Use the RAW body — never re-encode JSON before hashing.
$secret = getenv('CRYPTONERA_WEBHOOK_SECRET'); // whsec_...
$raw = file_get_contents('php://input');
$sigHdr = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$tsHdr = $_SERVER['HTTP_X_WEBHOOK_TIMESTAMP'] ?? '';
$eventId = $_SERVER['HTTP_X_WEBHOOK_ID'] ?? '';
if (!ctype_digit($tsHdr) || abs(time() - (int) $tsHdr) > 300) {
http_response_code(400); exit('stale timestamp');
}
$expected = 'v1=' . hash_hmac('sha256', $tsHdr . '.' . $raw, $secret);
$ok = false;
foreach (explode(',', $sigHdr) as $part) { // two v1= values while a secret is rotated: compare each
if (hash_equals($expected, trim($part))) { $ok = true; break; }
}
if (!$ok) { http_response_code(401); exit('bad signature'); }
// Dedupe on $eventId (retries and replays reuse it), then answer 2xx quickly.
$event = json_decode($raw, true, 64, JSON_THROW_ON_ERROR);
// $event['type'], $event['resource'], $event['data']
http_response_code(200);
import express from 'express';
import { createHmac, timingSafeEqual } from 'node:crypto';
const app = express();
// Verify the raw body: never a re-serialised JSON object.
app.post('/webhooks/cryptonera', express.raw({ type: 'application/json' }), (req, res) => {
const ts = req.get('X-Webhook-Timestamp') ?? '';
if (!/^\d+$/.test(ts) || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return res.sendStatus(400);
const expected = Buffer.from('v1=' + createHmac('sha256', process.env.CRYPTONERA_WEBHOOK_SECRET)
.update(ts + '.').update(req.body).digest('hex'));
const ok = (req.get('X-Webhook-Signature') ?? '').split(',').some((s) => {
const b = Buffer.from(s.trim());
return b.length === expected.length && timingSafeEqual(b, expected);
});
if (!ok) return res.sendStatus(401);
const event = JSON.parse(req.body.toString('utf8'));
// dedupe on req.get('X-Webhook-Id'), then handle event.type quickly
res.sendStatus(200);
});
import hashlib
import hmac
import os
import time
from flask import Flask, abort, request
app = Flask(__name__)
SECRET = os.environ['CRYPTONERA_WEBHOOK_SECRET'].encode()
@app.post('/webhooks/cryptonera')
def cryptonera_webhook():
raw = request.get_data() # the raw body, before any JSON parsing
ts = request.headers.get('X-Webhook-Timestamp', '')
if not ts.isdigit() or abs(time.time() - int(ts)) > 300:
abort(400)
expected = 'v1=' + hmac.new(SECRET, ts.encode() + b'.' + raw, hashlib.sha256).hexdigest()
signatures = request.headers.get('X-Webhook-Signature', '').split(',')
if not any(hmac.compare_digest(expected, s.strip()) for s in signatures):
abort(401)
event = request.get_json()
# dedupe on request.headers['X-Webhook-Id'], then handle event['type'] quickly
return '', 200
Hash the raw body, never a re-serialised object; reject timestamps older than 300 s; compare in constant time; dedupe on X-Webhook-Id, then answer quickly. Check your receiver with this test vector:
Test vector (check your receiver with it):
secret whsec_test
timestamp 1760000000
raw body {"id":"evt_1"}
signed 1760000000.{"id":"evt_1"}
header v1=66e880d7175fffb43ce10c4e14db1cfb230c8804b5aafb116affbc9a836c7690
Delivery and retries #
Answer any 2xx within 8 seconds. A failed delivery is retried with growing waits (30 s · 2n, at most 6 hours) up to 12 attempts, about 17 hours after the first; then it is dead. An endpoint that keeps failing is marked failing and still receives. A disabled endpoint receives nothing: its pending deliveries are cancelled, and replays to it are refused until you enable it. A replay keeps the event id, so a receiver that already handled the event ignores it.
Product 2 of 2
Crypto Splitter API #
Split incoming crypto between several recipients by percentage. Two shapes: a splitter is a persistent receiving wallet whose recipients are versioned (edits apply from the next block on), and a split session is a one-off fresh deposit address for a single order or request. Both settle confirmed deposits to the recipients through the custody provider; the platform commission is snapshotted when funds are accepted.
Share this section: https://cryptonera.com/developers#crypto-splitter
Base URL, keys, idempotency, errors and webhooks are shared with Crypto Billing: start with the quick start.
Endpoints #
| Endpoint | Scope | Idempotency-Key | Purpose |
|---|---|---|---|
| Splitters | |||
| GET /splitters | splitters:read | — | List splitters |
| POST /splitters | splitters:write | Optional | Create a splitter |
| GET /splitters/{id} | splitters:read | — | Read a splitter |
| PATCH /splitters/{id} | splitters:write | Optional | Change the recipients |
| POST /splitters/{id}/pause | splitters:write | Optional | Pause a splitter |
| POST /splitters/{id}/resume | splitters:write | Optional | Resume a splitter |
| Split sessions | |||
| GET /split-sessions | sessions:read | — | List split sessions |
| POST /split-sessions | sessions:write | Optional | Create a split session |
| GET /split-sessions/{id} | sessions:read | — | Read a split session |
| POST /split-sessions/{id}/close | sessions:write | Optional | Close a split session |
Assets, the fee policy, deposits, settlements and webhooks are in the shared endpoint table.
Example: create a fresh-address split session #
Two recipients on Tron receive USDT 60/40. Each confirmed deposit is settled as soon as it is confirmed. Replace the placeholders with addresses valid on the target network; addresses are validated before the session is created.
# split sessions have no order check: a new UUID per session protects a retry
curl -X POST https://cryptonera.com/api/v1/split-sessions \
-H "Authorization: Bearer $CRYPTONERA_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 7d4e1b2c-9f6a-4e38-a5d1-3b8c0e2f6a97" \
-d '{
"asset": "USDT_TRON",
"recipients": [
{
"address": "<valid-tron-recipient-A>",
"percentage": "60.00",
"label": "Studio"
},
{
"address": "<valid-tron-recipient-B>",
"percentage": "40.00",
"label": "Producer"
}
],
"settlement_trigger": "each_confirmed_deposit",
"order_id": "order-10021",
"expires_in_seconds": 3600
}'
Response (201 Created, or 202 Accepted with status allocating when no fresh address is available yet: poll status_url):
{
"id": "01J9Z2K7M3N4P5Q6R7S8T9V0WX",
"object": "split_session",
"transaction_id": "TST-4Q8M-2ZK7-VN3D",
"status": "awaiting_payment",
"environment": "sandbox",
"asset": "USDT_TRON",
"network": "tron-nile",
"deposit_address": "<fresh-tron-deposit-address>",
"memo": null,
"payment_uri": null,
"order_id": "order-10021",
"settlement_trigger": "each_confirmed_deposit",
"expected_amount": null,
"minimum_deposit": "1.000000",
"received_confirmed": "0.000000",
"settled_gross": "0.000000",
"held": "0.000000",
"recipients": [
{
"address": "<valid-tron-recipient-A>",
"memo": null,
"percentage": "60.00",
"label": "Studio"
},
{
"address": "<valid-tron-recipient-B>",
"memo": null,
"percentage": "40.00",
"label": "Producer"
}
],
"fee_policy": {
"snapshot_id": "01J9Z2K7M3N4P5Q6R7S8T9V0WY",
"platform_percentage": "1.00",
"recipient_basis": "after_platform_fee_and_disclosed_same_asset_costs",
"network_cost_mode": "separate_native_gas_budget",
"operator_fee_wallet_configured": true
},
"confirmation_policy": {
"required_confirmations": 19,
"finality": "solidified"
},
"freshness": {
"never_previously_allocated": true,
"prior_activity_observed": false,
"checked_at": "2026-10-12T10:00:00+00:00",
"provider": "trongrid"
},
"late_payment_policy": "hold_for_review",
"metadata": null,
"expires_at": "2026-10-12T11:00:00+00:00",
"closed_at": null,
"first_payment_at": null,
"settled_at": null,
"created_at": "2026-10-12T10:00:00+00:00",
"status_url": "https://cryptonera.com/api/v1/split-sessions/01J9Z2K7M3N4P5Q6R7S8T9V0WX",
"checkout_url": "https://cryptonera.com/pay/s/<public-token>",
"request_id": "req_01J9Z2K7M3N4P5Q6R7S8T9V0WZ"
}
Field values above are illustrative; the exact fee_policy and freshness objects depend on operator configuration. A persistent splitter is created the same way with POST /splitters (no expiry; recipients can be replaced later with PATCH, which opens a new version from the next block).
Lifecycle & events #
| Value | Meaning |
|---|---|
| allocating | No fresh address yet: poll status_url (or GET /split-sessions/{id}). |
| awaiting_payment | Waiting for the payment to deposit_address. |
| payment_detected | A payment is seen, waiting for confirmations. |
| processing | Confirmed; the split is being sent. |
| settled | Every recipient was paid. |
| partially_settled | Some transfers confirmed, others not (the operator reviews them). |
| expired | Closed at expires_at without a payment. |
| held | Kept aside (late or not payable as configured): see the settlement or split_session.held. |
| cancelled | Closed by you before a payment. |
Late or excess money after a session closed is held for you, never sent to the recipients. Events: split_session.*, splitter.*, deposit.* and settlement.*: see events.
Sandbox (test mode) #
Sandbox is a complete, separate copy of the platform for building and testing your integration. It runs on public test networks (Bitcoin testnet, Ethereum Sepolia, Base Sepolia, Arbitrum Sepolia, Polygon Amoy, Tron Nile) with free test coins that have no value. Sandbox and live never mix: keys, addresses, balances, webhooks and events are separate, and a ck_sandbox_ key can only ever see sandbox data.
Never send real cryptocurrency to a sandbox address. Ethereum-style (0x…) and Tron (T…) test addresses look exactly like real ones; real coins sent there are not credited. Hosted checkout pages in sandbox show a TEST MODE banner.
Testing step by step #
- Switch your dashboard to Sandbox and create an API key under Developers → API keys. It starts with
ck_sandbox_. - Create a splitter, split session or invoice with that key. The response contains a payment address on a test network (or a hosted checkout URL).
- Get free test coins from a faucet (table below) into your own test wallet: MetaMask with "Show test networks" on (add Base Sepolia, Arbitrum Sepolia and Polygon Amoy from chainlist.org), TronLink set to the Nile node, or a Bitcoin testnet wallet.
- Pay the address with the exact asset on the exact network. Invoices expect an exact amount, so pay from your wallet; a splitter address may also be pasted straight into a faucet (any amount above the asset's minimum deposit). Paying with a token such as USDC also needs a little of the network's own test coin (ETH, or POL on Amoy) for the network fee.
- After the network's confirmations you receive the same webhooks as in live (
deposit.confirmed,settlement.*,settlement.transfer_confirmed,invoice.paid…) with"environment": "sandbox", and the objects move through the same states. - To go live, the platform enables live for your account; then create a
ck_live_key and replace the sandbox key. Nothing from sandbox carries over.
Free test coins, per asset #
Faucets are free public services run by other companies; their limits and sign-in rules change from time to time. Test networks are slower and less reliable than real ones.
| Asset | How to get test coins |
|---|---|
| BTC Bitcoin Testnet |
coinfaucet.eu: Paste your tb1… address, solve the captcha, press "Get bitcoins". Arrives after one block (about 10–30 minutes). bitcoinfaucet.uo1.net: Same steps; small amounts, limited per day. Wallet: A Bitcoin testnet wallet (e.g. Electrum started in testnet mode). Addresses start with tb1. |
| BCH Bitcoin Cash Testnet |
tbch.googol.cash: Choose network "testnet4", paste your bchtest:… address, answer the sum and press "Get Coins!". Wallet: Electron Cash started in testnet4 mode (electron-cash --testnet4). Addresses start with bchtest:. |
| ETH Ethereum Sepolia |
Google Cloud Web3 faucet: Sign in with a Google account, paste your 0x… address, press "Receive". Arrives within a minute. Alchemy Sepolia faucet: Free Alchemy account; may ask for a small amount of real ETH on mainnet in the same address as an anti-abuse check. Wallet: MetaMask (Settings → Advanced → "Show test networks", then pick Sepolia) or any EVM wallet. |
| TRX Tron Nile Testnet |
Nile faucet: Paste your T… address, solve the captcha, press "Obtain". Test TRX arrives in seconds; limited per day. Wallet: TronLink (settings → Node → "Nile Testnet"). Addresses start with T. |
| USDT Ethereum Sepolia |
USDT has no official test token on Ethereum Sepolia (the issuer publishes none), so it can only be tested in live with a small amount. |
| USDT Tron Nile Testnet |
USDT has no official test token on Tron Nile Testnet (the issuer publishes none), so it can only be tested in live with a small amount. |
| USDC Ethereum Sepolia |
Circle USDC faucet: Choose USDC, pick the same network, paste your 0x… address, press "Send". Limited per hour. To pay with it you also need a little test ETH/POL on that network for the gas fee. Wallet: MetaMask (Settings → Advanced → "Show test networks", then pick Sepolia) or any EVM wallet. |
| USDC Base Sepolia |
Circle USDC faucet: Choose USDC, pick the same network, paste your 0x… address, press "Send". Limited per hour. To pay with it you also need a little test ETH/POL on that network for the gas fee. Wallet: MetaMask with the Base Sepolia network added (chainlist.org → search "Base Sepolia" → "Add to MetaMask"). |
| USDC Arbitrum Sepolia |
Circle USDC faucet: Choose USDC, pick the same network, paste your 0x… address, press "Send". Limited per hour. To pay with it you also need a little test ETH/POL on that network for the gas fee. Wallet: MetaMask with Arbitrum Sepolia added (chainlist.org → "Arbitrum Sepolia"). |
| USDC Polygon Amoy |
Circle USDC faucet: Choose USDC, pick the same network, paste your 0x… address, press "Send". Limited per hour. To pay with it you also need a little test ETH/POL on that network for the gas fee. Wallet: MetaMask with Polygon Amoy added (chainlist.org → "Amoy"). |
Reference files #
- OpenAPI 3.1 document (both products; every operation, object and webhook event): https://cryptonera.com/developers/openapi.json
- API revision 2026-10-12 (also the webhook
schema_version). docs/API.mdin the source repository has the same reference as Markdown.- Every table on this page is generated from the platform's code and its OpenAPI document.