> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pocketsflow.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication & security

> Verify that Pocketsflow webhook requests are genuine and keep your endpoint secure.

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`](/api-reference/introduction#webhooks):

```json theme={null}
{
  "id": "6a43ca9f7e229e3568f9c9bb",
  "url": "https://acme.com/webhooks/pocketsflow",
  "events": ["order.completed", "invoice.payment_succeeded"],
  "description": "Production endpoint",
  "secret": "431b749a23715a17f15a0ecd60c0978403f7b3c7c85a8b94"
}
```

<Warning>
  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.
</Warning>

## 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.

| Header                    | Value                                           |
| ------------------------- | ----------------------------------------------- |
| `X-Pocketsflow-Signature` | Hex HMAC-SHA256 of the raw request body.        |
| `X-Pocketsflow-Event`     | The event type (for example `order.completed`). |
| `X-Pocketsflow-Timestamp` | Unix epoch milliseconds the event was sent.     |

<Note>
  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.
</Note>

## Verifying the signature

<Steps>
  <Step title="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.
  </Step>

  <Step title="Recompute the HMAC">
    Compute `HMAC-SHA256(secret, rawBody)` and hex-encode it, using the signing
    secret for that endpoint.
  </Step>

  <Step title="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.
  </Step>

  <Step title="Only then parse and process">
    Parse the JSON, route on `X-Pocketsflow-Event`, and process idempotently.
  </Step>
</Steps>

```javascript Node.js theme={null}
import crypto from "crypto";

function verifyWebhook(rawBody, signatureHeader, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody) // exact bytes, not the re-serialized object
    .digest("hex");

  const a = Buffer.from(signatureHeader || "", "utf8");
  const b = Buffer.from(expected, "utf8");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}
```

<Warning>
  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.
</Warning>

## Freshness (optional but recommended)

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.

## Related topics

* [Webhook events](/api-webhooks/events)
* [Consuming webhooks](/api-webhooks/consuming-webhooks)
* [Webhook examples](/api-webhooks/examples)
* [Webhooks & API overview](/api-webhooks/overview)
