Skip to content
Documentation
EN
English简体中文繁體中文
Workspace
GuidesAPI referenceAPI v1
Guide

Receive webhook events

Get signed notifications when purchases, renewals or balances change.

Select an endpoint to edit a request and view its response.

Create a subscription

Create POST/webhooks with a name, your public HTTPS URL, enabled: true and the event_types you need. Send a saved Idempotency-Key. Your key needs write access and webhooks:write.

Store the returned signing secret securely. Use the entire whsec_… value, including the prefix. Save the secret from the creation response for future signature checks.

Event What it tells you
fulfillment.updated A static resource or its fulfillment status changed
renewal.updated A renewal changed status
dynamic.purchase.updated A traffic purchase changed status
refund.completed A refund completed
wallet.low_balance The wallet crossed below your chosen threshold

For low-balance alerts, include wallet_low_balance_threshold_fen as an integer string. Notifications cover changes after subscription creation.

Verify the signature

Preserve the raw request body. Compute HMAC-SHA256 over the timestamp, a period and the exact body bytes, using your signing secret. Compare against X-Webhook-Signature in constant time.

This Node.js helper assumes rawBody is a Buffer and headers is a Headers object:

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

function verifyWebhook(rawBody, headers, secret) {
  const timestamp = headers.get('X-Webhook-Timestamp') ?? '';
  const signature = headers.get('X-Webhook-Signature') ?? '';
  if (!/^\d+$/.test(timestamp)) return false;
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
  if (!/^v1=[0-9a-f]{64}$/.test(signature)) return false;
  const expected = createHmac('sha256', secret)
    .update(timestamp + '.')
    .update(rawBody)
    .digest();
  const actual = Buffer.from(signature.slice(3), 'hex');
  return timingSafeEqual(expected, actual);
}

Parse the JSON only after verification. Re-serializing JSON before verification changes the signed bytes.

Handle duplicates and retries

Use the event's id to avoid processing a notification twice. Events can arrive more than once or out of order. Read the related API resource when you need its latest status.

Return 2xx after safely storing or processing the event. Failed deliveries retry up to eight times per round, with delays starting at 30 seconds and rising to one hour. Read delivery history with GET/webhooks/{id}/deliveries. Retry a failed delivery with POST/webhooks/{id}/deliveries/{delivery}/retry.

Rotate the secret

Use POST/webhooks/{id}/rotate-secret to replace it. An in-flight delivery may still use the previous secret, so allow a short transition in your receiver. Your endpoint must accept requests directly over public HTTPS on port 443.