Skip to main content
Webhooks let Pocketsflow talk directly to your systems, so every request must be authenticated before you act on it. Pocketsflow signs each delivery with an HMAC-SHA256 signature you can verify with a shared secret.

Signing secret

Each webhook endpoint has its own signing secret (a 48-character hex string). You receive it once, in the response to POST /webhooks:
The secret is only returned when the endpoint is created. GET /webhooks never returns secrets. Store it immediately in a secret manager or environment variable. If you lose it, rotate by creating a new endpoint.

How Pocketsflow signs a request

For each delivery, Pocketsflow:
  1. Builds the JSON body (your event data with a webhookId field merged in).
  2. Computes HMAC-SHA256(secret, rawBody) over the exact bytes of that body.
  3. Sends the hex digest in the X-Pocketsflow-Signature header.
The timestamp is sent for your logging and freshness checks but is not included in the signed content — the signature covers the raw body only (which already contains webhookId). Do not add the timestamp to the string you hash.

Verifying the signature

1

Read the raw body

Capture the request body as raw bytes before any JSON parsing or middleware re-serializes it. In Express, use express.raw({ type: "application/json" }) for the webhook route.
2

Recompute the HMAC

Compute HMAC-SHA256(secret, rawBody) and hex-encode it, using the signing secret for that endpoint.
3

Compare in constant time

Compare your digest to X-Pocketsflow-Signature with a timing-safe comparison (crypto.timingSafeEqual, hmac.compare_digest, hash_equals). Reject with 400 if they differ.
4

Only then parse and process

Parse the JSON, route on X-Pocketsflow-Event, and process idempotently.
Node.js
Because webhookId is part of the signed body, verifying against a re-serialized copy of the parsed JSON can change key order or whitespace and break the signature. Always hash the raw request bytes exactly as received.
Read X-Pocketsflow-Timestamp and reject deliveries whose timestamp is outside a reasonable window (for example a few minutes) to blunt replay attempts. Since the timestamp isn’t signed, treat it as a secondary defense layered on top of signature verification, not a replacement for it.

Additional defenses

  • Serve your endpoint over HTTPS with a valid certificate (Pocketsflow only delivers to https:// URLs).
  • Validate Content-Type: application/json and the payload shape before acting.
  • Respond fast — deliveries time out after 5 seconds. Acknowledge with a 2xx and offload work to a queue.
  • Restrict inbound access with a firewall/WAF if feasible, and never expose sensitive internal services directly.

Handling secrets safely

  • Store secrets in environment variables or a secret manager — never in source control or client-side code.
  • Use a distinct endpoint (and therefore secret) per environment; keep test-mode and live-mode endpoints separate.
  • Rotate a secret (by recreating the endpoint) if you suspect exposure or change infrastructure ownership.