> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stablestack.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Events

> Webhook event types and payload structures for StableStack transactions

## 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:**

```json theme={null}
{
  "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:**

```json theme={null}
{
  "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:**

```json theme={null}
{
  "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:**

```json theme={null}
{
  "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:**

```json theme={null}
{
  "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:**

```json theme={null}
{
  "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:**

```json theme={null}
{
  "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:

```json theme={null}
{
  "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:

| 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    |

**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](https://dashboard.stablestack.com/) to:

* Send test events
* View delivery history
* Check response logs
* Verify signature validation

See [Webhook Security](/webhooks/security) for testing signature verification.
