Confirmations
When a payment counts, what the buyer and the merchant see while it confirms, and why it is "confirmed", not "final".
Last updated Oct 27, 2026
The thresholds
An invoice is credited only after the transfer has reached its network's confirmation threshold. The count is one formula everywhere (confirmationsAt): head − block + 1, so the block that contains the transfer is confirmation 1.
| Network | Threshold | Typical block | ≈ Wait |
|---|---|---|---|
| Polygon PoS / Amoy | 64 | 2 s | ~2 min |
| BNB Chain / BSC Testnet | 15 | 3 s | ~45 s |
| Base / Base Sepolia | 30 | 2 s | ~1 min |
Testnet thresholds can be lowered for development with CRYPTO_CONFIRMATIONS_<NETWORK> (e.g. CRYPTO_CONFIRMATIONS_POLYGON_AMOY=5). Mainnet overrides are refused at boot in production. Each invoice stores the threshold it was created with (required_confirmations), so changing the env never moves an invoice that is already confirming.
How a transfer is followed
The worker's scan job runs every 2 s per network:
- Reads the logs of every canonical token contract of the network sent to the active deposit addresses (
eth_getLogs, in ranges of at mostCRYPTO_LOGS_MAX_RANGE_<NETWORK>blocks, default 200, and at most 20 addresses per query; a range the provider refuses is halved until it passes). - Re-scans the last 12 blocks on every tick and re-checks block hashes: a transfer whose block was replaced by a reorg is marked
ORPHANED, and an invoice that only had orphaned transfers goes back fromDETECTEDtoPENDING. - Updates confirmations and runs the invoice state machine. Status, ledger posting, refunds and the outbox events of one evaluation are written in a single database transaction.
The first sighting moves the invoice to DETECTED (the buyer is told the payment is on its way and can close the page). Reaching the threshold moves it to PAID, OVERPAID or UNDERPAID and posts the settlement to the ledger. A DETECTED invoice never expires: the buyer paid in time.
What each side sees:
- Checkout: a
n / requiredconfirmation bar with an ETA, pushed over Server-Sent Events (GET /api/v1/public/pay/:id/stream, falling back to polling every 4 s). - Dashboard: the invoice line says "Confirming n/required"; the split bar is drawn hollow until the invoice settles.
- Webhooks:
invoice.detectedat the first sighting,invoice.paid(andinvoice.overpaid) at the threshold.
"Confirmed", not "final"
The thresholds are a risk policy chosen per network: deep enough that a reorg reaching back past them is not a practical concern, short enough for checkout. They are not the chain's protocol finality, which the gateway does not track. That is why the product says "Confirmed at 64/64 confirmations" and never "final", "irreversible" or "settled forever", in the checkout, in the dashboard, in emails and in these docs. Keep that rule when you write copy.
The same thresholds gate money that leaves: a sweep, a payout or a refund is COMPLETED only when its own transaction reaches the network's threshold, and a refund is sent only after the deposit's sweep is confirmed.