On this page
Payment Flow
Follow an invoice from creation through asset selection, payment detection, confirmation, expiry, and the final webhook your system consumes.
Two ways to get paid
Paymos has two money-in products, and which one you use changes the shape of your integration:
| Invoice | Payment channel | |
|---|---|---|
| What it is | One payment request | One payer's permanent deposit identity |
| Amount | Fixed at creation | None — every deposit at or above the token's minimum is credited |
| Expiry | Yes | Never |
| Address | Derived per invoice | One permanent address per network, never reassigned |
| Terminal state | paid, paid_over, underpaid, expired, cancelled |
None — a channel keeps accepting until you block it |
| Best for | Checkouts, orders, one-off payments | Balance top-ups, recurring funding, accounts you invoice out of band |
The rest of this page describes the invoice lifecycle. For the channel branch see Create Payment Channel — a channel has no lifecycle of its own; each deposit that arrives has one, and it is much shorter: confirming → confirmed, with reorged in between if the chain reorganizes.
Lifecycle
The invoice ends in one terminal state: paid, paid_over, underpaid, expired, or cancelled. Most transitions fire a webhook event — the per-status table below lists which. Everything from confirming onward emits one. The two pre-payment states do not: opening in awaiting_client is silent, and so is the ordinary move into awaiting_payment when the checkout confirms the token. The one exception runs backwards — if a deep reorg removes every counted transfer, the invoice regresses to awaiting_payment and that regression does emit invoice.awaiting_payment.
Two creation flows
Both flows open the same way. The invoice is created in awaiting_client, with no address behind it yet. What differs is what you send and how much is left for the customer to decide. The address is allocated — and the invoice moves to awaiting_payment — at the moment the checkout confirms the token.
Direct crypto flow — provide amount + currency + network. The amount is the crypto amount to pay, and both the token and the chain are fixed at creation. The customer has nothing left to decide, so the checkout confirms that pair and the address is allocated straight away.
Fiat flow — provide amount + currency (no network). The amount is a fiat amount. The customer selects a token and network on the hosted checkout; at that moment Paymos locks the FX rate, assigns an address, and the invoice moves to awaiting_payment. Read the locked rate back from payment.exchange_rate on the invoice, alongside the exact payment.expected token amount.
See Supported currencies for the full list of fiat codes and tokens, and Create invoice for the request body.
Statuses
| Status | Description | Webhook event |
|---|---|---|
awaiting_client |
Opening status for every invoice — no address until the checkout confirms the token | — |
awaiting_payment |
Address assigned, waiting for transfer | — on the way in; invoice.awaiting_payment only on a reorg regression |
confirming |
Payment detected, waiting for confirmations | invoice.confirming |
underpaid_waiting |
Partial payment received, waiting for remaining | invoice.underpaid_waiting |
paid |
Payment confirmed in full | invoice.paid |
paid_over |
Received amount exceeds expected (credited in full) | invoice.paid_over |
underpaid |
Closed with insufficient payment | invoice.underpaid |
expired |
Timer ran out without payment | invoice.expired |
cancelled |
Cancelled by merchant (only from awaiting_client) |
invoice.cancelled |
Cancellation
An invoice can only be cancelled while in awaiting_client — before the checkout confirms the token. After that the address is live and the customer has been quoted a fixed amount on a specific chain, so cancellation is off the table.
Timeouts and expiration
| Setting | Default |
|---|---|
| Token selection window (fiat flow) | Configurable |
| Invoice expiration (after address assigned) | Configurable |
| Underpayment threshold | Configurable per project |
| Overpayment handling | Credited in full |
If the customer sends less than the required amount before the invoice expires, the resulting status depends on allow_multiple_payments:
- —
allow_multiple_payments: true— status becomesunderpaid_waiting, additional payments are accepted until the timer runs out - —
allow_multiple_payments: false— a single insufficient payment immediately closes the invoice withunderpaid
If the timer runs out while in underpaid_waiting, the final status is resolved from the total received amount and the underpayment policy. If the customer sends more than expected, the full amount is credited and the status becomes paid_over.