status is stored verbatim and returned on every read. completed means the
destination-side payment was confirmed; a broadcast or payer-claimed
transaction is not completion.
Intents come before the session
A multi-destination deposit — created withdestinations or a
multi-pair destinationRecipients — does not start as a session. It starts as an
intent: status: "awaiting_destination", no quote, no deposit address, no
fiat order. An intent never reaches a paid state on its own.
PUT records a tentative choice and leaves the intent open; POST freezes it
into an ordinary single-destination session and the rest of this table applies.
A single-destination create (the ordinary shape, or a one-item multi-destination
list) skips the intent entirely and starts at created.
Reading the timeline
A session that never leftcreated was never paid — look at the payer’s
wallet, not at our settlement. One that reached pending and stopped has funds
in flight and is worth investigating on-chain.
expiresIn sets the TTL at creation; the default comes from server config.
Expiry is not a failure — it usually means the payer walked away.
Never fulfil from onSuccess, an iframe callback, a requested amount, or a
payer-claimed transaction hash. A signed webhook is a prompt to perform a
server-side session read; the read must show exactly completed. A merchant
without a backend uses the Dashboard as the authority, with email serving only
as a prompt to check it.
duplicate, late, and out-of-bounds are exception outcomes, not successful
payments. A duplicate is an additional confirmed payment on a consumed
single-use link; late means payment reached a cancelled or expired session;
out-of-bounds means the verified amount missed the session’s configured bounds.
Keep each for reconciliation and do not fulfil automatically.
Keys select an environment. Keep live and test keys, links, webhooks and
session reads separate; never use a test result to fulfil a live order.
Idempotency
orderId is the idempotency key. Creating twice with the same one returns the
existing non-terminal session and a 200 rather than a 201, so a retried
request cannot charge a payer twice. Send one, and send the same one on retry.