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

  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:

Webhook Payloadjson
{
  "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"
  }
}
FieldDescription
sourceResource type: endpoint, product
source_slugThe slug of the resource that was paid for
amount / currencyPayment amount and asset (e.g. USDC)
tx_hashOn-chain transaction hash
networkbase or solana

Verifying Signatures

Every delivery includes two headers for HMAC verification:

  • X-X402-Signature — HMAC-SHA256 hex digest of the raw body
  • X-X402-Timestamp — Unix timestamp of when the event was sent
Node.js — Verify Webhook Signaturejavascript
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 — Verify Webhook Signaturepython
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

SourceRouteStatus
Endpoint payments/e/:slugLive
Product payments/p/:slugLive
Components (via endpoints)Live
Agent endpoint create/agent/endpointsLive
Agent endpoint top-up/agent/endpointsLive

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

MethodEndpointDescription
GET/api/webhooksList your webhooks
POST/api/webhooksCreate a new webhook
PATCH/api/webhooks/:idToggle active / rotate secret
DELETE/api/webhooks/:idDelete a webhook