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, and stamps it with a unique event id you can use to drop duplicates and replays.

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 returned when the endpoint is created, by GET /webhooks/{id}, and by POST /webhooks/{id}/rotate-secret. GET /webhooks (the list) never returns secrets. Store it immediately in a secret manager or environment variable.

Rotating the secret

POST /webhooks/{id}/rotate-secret replaces the endpoint’s signing secret and returns the new one once:
The old secret stops working immediately — deliveries from that moment on are signed with the new one. Deploy the new secret to your endpoint right after rotating (or briefly accept both while you roll it out).

How Pocketsflow signs a request

For each delivery, Pocketsflow:
  1. Builds the JSON body once: the envelope fields id, type and created, then your event data, then webhookId.
  2. Signs the exact bytes of that body — two ways, described below.
  3. Sends those same bytes as the request body.
The envelope fields in the body: X-Pocketsflow-Signature-V2 binds a timestamp to the body, so a captured request can’t be replayed later with a fresh timestamp.
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

Parse the header

Split X-Pocketsflow-Signature-V2 on , into t=<seconds> and v1=<hex>.
3

Check the timestamp

Reject the request if t is more than 5 minutes away from your current time.
4

Recompute the HMAC

Compute HMAC-SHA256(secret, t + "." + rawBody) and hex-encode it.
5

Compare in constant time

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

Deduplicate, then process

Skip the event if you’ve already handled its X-Pocketsflow-Event-Id (also in the body as id). Otherwise record the id, parse the JSON, route on type, and process.
Node.js
Python
The signed body includes webhookId and the envelope fields, so 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.

Deduplicating events

Pocketsflow retries failed deliveries (see Consuming webhooks) and you can redeliver an event from the delivery log, so the same event can reach you more than once. Every copy carries the same event id in X-Pocketsflow-Event-Id and the body’s id. Store the ids you’ve processed (keeping them for at least a few days is plenty) and skip any id you’ve already seen. Combined with the 5-minute timestamp tolerance, this also blocks replayed requests.

Legacy signature (V1)

X-Pocketsflow-Signature is still sent on every delivery so existing integrations keep working. It is the hex HMAC-SHA256(secret, rawBody) of the raw body only:
Node.js
V1 does not cover the timestamp, so on its own it can’t stop a captured request from being replayed. If you stay on V1, deduplicate on the event id and treat X-Pocketsflow-Timestamp as a secondary freshness check only. New integrations should verify V2.

Additional defenses

  • Serve your endpoint over HTTPS with a valid certificate (Pocketsflow only delivers to https:// URLs on public addresses; it follows up to 3 307/308 redirects, re-checking each hop the same way, and any other 3xx counts as a failed delivery).
  • 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 with POST /webhooks/{id}/rotate-secret if you suspect exposure or change infrastructure ownership.