Status lifecycle

A swap moves through a sequence of statuses from creation to terminal state. Poll GET /v1/swaps/:id until terminal.

Happy path

waiting → confirming → exchanging → sending → finished

All statuses

StatusTerminalMeaningSuggested partner UX
waitingnoAwaiting incoming payment to payinAddressShow "Send <amountFrom> <from> to <payinAddress>" with a copy button
confirmingnoDetected on chain — waiting for confirmations"Detected on chain — waiting for N confirmations"
exchangingnoConfirmed; the swap is executing"Exchanging…" with a spinner
sendingnoFunds en route to payoutAddress"Sending to your wallet…"
finishedyesSwap complete; user has been creditedSuccess state with a link to the payout tx (when available)
failedyesFailed — most often the deposit was below minimum"Swap failed — <message>". Offer support contact
refundedyesFunds returned to refundAddress (or to the original sending address when refundAddress was omitted and the chain supports it)"Refunded — see <refundHash>"
overdueyesFloat-rate window expired before payin landed"Window closed. Start a new swap."
expiredyesFixed-rate window expired before payin landed"Window closed. Start a new swap."
holdconditionalAML/KYC review — funds held until verifiedDirect user to email security@ghostswap.io. Do not promise a resolution time

When to stop polling

Stop polling when status is in the terminal set:

const TERMINAL = new Set(['finished', 'failed', 'refunded', 'overdue', 'expired']);
 
if (TERMINAL.has(swap.status)) {
  // Update your DB, credit the user, etc.
  return;
}

hold is a soft-block — keep polling at a slower cadence (every few minutes) since it can resolve to finished or refunded after manual review.

How fresh is the status?

A background worker on our side polls upstream every ~30 seconds for any swap not in a terminal state. So your worst-case lag from "user's deposit was confirmed on chain" → "your dashboard shows confirming" is ~30 seconds + your poll cadence.

For end-to-end "real-time" feel, poll us every 10 seconds while the partner-facing UI is open. We absorb the upstream cost.

Common transitions

  • waitingoverdue: user never sent the funds within the float window. Status flips after ~36 hours of inactivity. Refund flow doesn't apply (no funds were received).
  • waitingconfirmingfailed: the user sent an amount under the pair's minimum. Funds are received but cannot be exchanged. Goes to refunded shortly after — funds return to refundAddress if one was provided, otherwise to the original sending address where the chain supports it (a small number of chains require refundAddress for automatic refund and will queue the refund for support otherwise).
  • confirminghold: the deposit triggered an AML flag. Funds are held; the user must complete KYC verification — direct them to security@ghostswap.io.
  • holdfinished: KYC cleared, swap completed.
  • holdrefunded: KYC failed or the user requested a refund.

See also