Skip to main content

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
Payload Example:
{
  "id": "evt_a0b8f4cc-95c4-4c74-9b18-050813546eb5",
  "timestamp": 1778538982206,
  "event_type": "wallet.transaction.inbound",
  "signature": "t=1778538982206,s=a43187f66dff73d9681a4ef43c5c93349d95fc10b6dc0482ec65e39b59564a83",
  "data": {
    "id": "dd1aebfd-acec-4367-a8dd-bdecea396753",
    "user_id": "318325f3-e612-4c57-8ce9-edfcbdcce1f3",
    "transaction_type": "DEPOSIT",
    "transaction_mode": "CREDIT",
    "amount": "20.00000000",
    "asset_code": "USDC",
    "network": "polygon",
    "status": "COMPLETED",
    "balance": "40.00000000",
    "transaction_id": "DCVDZZPVFC",
    "crypto_transaction_id": "0xda988304ee4506e9d8db67f2c90b7cb91aaf93457af6e9c05b9f377241764ca5",
    "reference_id": "e8ca80e3-10b2-5eab-a66b-c9edce037871",
    "sender_user": {
      "first_name": null,
      "last_name": null,
      "businessName": null,
      "role": null
    },
    "sender_wallet_details": {
      "address": null,
      "network": null,
      "currency": null,
      "bank_code": null,
      "bank_name": null,
      "account_number": null
    },
    "receiver_user": {
      "first_name": null,
      "last_name": null,
      "businessName": "ten demo ltd",
      "role": "merchant"
    },
    "receiver_wallet_details": {
      "address": "0x80fc73f856968ff4035ed239f82a962e50b1bae3",
      "network": "polygon",
      "currency": "USDC",
      "country": null,
      "bank_code": null,
      "bank_name": null,
      "account_number": null
    },
    "created_at": "2026-05-11T22:36:19.000Z",
    "updated_at": "2026-05-11T22:36:19.000Z"
  }
}
Important Fields:
  • `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
Payload Example:
{
  "id": "evt_550e8400-e29b-41d4-a716-446655440002",
  "timestamp": 1715420500000,
  "event_type": "wallet.transaction.outbound",
  "signature": "t=1715420500000,s=bcde2345fghi6789jklm0123nopq4567...",
  "data": {
    "id": "txn_550e8400-e29b-41d4-a716-446655440003",
    "user_id": "user_123",
    "transaction_type": "WITHDRAWAL",
    "transaction_mode": "DEBIT",
    "amount": "500.00",
    "asset_code": "USDC",
    "network": "matic",
    "status": "COMPLETED",
    "balance": "500.00",
    "transaction_id": "WTHD123456",
    "crypto_transaction_id": "0x9876543210fedcba",
    "reference_id": "ref_withdrawal_001",
    "sender_user": {
      "first_name": "John",
      "last_name": "Doe",
      "businessName": null,
      "role": null
    },
    "sender_wallet_details": {
      "address": "0x1111111111111111111111111111111111111111",
      "network": "matic",
      "currency": "USDC",
      "bank_code": null,
      "bank_name": null,
      "account_number": null
    },
    "receiver_user": {
      "first_name": null,
      "last_name": null,
      "businessName": null,
      "role": null
    },
    "receiver_wallet_details": {
      "address": "0x2222222222222222222222222222222222222222",
      "network": "matic",
      "currency": "USDC",
      "country": null,
      "bank_code": null,
      "bank_name": null,
      "account_number": null
    },
    "created_at": "2025-05-11T10:30:00Z",
    "updated_at": "2025-05-11T10:35:00Z"
  }
}
Important Fields:
  • `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
