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
Studio seller webhooks are HMAC-signed. Current deliveries include four important headers:
X-X402-Signatureโ HMAC-SHA256 hex digest of the raw bodyX-X402-Timestampโ Unix timestamp of when the event was sentX-X402-Eventโ event type for quick routing / loggingX-X402-Event-Idโ stable event id for idempotency / replay detection
Verify HMAC-SHA256(timestamp + "." + rawBody) with the webhook signing_secret. Use the raw request body bytes; do not parse and re-serialize JSON before hashing.
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)Backward Compatibility
Some older receivers still accept raw-secret headers like x-x402layer-secret, x-x402-secret, or 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
Use the webhook signing_secret and the signed HMAC headers.
2) Your receiver โ your app settlement route
Use 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. Verify the HMAC using timestamp + raw body before any fulfillment logic.
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 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
For debugging, log the presence of signature/timestamp/event/event-id headers. Do not log the secret itself or full sensitive payloads unless absolutely necessary.
Know the common failure mode
If Studio deliveries 401 but a manual POST with the stored secret works, your 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 |