State Diagram
Transaction States
| Status | Description | Next States |
|---|
deposit_pending | Waiting for deposit | deposit_detected, cancelled, expired |
deposit_detected | Deposit seen on-chain | deposit_confirmed |
deposit_confirmed | Required confirmations met | payout_processing |
payout_processing | Bank transfer in progress | completed, payout_failed |
completed | Payout successful | - |
payout_failed | Bank transfer failed | payout_processing, manual_review |
cancelled | Transaction cancelled | - |
expired | Quote expired | - |
Webhooks by State
Each state transition triggers a webhook:
// deposit_pending → deposit_detected
{
"event": "transaction.deposit_detected",
"data": {
"transactionId": "txn_xyz",
"deposit": {
"txHash": "0x...",
"amount": "100",
"confirmations": 1
}
}
}
// deposit_detected → deposit_confirmed
{
"event": "transaction.deposit_confirmed",
"data": {
"transactionId": "txn_xyz",
"deposit": {
"confirmations": 30
}
}
}
// payout_processing → completed
{
"event": "transaction.payout_completed",
"data": {
"transactionId": "txn_xyz",
"payout": {
"amount": "8407.75",
"utr": "123456789012"
}
}
}
Confirmation Requirements
| Network | Confirmations | Typical Time |
|---|
| Polygon | 30 | ~1 minute |
| Ethereum | 12 | ~3 minutes |
| Arbitrum | 20 | ~30 seconds |
| Base | 20 | ~30 seconds |
| Solana | Finalized | ~15 seconds |
Payout Requirements
Before a payout can be initiated, the system verifies:
| Requirement | Check |
|---|
| KYC Status | Must be verified |
| KYC Level | Must be basic or enhanced |
| Bank Account | Must exist and be verified |
KYC is checked both at transaction creation AND at payout time. This ensures payouts only go to fully verified users.
Payout Retries
If a payout fails:
- Auto-retry: System retries up to 3 times with exponential backoff
- Manual review: After 3 failures, transaction goes to manual review
- Resolution: Support team investigates and processes manually
Common payout failures:
- KYC not verified (
KYC_NOT_VERIFIED)
- KYC level insufficient (
KYC_LEVEL_INSUFFICIENT)
- Invalid bank account (
BANK_ACCOUNT_INVALID)
- Bank maintenance window
- Account frozen/closed
Quote Expiration
Transactions must receive deposits within 24 hours:
- After 24h, quote expires and transaction moves to
expired
- User must create a new transaction for a fresh quote
- Exchange rate may differ from original quote
Polling vs Webhooks
We recommend webhooks for real-time updates, but you can also poll:
# Poll transaction status
curl https://api.stablepay.global/v2/transactions/{transactionId} \
-H "Authorization: Bearer YOUR_API_KEY"
Rate limit: 100 requests/minute. Use webhooks for high-volume integrations.