NoHoldPay

Payment Lifecycle

Every status a payment can reach, the transitions between them, and what action (if any) to take.

A payment moves through a small number of states. Most payments follow the same line from creation to confirmed. Branches exist for partial pay, expiry, reorg, and refund.

Lifecycle

Normal path

pending

awaiting_payment

payment_detected

confirming

confirmed ✓

POST /api/v1/payments returns the payment in pending state. As soon as a chain is selected on the checkout (either by the customer or by your code via /checkout/{id}/select-chain), it flips to awaiting_payment. When the customer's transaction appears on-chain it becomes payment_detected, then confirming, and finally confirmed once the chain's confirmation rule is satisfied.

Cancellation and expiry

pending

/

awaiting_payment

cancelled

customer or merchant cancels before any funds arrive

pending

/

awaiting_payment

expired

window closed with no funds received

Underpayment

payment_detected

/

confirming

underpaid

cumulative short by more than the tolerance

underpaid

confirmed ✓

customer tops up, or merchant accepts the shortfall

underpaid

refund_requested

refunded

customer requests refund, merchant marks refunded

underpaid

expired_underpaid

window closes with the partial amount still short

Late-grace revival

expired

/

expired_underpaid

payment_detected

a late transaction that fully covers the invoice within the late-grace window

expired

/

expired_underpaid

underpaid

a late transaction that only partially covers. The merchant can still accept or refund

Both expired and expired_underpaid payments revive this way. Once revived, the payment is flagged so it is never auto-expired again. A revived underpaid payment waits for a top-up, a merchant accept, or a refund.

Reorg

confirming

/

confirmed

expired_reorged

confirming tx removed by a chain reorg after the payment window closed

confirmed

underpaid

rare: reorg of a contributing tx drops the cumulative back under tolerance


State reference

Normal path

StatusWebhook eventMeaning
pendingnonePayment created. The customer has not picked a chain yet.
awaiting_paymentpayment.awaitingChain picked and the locked crypto amount displayed. Waiting for the customer to send.
payment_detectedpayment.detectedTransaction seen in the mempool or block. Not yet final.
confirmingpayment.confirmingOn-chain. Waiting for the chain's required confirmations.
confirmedpayment.confirmedFinal. Funds are in your wallet. Platform fee is deducted from Prepaid Credit.

confirmed is the terminal success state. Safe to release goods or services on this event.

Overpayment

StatusWebhook eventMeaning
confirmed + overpayment flagpayment.overpaidCustomer sent more than the invoice amount. The payment is still confirmed. The event carries the excess amount.

payment.overpaid fires only when the overpayment exceeds the per-merchant threshold (default 1%). Below that, the payment confirms silently.

Underpayment

StatusWebhook eventMeaning
underpaidpayment.underpaidCumulative received is below the invoice amount by more than the tolerance (default 0.5%). Invoice is still open.
refund_requestedpayment.refund_requestedCustomer requested a refund from the checkout. Awaits merchant action.
refundedpayment.refundedMerchant marked the payment refunded after sending coins back.

underpaid is non-terminal. The window is still open and the customer can top up. The merchant can also accept the shortfall via the dashboard.

Expiry and reorg

StatusWebhook eventMeaning
expiredpayment.expiredPayment window closed with no funds received. A late transaction within the grace window can still revive it.
expired_underpaidpayment.expired_underpaidWindow closed with a partial amount. A late transaction within the grace window can still revive the payment (to underpaid, where the merchant can then accept the shortfall). Otherwise the customer can request a refund from the checkout, and the merchant records the refund once sent.
expired_reorgedpayment.expired_reorgedA confirming or confirmed transaction was removed from the chain by a reorg after the payment window had closed. Does not revive.

A payment in payment_detected or confirming that is fully paid (within tolerance) never expires on the timer. One that is still short beyond the tolerance when the window closes moves to expired_underpaid.

A late on-chain transaction within the per-chain late-grace window after expires_at revives an expired or expired_underpaid payment: full coverage moves it to payment_detected, partial coverage moves it to underpaid.

Cancelled

StatusWebhook eventMeaning
cancelledpayment.cancelledCancelled before any on-chain transaction was detected. Can be triggered by the customer (Cancel button on the checkout) or the merchant (DELETE /api/v1/payments/{id}). Returns PAYMENT_NOT_CANCELLABLE if the status is payment_detected or later.

Failed

StatusWebhook eventMeaning
failedpayment.failedThe gasless settlement for this payment failed and no funds landed. Terminal and system-initiated. Never reached by a user or merchant cancel (those use cancelled). The payload's failure_reason carries one of insufficient_balance (customer's wallet held less than the payment amount at settlement time), estimate_revert (the token contract rejected the settlement simulation), or settlement_reverted (the settlement transaction was broadcast but reverted on-chain). The first two are pre-broadcast aborts. Today the x402 (gasless) relay is the only path that produces failed, either before broadcast or on a reverted settlement.

Payment window

The invoice amount in crypto is locked for the payment window. When the window closes without confirmation, the payment expires. Late-grace revival is the period after expiry during which a late on-chain transaction can still revive the payment.

Chain familyPayment windowLate-grace revival
Bitcoin, Litecoin, Bitcoin Cash, Dogecoin60 minutes12 hours
Ethereum, Base, Polygon, BNB Chain, Ethereum Sepolia20 minutes12 hours
Solana15 minutes12 hours
TRON15 minutes12 hours
XRP20 minutes12 hours
Stellar20 minutes12 hours
Monero2 hours12 hours

The window is set per chain. When a payment accepts multiple chains, the longest window applies until the customer picks a specific chain. The window is not configurable per payment from the API.


Tolerances

Both tolerances are merchant-configurable in the dashboard under SettingsPayment preferences:

SettingDefaultWhat it controls
Underpayment tolerance0.5%Shortfalls below this confirm automatically. Above, the status becomes underpaid and the customer can top up or request a refund.
Overpayment threshold1%Overpayments above this fire payment.overpaid with the excess amount. Below, the payment confirms silently.

Both values are snapshotted onto the payment row at creation. Changing the setting later does not retro-shift open invoices.


Dashboard and API names

The API and webhooks use the snake_case status strings. The dashboard renders friendlier labels.

Dashboard labelAPI / webhook status
Pendingpending
Awaiting paymentawaiting_payment
Payment detectedpayment_detected
Confirmingconfirming
Confirmedconfirmed
Underpaidunderpaid
Refund requestedrefund_requested
Refundedrefunded
Expiredexpired
Expired (underpaid)expired_underpaid
Expired (reorg)expired_reorged
Cancelledcancelled
Failedfailed

Safe confirmation

Do not release goods or services on payment_detected or confirming. Both can roll back.

Release only on payment.confirmed. For high-value orders, verify the webhook signature before acting. See Webhooks.

On this page