# CoinsFlow CoinsFlow is a self-hosted crypto payment gateway and Litecoin block explorer. Merchants create invoices through a REST API and send customers to a hosted payment page. Supported: Bitcoin (BTC), Litecoin (LTC), Ethereum (ETH, USDT, USDC), BNB Smart Chain (BNB, USDT, USDC) and TRON (TRX, USDT, USDC). The explorer covers Litecoin. ## Payments API Base URL: https://api.coinsflow.app Auth: Authorization: Bearer cf_live__ Keys: https://coinsflow.app/apis/dashboard (one key per merchant, shown once) Format: JSON. Send Content-Type: application/json with a body. Amounts: ALWAYS decimal strings ("0.015"), never JSON numbers. Fee: 0.5% per received payment. No monthly fee. ### POST /v1/invoices Create an invoice with a fresh address. Body: chain (bitcoin|litecoin|ethereum|bsc|tron), asset (BTC|LTC|ETH|BNB|TRX|USDT|USDC, must exist on the chain), and exactly one of amount (crypto, decimal string), priceAmount (USD, up to 8 decimals; rate locked on creation) or openAmount: true (pay what you want: no price, any confirmed payment makes it paid, amountDue stays "0", read amountPaid; never underpaid/overpaid). Optional: orderId, description, ttlSeconds (3-7 days in seconds, default 259200; shorter is raised to 3 days), metadata (up to 20 key/value pairs, returned on reads, never shown to the customer). Header Idempotency-Key (1-200 chars) makes retries safe, even concurrent ones (409 idempotency_in_progress while the first is running). Returns 201 with the invoice, including paymentUrl. curl https://api.coinsflow.app/v1/invoices \ -H "Authorization: Bearer $COINSFLOW_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: order-1042" \ -d '{"chain":"tron","asset":"USDT","priceAmount":"49.99","orderId":"order-1042"}' ### GET /v1/invoices/{id} Current state plus payments[] (txid, amount, confirmations, status mempool|confirming|confirmed|orphaned, blockHeight, seenAt). Fulfil when status is paid, overpaid or settled. txid is opaque: for ETH/BNB/TRX sent from inside a smart contract it is "internal:", not a transaction hash. ### GET /v1/invoices?status=&chain=&orderId=&q=&limit=&cursor= Newest first. status takes one value or several comma-separated. Follow nextCursor until it is null. ### POST /v1/invoices/{id}/cancel Only before any payment is detected; otherwise 409 not_cancellable. ### GET /v1/balances Per (chain, asset): pending (credited, not final), payable (final), reserved (the part of payable already claimed by a withdrawal in progress) and withdrawable (payable - reserved). ### GET /v1/me, GET /v1/stats Merchant profile; headline numbers. ### GET /chains, GET /prices, GET /status (no auth) Network status (invoices are refused with 503 chain_unavailable unless status is ok), USD prices, and overall platform status. ### Withdrawals GET /v1/withdrawals/quote?chain=&asset=&amount=&destination= -> fee, networkTransactions, minAmount, withdrawable, maxAmount. With amount (and destination) the fee is exact. The fee covers every network cost of the withdrawal, including transactions that bring funds together first. POST /v1/withdrawals {chain, asset, amount, destination, maxFee?, orderRef?} (+ Idempotency-Key header) -> 201 status "requested". amount is what arrives; fee is taken from the balance on top. maxFee (from the quote): a higher fee now is refused with 409 fee_changed, nothing created. Needs a key with the withdrawals permission AND an IP allowlist (set in the dashboard, requires 2FA). Otherwise 403 scope_required / ip_not_allowed. GET /v1/withdrawals/{id}, GET /v1/withdrawals?status=&chain=&limit=&cursor=, POST /v1/withdrawals/{id}/cancel (only while requested or approved). Statuses: requested -> approved -> signing -> broadcast -> confirmed; failed (refunded), cancelled, stuck (being resolved; never auto-refunded). Minimums: BTC 0.0002, LTC 0.01, ETH 0.002, BNB 0.005, TRX 20, USDT 5, USDC 5. automatic: true when an automatic withdrawal made it. Those are set up in the dashboard (Withdrawals): an asset, the merchant's own address, and when (balance reaches an amount, daily or weekly). They are ordinary withdrawals: same fee, limits, statuses and webhooks. ### Webhooks Manage with GET/POST /v1/webhooks, PATCH/DELETE /v1/webhooks/{id}, POST /v1/webhooks/{id}/test, GET /v1/webhooks/{id}/deliveries, POST /v1/webhooks/{id}/deliveries/{deliveryId}/retry, POST /v1/webhooks/{id}/rotate-secret. Events: invoice.detected, invoice.confirming, invoice.paid, invoice.underpaid, invoice.overpaid, invoice.settled, invoice.expired, invoice.cancelled, withdrawal.broadcast, withdrawal.confirmed, withdrawal.failed. Body: {id (delivery id, stable across retries), type, createdAt, previousStatus, data}. Verify: header CoinsFlow-Signature "t=,v1="; v1 = HMAC-SHA256(secret, "."). Reject if |now - t| > 300 s. Reply 2xx within 10 s; retries at 3 s, 30 s, 5 min, 1 h, 6 h, 24 h. ## Invoice statuses pending -> detected -> confirming -> paid (credited) -> settled (final, withdrawable). detected: on bitcoin and litecoin, seconds after the customer sends (still in the mempool, not counted); elsewhere, in its first block. A mempool payment dropped before mining (replaced or double-spent) reopens the invoice (pending, or expired past the deadline). Also: underpaid (top up to the same address), overpaid, expired (a late payment is still credited, paidLate: true), cancelled. Every address we issue is watched permanently: money sent to a cancelled or long-closed invoice is credited to the merchant as a deposit. paidWith: "chain", "coinsflow" or null. "coinsflow" = the customer paid from their CoinsFlow balance on the hosted page (Pay with CoinsFlow): settled at once, webhooks detected/paid/settled fire together, payments is empty. Treat it like any settled invoice. Confirmations to paid (invoice <= $500 / above): bitcoin 1/2, litecoin 2/6, ethereum 12, bsc 15, tron 20. Each invoice carries its own confirmationsRequired. Confirmations to settled (withdrawable): ethereum 32, bsc 30, tron 40. On bitcoin and litecoin it depends on what the payment is worth (<= $50 / <= $500 / <= $5,000 / above): bitcoin 1/2/3/6, litecoin 2/4/6/12, never before paid. A small payment can therefore be paid and settled in the same block: both events are sent, paid first. ## Errors JSON { "error": "", "detail": "" }. Codes: invalid_request, unsupported_asset, invalid_amount, invalid_cursor, unauthorized, merchant_inactive, not_found, idempotency_key_reuse, idempotency_in_progress, not_cancellable, price_unavailable, chain_unavailable, internal_error, invalid_destination, amount_too_small, insufficient_balance, scope_required, ip_not_allowed, withdrawals_disabled, withdrawals_paused. Rate limit: 50 requests/second per IP (bursts to 200), then HTTP 429. ## Not yet available Bitcoin Lightning. ## Links OpenAPI 3.1: https://coinsflow.app/openapi.json Docs: https://coinsflow.app/apis/docs Explorer: https://coinsflow.app/explorer/litecoin