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

# Webhooks

> Configure webhook endpoints for real-time event notifications

Webhooks let you receive real-time HTTP POST notifications when events happen in your AnySpend account. Configure endpoints, choose which events to subscribe to, and monitor delivery history.

<Info>
  All webhook management endpoints require **admin** permission unless noted. Read-only endpoints (list, get, deliveries) require **read** permission.
</Info>

## Authentication

```bash theme={null}
Authorization: Bearer asp_xxx
```

Base URL: `https://platform-api.anyspend.com/api/v1`

## Endpoints

### List Webhooks

<ParamField path="GET /webhooks" type="read">
  Returns all webhook endpoints configured for your account.
</ParamField>

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    curl -X GET https://platform-api.anyspend.com/api/v1/webhooks \
      -H "Authorization: Bearer asp_xxx"
    ```
  </Tab>

  <Tab title="SDK">
    ```typescript theme={null}
    import { AnySpendClient } from "@b3dotfun/sdk/anyspend";

    const client = new AnySpendClient({ apiKey: process.env.ANYSPEND_API_KEY! });
    const webhooks = await client.webhooks.list();
    ```
  </Tab>
</Tabs>

**Response**

```json theme={null}
{
  "success": true,
  "data": [
    {
      "id": "wh_abc123",
      "url": "https://myapp.com/webhooks/anyspend",
      "events": ["payment.completed", "payment.failed"],
      "is_active": true,
      "success_count": 142,
      "failure_count": 3,
      "last_triggered_at": "2026-02-27T10:30:00Z",
      "created_at": "2026-01-15T08:00:00Z",
      "updated_at": "2026-02-27T10:30:00Z"
    }
  ]
}
```

***

### Create Webhook

<ParamField path="POST /webhooks" type="admin">
  Create a new webhook endpoint. The response includes a `secret` field used to verify webhook signatures. Store this securely -- it is only returned once at creation time.
</ParamField>

<ParamField path="url" type="string" required>
  The HTTPS URL that will receive webhook POST requests.
</ParamField>

<ParamField path="events" type="string[]" required>
  Array of event types to subscribe to. See [Supported Events](#supported-events) below.
</ParamField>

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    curl -X POST https://platform-api.anyspend.com/api/v1/webhooks \
      -H "Authorization: Bearer asp_xxx" \
      -H "Content-Type: application/json" \
      -d '{
        "url": "https://myapp.com/webhooks/anyspend",
        "events": ["payment.completed", "payment.failed", "checkout.completed"]
      }'
    ```
  </Tab>

  <Tab title="SDK">
    ```typescript theme={null}
    const webhook = await client.webhooks.create({
      url: "https://myapp.com/webhooks/anyspend",
      events: ["payment.completed", "payment.failed", "checkout.completed"],
    });

    // Store webhook.secret securely -- only returned on create
    console.log("Signing secret:", webhook.secret);
    ```
  </Tab>
</Tabs>

**Response**

```json theme={null}
{
  "success": true,
  "data": {
    "id": "wh_abc123",
    "url": "https://myapp.com/webhooks/anyspend",
    "events": ["payment.completed", "payment.failed", "checkout.completed"],
    "secret": "whsec_a1b2c3d4e5f6...",
    "is_active": true,
    "success_count": 0,
    "failure_count": 0,
    "last_triggered_at": null,
    "created_at": "2026-02-27T12:00:00Z",
    "updated_at": "2026-02-27T12:00:00Z"
  }
}
```

<Warning>
  The `secret` field is only included in the create response. Copy it immediately and store it in your environment variables or secrets manager.
</Warning>

***

### Get Webhook

<ParamField path="GET /webhooks/:id" type="read">
  Retrieve a single webhook endpoint by ID.
</ParamField>

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    curl -X GET https://platform-api.anyspend.com/api/v1/webhooks/wh_abc123 \
      -H "Authorization: Bearer asp_xxx"
    ```
  </Tab>

  <Tab title="SDK">
    ```typescript theme={null}
    const webhook = await client.webhooks.get("wh_abc123");
    ```
  </Tab>
</Tabs>

***

### Update Webhook

<ParamField path="PATCH /webhooks/:id" type="admin">
  Update an existing webhook endpoint. All fields are optional.
</ParamField>

<ParamField path="url" type="string">
  Updated HTTPS endpoint URL.
</ParamField>

<ParamField path="events" type="string[]">
  Updated list of subscribed event types. Replaces the existing list entirely.
</ParamField>

<ParamField path="is_active" type="boolean">
  Set to `false` to pause delivery without deleting the endpoint.
</ParamField>

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    curl -X PATCH https://platform-api.anyspend.com/api/v1/webhooks/wh_abc123 \
      -H "Authorization: Bearer asp_xxx" \
      -H "Content-Type: application/json" \
      -d '{
        "events": ["payment.completed", "checkout.completed"],
        "is_active": false
      }'
    ```
  </Tab>

  <Tab title="SDK">
    ```typescript theme={null}
    const updated = await client.webhooks.update("wh_abc123", {
      events: ["payment.completed", "checkout.completed"],
      is_active: false,
    });
    ```
  </Tab>
</Tabs>

***

### Delete Webhook

<ParamField path="DELETE /webhooks/:id" type="admin">
  Permanently remove a webhook endpoint. Pending deliveries will be cancelled.
