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

# Webhooks

> Receive real-time payment notifications via webhooks

# Webhooks

Webhooks let you receive real-time `payment.succeeded` notifications whenever a buyer pays for any of your resources — endpoints, products, components, or agent endpoints.

> **Plan Requirement:** Webhooks are available on **Pro and Business** plans only. Free plan accounts receive a `403` response.

## How It Works

Webhooks are **account-level** (seller-level), not per-resource. You register one webhook URL and it receives events for **all** resources you own.

1. 💰 **Payment Happens** — Buyer pays for any of your resources
2. 📡 **Webhook Fires** — Signed POST request to your URL
3. ✅ **You Act** — Fulfill order, grant access, log it

## Setup via Dashboard

1. Go to **Dashboard → Webhooks**
2. Click **Create Webhook**
3. Enter your HTTPS endpoint URL
4. A signing secret is generated automatically — copy and store it securely

From the Webhooks dashboard you can also **toggle** webhooks on/off, **rotate** the signing secret, **delete** webhooks, and view **recent delivery logs**.

## Event: `payment.succeeded`

Every webhook delivery is a `POST` request with this JSON body:

```json theme={null}
{
  "id": "evt_abc123",
  "event": "payment.succeeded",
  "timestamp": "2026-02-26T19:30:00Z",
  "data": {
    "source": "endpoint",
    "source_id": "ep_xyz789",
    "source_slug": "my-api",
    "amount": "1.00",
    "currency": "USDC",
    "tx_hash": "0xabc...def",
    "payer_wallet": "0x1234...5678",
    "network": "base",
    "status": "settled"
  }
}
```

| Field                 | Description                                |
| --------------------- | ------------------------------------------ |
| `source`              | Resource type: `endpoint`, `product`       |
| `source_slug`         | The slug of the resource that was paid for |
| `amount` / `currency` | Payment amount and asset (e.g. `USDC`)     |
| `tx_hash`             | On-chain transaction hash                  |
| `network`             | `base` or `solana`                         |

## Verifying Signatures

Studio seller webhooks are HMAC-signed. Current deliveries include:

* `X-X402-Signature` — HMAC-SHA256 hex digest
* `X-X402-Timestamp` — Unix timestamp for the delivery
* `X-X402-Event` — event type for routing/logging
* `X-X402-Event-Id` — stable event id for idempotency/replay protection

Verify:

* payload: `timestamp.rawBody`
* algorithm: `HMAC-SHA256`
* secret: webhook `signing_secret`

Use the raw request body bytes. Do not parse and re-serialize JSON before hashing.

### Node.js

```javascript theme={null}
const crypto = require('crypto');

function verifyWebhook(rawBody, signature, timestamp, secret) {
  const payload = `${timestamp}.${rawBody}`;
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}
```

### Python

```python theme={null}
import hmac, hashlib

def verify_webhook(raw_body: bytes, signature: str, timestamp: str, secret: str) -> bool:
    payload = timestamp.encode() + b"." + raw_body
    expected = hmac.new(
        secret.encode(), payload, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)
```

## Backward Compatibility

Some older receivers still accept raw-secret headers like:

* `x-x402layer-secret`
* `x-x402-secret`
* `Authorization: Bearer <secret>`

Keep that only as fallback for legacy clients. Current Studio deliveries should be verified with the signed HMAC headers first.

## Two Secret Hops

### 1) Studio → your webhook receiver

* secret: webhook `signing_secret`
* auth model: HMAC verification using the signed headers above

### 2) Your receiver → your app settlement route

* secret: your own app-internal shared secret
* example: a worker sends `x-x402-secret` and the app verifies it with `X402_WEBHOOK_SECRET`

Do not confuse the Studio webhook signing secret with the app settlement secret. They solve different trust boundaries.

## Coverage Matrix

| Source                     | Route              | Status |
| -------------------------- | ------------------ | ------ |
| Endpoint payments          | `/e/:slug`         | ✅ Live |
| Product payments           | `/p/:slug`         | ✅ Live |
| Components (via endpoints) | —                  | ✅ Live |
| Agent endpoint create      | `/agent/endpoints` | ✅ Live |
| Agent endpoint top-up      | `/agent/endpoints` | ✅ Live |

## Best Practices

* 🔐 **Always verify signed headers first** — Never trust unverified payloads.
* ⚡ **Respond quickly (\< 5s)** — Process asynchronously and return `200` immediately. Queue heavy work.
* 🔄 **Handle duplicates** — Use the event `id` for idempotency.
* 🔑 **Rotate secrets carefully** — If deliveries suddenly start returning `401`, first check whether the webhook signing secret rotated or drifted. Update the receiver before discarding the old value.
* 🧾 **Log headers, not secrets** — Log signature/timestamp/event/event-id presence for debugging, but never log the secret itself.
* 🛠️ **Know the common failure mode** — If Studio deliveries `401` but a manual POST with the stored secret works, the receiver likely only supports legacy raw-secret auth while Studio is sending signed webhooks.

## Webhook API Reference

| Method   | Endpoint            | Description                   |
| -------- | ------------------- | ----------------------------- |
| `GET`    | `/api/webhooks`     | List your webhooks            |
| `POST`   | `/api/webhooks`     | Create a new webhook          |
| `PATCH`  | `/api/webhooks/:id` | Toggle active / rotate secret |
| `DELETE` | `/api/webhooks/:id` | Delete a webhook              |
