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>.
{
"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.
// 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.
Related reference
- Webhooks API
- Signature generator (for HMAC on outbound API requests — webhook verification uses the same HMAC primitive with the raw body)