</ParamField>

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    curl -X DELETE https://platform-api.anyspend.com/api/v1/webhooks/wh_abc123 \
      -H "Authorization: Bearer asp_xxx"
    ```
  </Tab>

  <Tab title="SDK">
    ```typescript theme={null}
    await client.webhooks.delete("wh_abc123");
    ```
  </Tab>
</Tabs>

***

### Send Test Webhook

<ParamField path="POST /webhooks/:id/test" type="admin">
  Sends a test event to the webhook URL so you can verify your endpoint is receiving and processing events correctly. The test payload uses a `test.ping` event type.
</ParamField>

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    curl -X POST https://platform-api.anyspend.com/api/v1/webhooks/wh_abc123/test \
      -H "Authorization: Bearer asp_xxx"
    ```
  </Tab>

  <Tab title="SDK">
    ```typescript theme={null}
    const result = await client.webhooks.test("wh_abc123");
    // result.delivery_id can be used to check the delivery status
    ```
  </Tab>
</Tabs>

**Response**

```json theme={null}
{
  "success": true,
  "data": {
    "delivery_id": "del_xyz789",
    "status": "sent",
    "response_code": 200
  }
}
```

***

### List Delivery History

<ParamField path="GET /webhooks/:id/deliveries" type="read">
  View the delivery log for a webhook endpoint. Returns recent delivery attempts with status codes and response times.
</ParamField>

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    curl -X GET https://platform-api.anyspend.com/api/v1/webhooks/wh_abc123/deliveries \
      -H "Authorization: Bearer asp_xxx"
    ```
  </Tab>

  <Tab title="SDK">
    ```typescript theme={null}
    const deliveries = await client.webhooks.listDeliveries("wh_abc123");
    ```
  </Tab>
</Tabs>

**Response**

```json theme={null}
{
  "success": true,
  "data": [
    {
      "id": "del_xyz789",
      "event_type": "payment.completed",
      "status": "success",
      "response_code": 200,
      "response_time_ms": 245,
      "attempts": 1,
      "created_at": "2026-02-27T10:30:00Z"
    },
    {
      "id": "del_xyz790",
      "event_type": "payment.failed",
      "status": "failed",
      "response_code": 500,
      "response_time_ms": 3012,
      "attempts": 3,
      "created_at": "2026-02-27T09:15:00Z"
    }
  ]
}
```

***

### Retry a Failed Delivery

<ParamField path="POST /webhooks/:id/deliveries/:did/retry" type="admin">
  Manually retry a failed webhook delivery. The delivery must have a `failed` status.
</ParamField>

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    curl -X POST https://platform-api.anyspend.com/api/v1/webhooks/wh_abc123/deliveries/del_xyz790/retry \
      -H "Authorization: Bearer asp_xxx"
    ```
  </Tab>

  <Tab title="SDK">
    ```typescript theme={null}
    const result = await client.webhooks.retryDelivery("wh_abc123", "del_xyz790");
    ```
  </Tab>
</Tabs>

**Response**

```json theme={null}
{
  "success": true,
  "data": {
    "delivery_id": "del_xyz790",
    "status": "sent",
    "response_code": 200,
    "attempts": 4
  }
}
```

***

## Webhook Object

| Field               | Type             | Description                                   |
| ------------------- | ---------------- | --------------------------------------------- |
| `id`                | `string`         | Unique webhook identifier (e.g., `wh_abc123`) |
| `url`               | `string`         | HTTPS endpoint URL                            |
| `events`            | `string[]`       | Subscribed event types                        |
| `secret`            | `string`         | Signing secret (only returned on create)      |
| `is_active`         | `boolean`        | Whether the webhook is currently active       |
| `success_count`     | `number`         | Total successful deliveries                   |
| `failure_count`     | `number`         | Total failed deliveries                       |
| `last_triggered_at` | `string \| null` | ISO 8601 timestamp of last delivery attempt   |
| `created_at`        | `string`         | ISO 8601 creation timestamp                   |
| `updated_at`        | `string`         | ISO 8601 last update timestamp                |

## Supported Events

| Event Type           | Description                                                                    |
| -------------------- | ------------------------------------------------------------------------------ |
| `payment.completed`  | A payment was successfully processed                                           |
| `payment.failed`     | A payment attempt failed                                                       |
| `checkout.completed` | A checkout session was completed (includes form data, shipping, discount info) |

## Verifying Webhook Signatures

Each webhook request includes a signature in the `X-AnySpend-Signature` header. Verify it using your webhook secret to ensure the request is authentic.

```typescript theme={null}
import crypto from "crypto";

function verifyWebhookSignature(
  payload: string,
  signature: string,
  secret: string
): boolean {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(payload)
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// In your webhook handler
app.post("/webhooks/anyspend", (req, res) => {
  const signature = req.headers["x-anyspend-signature"];
  const isValid = verifyWebhookSignature(
    JSON.stringify(req.body),
    signature,
    process.env.ANYSPEND_WEBHOOK_SECRET
  );

  if (!isValid) {
    return res.status(401).json({ error: "Invalid signature" });
  }

  // Process the event
  const { type, data } = req.body;
  switch (type) {
    case "payment.completed":
      // Handle successful payment
      break;
    case "payment.failed":
      // Handle failed payment
      break;
  }

  res.status(200).json({ received: true });
});
```

<Note>
  Always return a `2xx` status code within 30 seconds. Failed deliveries are retried up to 3 times with exponential backoff.
</Note>