Payload Example:
{
  "id": "evt_550e8400-e29b-41d4-a716-446655440004",
  "timestamp": 1715420600000,
  "event_type": "payout.initiated",
  "signature": "t=1715420600000,s=cdef3456ghij7890klmn1234opqr5678...",
  "data": {
    "id": "txn_550e8400-e29b-41d4-a716-446655440005",
    "user_id": "user_123",
    "transaction_type": "PAYOUT",
    "amount": "10000.00",
    "asset_code": "ZAR",
    "status": "PROCESSING",
    "reference_id": "FW-20250511-001234",
    "transaction_id": "PAYOUT12345",
    "recipient_currency": "ZAR",
    "bank_name": "ABSA Bank",
    "bank_account": "9876543210",
    "withdrawal_recipient_name": "John Doe",
    "sender_user": {
      "first_name": "Merchant",
      "last_name": "Account",
      "businessName": null,
      "role": "merchant"
    },
    "sender_wallet_details": {
      "address": null,
      "network": null,
      "currency": "ZAR",
      "bank_code": null,
      "bank_name": null,
      "account_number": null
    },
    "receiver_user": {
      "first_name": "John",
      "last_name": "Doe",
      "businessName": null,
      "role": null
    },
    "receiver_wallet_details": {
      "address": null,
      "network": null,
      "currency": "ZAR",
      "country": null,
      "bank_code": "123456",
      "bank_name": "ABSA Bank",
      "account_number": "9876543210"
    },
    "created_at": "2025-05-11T10:40:00Z",
    "updated_at": "2025-05-11T10:42:00Z"
  }
}
Important Fields:
  • `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
Payload Example:
{
  "id": "evt_550e8400-e29b-41d4-a716-446655440006",
  "timestamp": 1715420700000,
  "event_type": "payout.processing",
  "signature": "t=1715420700000,s=defg4567hijk8901lmno2345pqrs6789...",
  "data": {
    "id": "txn_550e8400-e29b-41d4-a716-446655440005",
    "user_id": "user_123",
    "transaction_type": "PAYOUT",
    "amount": "10000.00",
    "asset_code": "ZAR",
    "status": "PROCESSING",
    "reference_id": "FW-20250511-001234",
    "transaction_id": "PAYOUT12345",
    "recipient_currency": "ZAR",
    "bank_name": "ABSA Bank",
    "bank_account": "9876543210",
    "withdrawal_recipient_name": "John Doe",
    "receiver_user": {
      "first_name": "John",
      "last_name": "Doe",
      "businessName": null,
      "role": null
    },
    "receiver_wallet_details": {
      "address": null,
      "network": null,
      "currency": "ZAR",
      "country": null,
      "bank_code": "123456",
      "bank_name": "ABSA Bank",
      "account_number": "9876543210"
    },
    "created_at": "2025-05-11T10:40:00Z",
    "updated_at": "2025-05-11T10:45:00Z"
  }
}
Important Fields:
  • `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
Payload Example:
{
  "id": "evt_550e8400-e29b-41d4-a716-446655440007",
  "timestamp": 1715507100000,
  "event_type": "payout.completed",
  "signature": "t=1715507100000,s=efgh5678ijkl9012mnop3456qrst7890...",
  "data": {
    "id": "txn_550e8400-e29b-41d4-a716-446655440005",
    "user_id": "user_123",
    "transaction_type": "PAYOUT",
    "amount": "10000.00",
    "asset_code": "ZAR",
    "status": "COMPLETED",
    "reference_id": "FW-20250511-001234",
    "transaction_id": "PAYOUT12345",
    "recipient_currency": "ZAR",
    "bank_name": "ABSA Bank",
    "bank_account": "9876543210",
    "withdrawal_recipient_name": "John Doe",
    "receiver_user": {
      "first_name": "John",
      "last_name": "Doe",
      "businessName": null,
      "role": null
    },
    "receiver_wallet_details": {
      "address": null,
      "network": null,
      "currency": "ZAR",
      "country": null,
      "bank_code": "123456",
      "bank_name": "ABSA Bank",
      "account_number": "9876543210"
    },
    "created_at": "2025-05-11T10:40:00Z",
    "updated_at": "2025-05-12T10:05:00Z"
  }
}
Important Fields:
  • `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
Payload Example:
{
  "id": "evt_550e8400-e29b-41d4-a716-446655440008",
  "timestamp": 1715507200000,
  "event_type": "payout.failed",
  "signature": "t=1715507200000,s=fghi6789jklm0123nopq4567rstu8901...",
  "data": {
    "id": "txn_550e8400-e29b-41d4-a716-446655440005",
    "user_id": "user_123",
    "transaction_type": "PAYOUT",
    "amount": "10000.00",
    "asset_code": "ZAR",
    "status": "FAILED",
    "reference_id": "FW-20250511-001234",
    "transaction_id": "PAYOUT12345",
    "recipient_currency": "ZAR",
    "bank_name": "ABSA Bank",
    "bank_account": "9876543210",
    "withdrawal_recipient_name": "John Doe",
    "receiver_user": {
      "first_name": "John",
      "last_name": "Doe",
      "businessName": null,
      "role": null
    },
    "receiver_wallet_details": {
      "address": null,
      "network": null,
      "currency": "ZAR",
      "country": null,
      "bank_code": "123456",
      "bank_name": "ABSA Bank",
      "account_number": "9876543210"
    },
    "created_at": "2025-05-11T10:40:00Z",
    "updated_at": "2025-05-12T10:10:00Z"
  }
}
Important Fields:
  • `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
