Overview
StableStack sends webhook events for transaction status changes across wallet deposits, withdrawals, and payouts. Each event includes a signed payload for verification and the complete transaction details.Event Types
Wallet Events
wallet.transaction.inbound
Description: Triggered when a deposit/inbound transaction completes successfully. Triggered When:- User receives funds via Circle wallet deposit
- Transaction status changes to COMPLETED
- Funds are reflected in user’s wallet
- `amount`: Transaction amount (string, decimal)
- `asset_code`: Asset code (USDC, etc.)
- `network`: Blockchain network (polygon, ethereum, etc.)
- `crypto_transaction_id`: On-chain transaction hash
- `status`: Always COMPLETED for this event
- `balance`: User’s wallet balance after transaction
- `sender_user`: Nested object with sender identity details
- `receiver_user`: Nested object with receiver identity details
- `receiver_wallet_details`: Wallet address and network info
wallet.transaction.outbound
Description: Triggered when a withdrawal/outbound transaction completes or updates. Triggered When:- User initiates a withdrawal via Circle wallet
- Transaction reaches CONFIRMED or COMPLETE state
- Transaction fails with FAILED status
- `status`: COMPLETED or FAILED
- `crypto_transaction_id`: On-chain transaction hash
- `sender_wallet_details`: Originating wallet info
- `receiver_wallet_details`: Destination wallet info
Payout Events
payout.initiated
Description: Triggered when a payout request is initiated and confirmed. Triggered When:- User initiates a fiat payout (ZAR, NGN, USD, etc.)
- Transaction enters PROCESSING status
- `recipient_currency`: Target fiat currency (ZAR, NGN, USD, etc.)
- `bank_name`: Receiving bank for the payout
- `bank_account`: Bank account number (masked where possible)
- `reference_id`: transaction reference
payout.processing
Description: Triggered when a payout is actively being processed. Triggered When:- Payout passes initial validation
- Funds are being transferred to the recipient bank
- Processing has started
- `recipient_currency`: Target fiat currency (ZAR, NGN, USD, etc.)
- `bank_name`: Receiving bank for the payout
- `bank_account`: Bank account number
- `withdrawal_recipient_name`: Recipient name at receiving bank
- `reference_id`: transaction reference
payout.completed
Description: Triggered when a payout reaches the recipient’s bank account successfully. Triggered When:- Funds are confirmed received at destination bank
- confirms successful completion
- Transaction status changes to COMPLETED
- `status`: Always COMPLETED for this event
- `reference_id`: Use for tracking
- `bank_account`: Receiving bank account
- `withdrawal_recipient_name`: Recipient name at receiving bank
payout.failed
Description: Triggered when a payout fails at any stage. Triggered When:- Bank rejects the transfer
- Insufficient funds or invalid account details
- Transaction status changes to FAILED
- `status`: Always FAILED for this event
- `reference_id`: Use for tracking
- `bank_account`: Intended receiving bank account
payout.cancelled
Description: Triggered when a payout is cancelled. Triggered When:- User cancels the payout before processing
- Admin cancels the transaction
- Cancels due to compliance/fraud checks
- Transaction status changes to CANCELLED
- `status`: Always CANCELLED for this event
- `reference_id`: Use for tracking
Webhook Payload Structure
All webhook payloads follow this structure:- `id`: Unique event ID
- `timestamp`: Unix timestamp in milliseconds
- `event_type`: Event type string
- `data`: Event-specific transaction data
Signature Verification
All webhooks are signed with HMAC-SHA256. To verify:- Extract `t` (timestamp) and `s` (signature) from the signature header
- Create message: timestamp + ”.” + stringified_payload
- Generate HMAC-SHA256 with your signing secret
- Compare computed signature with received signature
- Always verify the signature before processing
- Check timestamp is recent (within 5 minutes recommended)
- Store `signing_secret` securely, never expose in client-side code
- Implement idempotency using event `id` to handle retries
Common Transaction Data Fields
All transaction payloads include these core fields:| Field | Type | Description |
|---|---|---|
| `id` | string | Unique transaction identifier |
| `user_id` | string | User who initiated the transaction |
| `transaction_type` | string | DEPOSIT, WITHDRAWAL, PAYOUT |
| `amount` | string | Transaction amount (decimal) |
| `currency` | string | Base currency (USDC, ZAR, NGN, etc.) |
| `status` | string | Current transaction status |
| `description` | string | Human-readable description |
| `created_at` | ISO 8601 | Timestamp when created |
| `updated_at` | ISO 8601 | Timestamp of last update |
Retry Policy
StableStack retries failed webhook deliveries with exponential backoff:| Attempt | Wait Time |
|---|---|
| 1st | Immediate |
| 2nd | 1 minute |
| 3rd | 5 minutes |
| 4th | 30 minutes |
| 5th | 4 hours |
- ✅ Retried: 5xx errors, timeouts, network errors, 408, 429
- ❌ Not retried: 4xx errors (except 408, 429)
- Respond with 2xx status immediately
- Process webhook asynchronously
- Use event `id` for idempotency
- Implement your own retry logic for downstream failures
Testing Webhooks
Use the dashboard to:- Send test events
- View delivery history
- Check response logs
- Verify signature validation