Event delivery

Use Webhooks as Your Source of Truth

postMessage events make the browser feel instant. Signed webhooks tell your backend what actually happened, even if the buyer closes the tab.

Create an endpoint

Register an HTTPS endpoint and choose the events your backend can process. Store the returned signing_secret securely; it is shown once.

Create endpoint
curl -X POST https://api.usethrottle.dev/api/v1/webhook-endpoints \
  -H "x-api-key: $THROTTLE_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "url": "https://shop.example.com/api/throttle/webhook",
    "enabled_events": ["order.created", "payment.captured", "payment.failed"]
  }'

Verify signatures

Each delivery includes X-Throttle-Signature. Compute HMAC-SHA256 over <timestamp>.<raw body>.

Node verifier
import { createHmac, timingSafeEqual } from 'node:crypto';

export function verifyThrottleWebhook(rawBody: string, header: string, secret: string): boolean {
  const parts = Object.fromEntries(
    header.split(',').map((part) => {
      const [key, value] = part.split('=');
      return [key, value];
    }),
  );

  if (!parts.t || !parts.v1) return false;

  const expected = createHmac('sha256', secret)
    .update(`${parts.t}.${rawBody}`)
    .digest('hex');

  return timingSafeEqual(Buffer.from(parts.v1, 'hex'), Buffer.from(expected, 'hex'));
}
Use the raw body
Signature checks fail if your framework parses and reserializes JSON before verification. Capture the raw request body first, then parse JSON after the signature passes.

Process idempotently

Webhook delivery is at-least-once. Store each event id before applying side effects so retries do not double-fulfill, double-email, or double-book revenue.

Recommended fields

  • event_id unique identifier from the delivery envelope.
  • event_type for routing and debugging.
  • processed_at timestamp after side effects finish.
  • payload raw JSON for audit and replay.

Common events

  • order.created
  • payment.authorized
  • payment.captured
  • payment.failed
  • payment.refunded
  • cart.created
  • cart.item_added
  • cart.discount_applied
  • cart.checkout_started
  • cart.converted

Retries

Non-2xx responses and network failures are retried with delayed backoff and are visible in webhook delivery logs. Design handlers to return 2xx only after the event is durably stored or safely ignored as a duplicate.