Skip to main content
SnapCount logoSnapCount

Signed outbound webhooks

SnapCount posts a signed JSON event to your HTTPS endpoint every time a counter moves.

Updated

On this page

How delivery works

  1. Someone countsA tap on Gate A: 186 to 187
  2. SnapCount signs itHMAC-SHA256 with your endpoint secret
  3. Your endpoint answersVerify, then reply 2xx within 10 s

Owners and admins of a Pro or Enterprise organization add endpoints in Settings, Developers, and choose which events each one receives. An organization can hold up to 10 endpoints.

  • Endpoints must use HTTPS and cannot point at a private address such as localhost or 10.0.0.0/8.
  • Each endpoint gets its own secret, starting with whsec_. It is shown once, when you create the endpoint.

Event types

Webhook event types
EventSent when
counter.incrementA counter goes up.
counter.decrementA counter goes down.
threshold.alert.firedAn Enterprise capacity alert fires on a counter or a group.
Pro can subscribe to threshold.alert.fired, but the event is only produced by an Enterprise threshold alert.

Payloads

Every event has the same envelope: an id, the type, createdAt, and a data object. Count events carry the counter and the change. The id is built from the event itself, so one count always has the same id, even across retries.

{
  "id": "counter.increment:cm1gatea0001:5812",
  "type": "counter.increment",
  "createdAt": "2026-09-26T18:42:10.000Z",
  "data": {
    "organizationId": "cm1org0001",
    "counterId": "cm1gatea0001",
    "counterName": "Gate A",
    "previousCount": 186,
    "count": 187,
    "delta": 1,
    "lastEventId": 5812
  }
}

counter.decrement has the same shape as an increment, with a negative delta. On a threshold alert, scope is counter or group, and kind is at_capacity, percent, or absolute.

Headers

Delivery request
POST /snapcount HTTP/1.1
Content-Type: application/json
User-Agent: SnapCount-Webhooks/1.0
X-SnapCount-Signature: t=1790448130,v1=5f8c...
X-SnapCount-Timestamp: 1790448130
X-SnapCount-Event: counter.increment
X-SnapCount-Delivery: cm1dlv0042
Webhook delivery headers
HeaderValue
X-SnapCount-Signaturet=<unix seconds>,v1=<hex>, an HMAC-SHA256 of ${timestamp}.${rawBody} keyed with the endpoint secret.
X-SnapCount-TimestampThe same unix timestamp, in seconds.
X-SnapCount-EventThe event type.
X-SnapCount-DeliveryA delivery id that stays the same across retries. Use it to ignore duplicates.

Verify the signature

Check the signature before you trust a delivery. Compute the HMAC over the raw body exactly as it arrived, then compare in constant time.

import { createHmac, timingSafeEqual } from "node:crypto"

// secret: your endpoint's whsec_... value.
// rawBody: the body as received, before JSON parsing.
export function verifySnapCount(rawBody, header, secret) {
  const parts = Object.fromEntries(
    header.split(",").map((part) => part.trim().split("=")),
  )
  const expected = createHmac("sha256", secret)
    .update(`${parts.t}.${rawBody}`)
    .digest("hex")
  const received = Buffer.from(parts.v1 ?? "")
  const wanted = Buffer.from(expected)
  return received.length === wanted.length && timingSafeEqual(received, wanted)
}
Parsing the JSON and serializing it again changes the bytes, and the signature will not match. Read the body as text or bytes first, verify, then parse.

Reject old deliveries

The signed timestamp lets you refuse a replayed request. Compare t with your clock and drop anything older than a few minutes, allowing for the retry schedule below if you store and process deliveries later.

Retries and responses

Reply with any 2xx within 10 seconds to acknowledge a delivery. Any other status, or no answer in time, schedules a retry.

  1. First tryon the count
  2. Retry 1+30 s
  3. Retry 2+2 min
  4. Retry 3+10 min
  5. Retry 4+1 h
  6. Retry 5+6 h
How SnapCount reads your response
Your responseWhat happens
2xxDelivered. No more attempts.
410 GoneStop. SnapCount does not retry this delivery.
Anything else, or a timeoutRetried after 30 s, 2 min, 10 min, 1 h, and 6 h, then marked failed.
Acknowledge first, work later. Put the event on a queue, reply 200, then process it, so a slow job never turns into a retry.

Was this page helpful?

Ready to connect SnapCount?

Create an API key or a webhook endpoint in Settings. The public API and webhooks come with Pro and Enterprise.