COUNTCOUNT
Sign Up

Guides

Verify Webhook Deliveries

When you configure a signing secret on a webhook subscription, COUNT signs every delivery so you can confirm it originated from COUNT and was not tampered with.

Step 1 — Create a subscription with a signing secret

POST /partners/webhooks with your callback URL, event type, and a signing secret you generate and store securely.

Store the secret immediately

The signing secret is write-only — it is never returned in list or update responses. Save it when you create the subscription.

Step 2 — Receive the delivery

COUNT sends an HTTPS POST to your callback URL. Read the raw request body as bytes or a string — do not re-serialize parsed JSON before verification, or the signature will not match.

The X-Webhook-Signature header contains sha256=<hex>.

Example delivery payload
{
  "id": "delivery-uuid",
  "event": "customer.created",
  "apiVersion": "1",
  "occurredAt": "2026-03-01T12:00:00.000Z",
  "team": {
    "id": "team-uuid",
    "name": "Acme Workspace"
  },
  "data": {
    "id": "dfa3219e-6af8-4c53-997a-037534f63a35",
    "customer": "Acme Corporation",
    "email": "contact@acme.com"
  }
}

Step 3 — Verify the signature

Compute HMAC-SHA256 of the raw body using your signing secret and compare to the header value using a constant-time comparison.

Node.js verification
// COUNT sends: X-Webhook-Signature: sha256=<hex>
// where hex = HMAC-SHA256(signingSecret, rawRequestBody)

import crypto from 'crypto';

function verifyWebhookSignature(params) {
  const { rawBody, signingSecret, signatureHeader } = params;
  const expectedHex = crypto
    .createHmac('sha256', signingSecret)
    .update(rawBody, 'utf8')
    .digest('hex');
  const expected = `sha256=${expectedHex}`;
  return signatureHeader.trim() === expected;
}

Step 4 — Respond quickly

Return HTTP 2xx within a few seconds. COUNT retries failed deliveries. Process the event asynchronously if your handler needs more time.