Payload Example:
{
  "id": "evt_550e8400-e29b-41d4-a716-446655440009",
  "timestamp": 1715420800000,
  "event_type": "payout.cancelled",
  "signature": "t=1715420800000,s=ghij7890klmn1234opqr5678stuv9012...",
  "data": {
    "id": "txn_550e8400-e29b-41d4-a716-446655440005",
    "user_id": "user_123",
    "transaction_type": "PAYOUT",
    "amount": "10000.00",
    "asset_code": "ZAR",
    "status": "CANCELLED",
    "reference_id": "FW-20250511-001234",
    "transaction_id": "PAYOUT12345",
    "recipient_currency": "ZAR",
    "bank_name": "ABSA Bank",
    "bank_account": "9876543210",
    "withdrawal_recipient_name": "John Doe",
    "receiver_user": {
      "first_name": "John",
      "last_name": "Doe",
      "businessName": null,
      "role": null
    },
    "receiver_wallet_details": {
      "address": null,
      "network": null,
      "currency": "ZAR",
      "country": null,
      "bank_code": "123456",
      "bank_name": "ABSA Bank",
      "account_number": "9876543210"
    },
    "created_at": "2025-05-11T10:40:00Z",
    "updated_at": "2025-05-11T10:50:00Z"
  }
}
Important Fields:
  • `status`: Always CANCELLED for this event
  • `reference_id`: Use for tracking

Webhook Payload Structure

All webhook payloads follow this structure:
{
  "id": "evt_<unique-id>",
  "timestamp": 1715420400000,
  "event_type": "<event-type>",
  "signature": "t=<timestamp>,s=<sig>",
  "data": {
    // Event-specific transaction data goes here
  }
}
Fields:
  • `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:
  1. Extract `t` (timestamp) and `s` (signature) from the signature header
  2. Create message: timestamp + ”.” + stringified_payload
  3. Generate HMAC-SHA256 with your signing secret
  4. Compare computed signature with received signature
Security Tips:
  • 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:
FieldTypeDescription
`id`stringUnique transaction identifier
`user_id`stringUser who initiated the transaction
`transaction_type`stringDEPOSIT, WITHDRAWAL, PAYOUT
`amount`stringTransaction amount (decimal)
`currency`stringBase currency (USDC, ZAR, NGN, etc.)
`status`stringCurrent transaction status
`description`stringHuman-readable description
`created_at`ISO 8601Timestamp when created
`updated_at`ISO 8601Timestamp of last update

Retry Policy

StableStack retries failed webhook deliveries with exponential backoff:
AttemptWait Time
1stImmediate
2nd1 minute
3rd5 minutes
4th30 minutes
5th4 hours
Retry Conditions:
  • ✅ Retried: 5xx errors, timeouts, network errors, 408, 429
  • ❌ Not retried: 4xx errors (except 408, 429)
Best Practices:
  1. Respond with 2xx status immediately
  2. Process webhook asynchronously
  3. Use event `id` for idempotency
  4. 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
See Webhook Security for testing signature verification.