Transaction Statuses
The status a transaction can carry, from initiated to final.
Statuses
Grouped by lifecycle phase- createdInitiated
The withdrawal has been recorded but not yet transmitted — typically the brief window between submission and being broadcasted on chain.
- pendingExecuting
CoinPayments has started executing the spend — signing it and broadcasting it to the network.
- processingAwaiting confirmation
The transaction is visible on-chain and is being watched for the confirmation depth CoinPayments requires.
- pendingReceiveDeposit awaiting confirmations
A deposit has been detected on-chain and is being watched until it reaches the required confirmation depth. A webhook fires on every new confirmation. On the final confirmation the status becomes confirmedOnBlockchain.
- completedOff-chain settlement - final
For transactions that settle off the chain — transfers between CoinPayments users, moves between your own wallets, and conversions or sweeps that stay on the platform — this is the terminal success state. They complete instantly, with no confirmations to wait for. For an external send or deposit, completed is not terminal. The transaction will be broadcast to the network and accrue confirmations. See confirmedOnBlockchain.
- confirmedOnBlockchainOn-chain settlement - final
The terminal success state for transactions that settle on the chain — external deposits and withdrawals, plus any conversion or auto-sweep whose funds actually move on-chain — reached once the currency's required number of confirmations is met. These never become completed; confirmedOnBlockchain is the final state.
- expiredExpired before confirming
The transaction sat too long without confirming and has been released.
- failedFailed before the chain
Sending was not successful before the transaction reached the chain (for example, no connection to the network). The amount is refunded.
- failedOnBlockchainFailed on-chain
The transaction reached the chain but errored while processing — most often a smart contract running out of gas. The blockchain fee is not refunded.
- cancelledCancelled
Manually cancelled by CoinPayments support and refunded.
- rejectedRejected
Manually rejected by CoinPayments support and refunded.
- unknownStatus could not be classified
Treat as a soft error and log for follow-up — production traffic should not produce this value.
Which status means 'done' depends on the transaction
There is no single terminal success status — it depends on whether the transaction settles on-chain or off-chain. Off-chain moves (internalReceive / internalSpend, sameUserReceive / sameUserSpend) finish at completed. On-chain moves (external deposits and withdrawals) finish at confirmedOnBlockchain and never reach completed. conversion and autoSweeping go whichever way their funds actually settle: kept on the platform → completed; moved on-chain → confirmedOnBlockchain. Key your credit logic off the right terminal status for the transaction's type.
The dashboard shows both as 'Completed'
The dashboard and transaction history collapse confirmedOnBlockchain and completed into a single Completed label, so the UI will not tell them apart. When you need the real state — for reconciliation or to decide whether an external transaction has actually confirmed — read the raw transactionStatus from the API or webhook, not the dashboard label.
Webhooks fire on status transitions
You don't have to poll for these states. Subscribe to a webhook and CoinPayments will push every status change to your endpoint — plus one on each new on-chain confirmation while a deposit is pendingReceive or a send is awaiting confirmation. Verify the HMAC signature, key your credit logic off the new status, and credit only on the terminal status for the transaction (confirmedOnBlockchain for external, completed for internal).
Status casing differs between the API and webhooks
The REST Transactions API returns these values in camelCase (e.g. confirmedOnBlockchain), while wallet webhook payloads deliver them in PascalCase (e.g. ConfirmedOnBlockchain). Compare the value case-insensitively so your code works against both.
Where to next?
Pick the most useful next step.
