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

# Webhooks

> Switch sends JSON **POST** notifications to a URL (**`callback_url`**) you provide when payment status changes or when a wallet receives or sends funds.

## Set webhook URL

### Payments

Add **`callback_url`** to the request body when you call **POST `/offramp/initiate`** to create a payment for stablecoin to local currency conversion. See [Stablecoin to NGN](/guides/stablecoin-to-ngn) for a guide.

### Wallets

Add **`callback_url`** when you call **POST `/wallet/create`**, or update it later on the wallet, so you can get webhook notifications about deposits and withdrawals on your wallet. See [Create stablecoin wallet](/guides/create-wallet).

## How notifications work

Switch sends each notification as:

* **Method:** `POST`
* **Headers:** `Content-Type: application/json`, `x-switch-signature` (HMAC-SHA256 hex digest of the raw body), `x-switch-timestamp` (when the request was sent; use for optional replay windows)
* **Body:** JSON payload (depending on context; see [Payment webhooks](#payment-webhooks) or [Wallet webhooks](#wallet-webhooks))

<Note>
  Your server should acknowledge the request by returning **any 2xx** status
  quickly for successfully received notifications. Switch will retry delivery up
  to 3 times if the server does not acknowledge the request.
</Note>

## Verify webhooks

Always verify that incoming requests comes from Switch before you trust.

### How to verify

1. Read the **body** and the `x-switch-signature` header from the incoming request.
2. Compute the expected webhook signature using your `switch service key` and the `body`.
3. Compare the `computed` signature to the `x-switch-signature` from request header.

<CodeGroup>
  ```javascript Node.js theme={"dark"}
  import crypto from 'crypto';

  function verifySwitchWebhook(webhookBody, webhookSignature, yourServiceKey) {
  const expected = crypto
  .createHmac('sha256', yourServiceKey)
  .update(webhookBody, 'utf8')
  .digest('hex');
  const a = Buffer.from(expected, 'utf8');
  const b = Buffer.from(webhookSignature.trim(), 'utf8');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
  }

  ```

  ```python Python theme={"dark"}
  import hashlib
  import hmac


  def verify_switch_webhook(webhook_body, webhook_signature, your_service_key):
      expected = hmac.new(
          your_service_key.encode('utf-8'),
          webhook_body.encode('utf-8'),
          hashlib.sha256,
      ).hexdigest()
      sig = webhook_signature.strip()
      if len(expected) != len(sig):
          return False
      return hmac.compare_digest(expected, sig)
  ```

  ```php PHP theme={"dark"}
  <?php

  function verifySwitchWebhook(string $webhookBody, string $webhookSignature, string $yourServiceKey): bool
  {
      $expected = hash_hmac('sha256', $webhookBody, $yourServiceKey);
      return hash_equals($expected, trim($webhookSignature));
  }
  ```
</CodeGroup>

<Warning>
  If verification fails, ignore the webhook and do not process it.
</Warning>

## Payment webhooks

You receive these notifications whenever **`callback_url`** is set on your payment initiation request payload.

### Payment status

| Status             | Description                                        |
| ------------------ | -------------------------------------------------- |
| `AWAITING_DEPOSIT` | Waiting for the user to send the stablecoin.       |
| `PROCESSING`       | Stablecoin received is being processed.            |
| `COMPLETED`        | Stablecoin payout to local currency is completed.  |
| `FAILED`           | Stablecoin payout to local currency failed.        |
| `REVERSED`         | Stablecoin payout to local currency was reversed.  |
| `SCHEDULED`        | Payment is delayed due to liquidity constraints.   |
| `BLOCKED`          | Payment is blocked for compliance or risk reasons. |

### Example payload

For a USDC on Base to Nigeria Naira (NGN) in `AWAITING_DEPOSIT` status.

```json theme={"dark"}
{
  "success": true,
  "message": "Offramp initiated successfully",
  "timestamp": "2026-04-23T14:35:04.264Z",
  "data": {
    "status": "AWAITING_DEPOSIT",
    "type": "OFFRAMP",
    "reference": "61f9a35a-e535-4f04-ba50-3058b4c856c4",
    "beneficiary": "69e8e664b9fea9f9e6dc3cbb",
    "rate": 1380.845,
    "developer_fee": {
      "amount": 0,
      "amount_usd": 0,
      "currency": "USDC",
      "network": "BASE"
    },
    "source": {
      "amount": 10,
      "amount_usd": 10,
      "network": "BASE",
      "currency": "USDC"
    },
    "destination": {
      "amount": 13808.45,
      "amount_usd": 10,
      "network": "FIAT",
      "currency": "NGN"
    },
    "deposit": {
      "amount": 10,
      "address": "0x3131b6f6a32751C9d99C1710e357A6C4297d17Bc",
      "asset": "base:usdc",
      "note": [
        "Kindly send the exact amount to the wallet address to complete the transaction.",
        "This wallet address is for one-time use only and has a 30 minutes expiry window."
      ]
    },
    "meta": {},
    "created_at": "2026-04-23T14:35:02.448Z",
    "updated_at": "2026-04-23T14:35:02.448Z"
  }
}
```

### Payload field reference

| Field                  | Type      | Description                                            |
| ---------------------- | --------- | ------------------------------------------------------ |
| `status`               | string    | Current status; see [Payment status](#payment-status). |
| `type`                 | string    | `OFFRAMP` or `ONRAMP`.                                 |
| `reference`            | string    | Unique transaction reference (UUID).                   |
| `source.amount`        | number    | Amount of source to be sent.                           |
| `source.currency`      | string    | Source currency code e.g. `USDC`.                      |
| `source.network`       | string    | Source network e.g. `Base`.                            |
| `destination.amount`   | number    | Amount of destination to be received.                  |
| `destination.currency` | string    | Destination currency code e.g. `NGN`.                  |
| `destination.network`  | string    | Destination network e.g. `Fiat`.                       |
| `deposit.amount`       | number    | Amount to send to the deposit address.                 |
| `deposit.address`      | string    | Wallet address to send the amount to.                  |
| `deposit.asset`        | string    | Asset to deposit e.g. `base:usdc`.                     |
| `deposit.note`         | string\[] | Specific instructions for the deposit                  |
| `meta.session_id`      | string    | Payout bank transfer session ID.                       |
| `meta.explorer_url`    | string    | URL to view the deposit on-chain.                      |
| `meta.hash`            | string    | Hash of the deposit transaction.                       |

<Note>
  **`meta.session_id`** is only present when the bank payment has been initiated
  and is successfully processed.
</Note>

<Note>
  **`meta.explorer_url`** and **`meta.hash`** are only present when the deposit
  has been made on-chain to the provided deposit address.
</Note>

## Wallet webhooks

You receive these notifications whenever **`callback_url`** is set on your wallet (at creation or via update) and the wallet receives or sends funds.

### Example wallet payload

Incoming USDC on Base to the wallet address.

```json theme={"dark"}
{
  "address": "0xe0836E4D35047843dA08D510dFb91E7ecd0c43FC",
  "wallet": "69d8d8f1ad6205a9a0e89302",
  "token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
  "decimals": 6,
  "type": "receive",
  "sender": "0x04e05B9bCed583e4F25F7942684dCfe8Ab2632E1",
  "receiver": "0xe0836E4D35047843dA08D510dFb91E7ecd0c43FC",
  "hash": "0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb",
  "amount": 100000000,
  "asset": "base:usdc"
}
```

### Wallet payload field reference

| Field      | Type   | Description                                                                   |
| ---------- | ------ | ----------------------------------------------------------------------------- |
| `address`  | string | Wallet address involved in the event.                                         |
| `token`    | string | Contract address of the token on the network.                                 |
| `decimals` | number | Token decimals (for interpreting `amount`).                                   |
| `type`     | string | `receive` for deposits to the wallet, `send` for withdrawals from the wallet. |
| `sender`   | string | Address that sent the funds.                                                  |
| `receiver` | string | Address that received the funds.                                              |
| `hash`     | string | On-chain transaction hash.                                                    |
| `amount`   | number | Amount of the token.                                                          |
| `asset`    | string | Switch asset id (e.g. `base:usdc`).                                           |

## Need Help?

<CardGroup cols={2}>
  <Card title="Email" icon="mail" href="mailto:contact@onswitch.xyz">
    Send us a message
  </Card>

  <Card title="Call" icon="calendar" href="https://calendly.com/contact-onswitch/30min">
    Book a call with us
  </Card>
</CardGroup>
