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 when attempting to use the webhook API.
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.
Payment Happens
Buyer pays for any of your resources
Webhook Fires
Signed POST request to your URL
You Act
Fulfill order, grant access, log it
Setup via Dashboard
- Go to Dashboard → Webhooks
- Click Create Webhook
- Enter your HTTPS endpoint URL
- 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:
{
"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
Every delivery includes two headers for HMAC verification:
X-X402-Signature— HMAC-SHA256 hex digest of the raw bodyX-X402-Timestamp— Unix timestamp of when the event was sent
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)
);
}import hmac, hashlib
def verify_webhook(raw_body: bytes, signature: str, timestamp: str, secret: str) -> bool:
payload = f"{timestamp}.{raw_body.decode()}"
expected = hmac.new(
secret.encode(), payload.encode(), hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)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 signatures
Never trust unverified payloads. Use the HMAC check shown above.
Respond quickly (< 5s)
Process asynchronously and return 200 immediately. Queue heavy work.
Handle duplicates
Use the event id for idempotency — you may receive the same event more than once.
Rotate secrets periodically
Use Dashboard → Webhooks → Rotate Secret. Update your server before discarding the old one.
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 |