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

# Consuming webhooks

> Receive, verify, and process Pocketsflow webhooks reliably — idempotency, reconciliation, and delivery guarantees.

Consuming webhooks correctly keeps your systems in sync with Pocketsflow without
losing events or doing work twice. These patterns apply regardless of your
stack.

## Core responsibilities

Your endpoint should:

1. **Receive** the HTTP `POST` from Pocketsflow.
2. **Verify** the `X-Pocketsflow-Signature` against the raw body (see
   [Authentication & security](/api-webhooks/authentication-and-security)).
3. **Acknowledge** with a `2xx` immediately.
4. **Parse** the JSON and route on the `X-Pocketsflow-Event` header.
5. **Process** the event idempotently in the background.

## Delivery guarantees

<Note>
  Pocketsflow makes a **single delivery attempt** per event with a **5-second
  timeout**. There is no automatic retry of the outbound call — so if your
  endpoint is slow, down, or returns a non-`2xx`, that delivery is recorded as
  failed and not re-sent. Design for at-most-once delivery: reconcile via the
  API and re-fire test events manually when needed.
</Note>

Because of this, two habits matter most:

* **Acknowledge fast.** Verify the signature, enqueue the event, and return
  `200` — all well under 5 seconds. Never do slow work (emails, third-party
  calls, heavy DB writes) before responding.
* **Reconcile.** Periodically pull the source of truth from the API so a missed
  delivery self-heals:
  * Orders → [`GET /orders`](/api-reference/introduction#orders)
  * Payments (one-time + subscription) → [`GET /payments`](/api-reference/introduction#payments)
  * Subscribers → [`GET /subscriptions/subscribers`](/api-reference/introduction#subscriptions-and-subscribers)

## Idempotency

Handlers must be safe to run more than once for the same logical event. Pick a
stable idempotency key from the payload and record it before you act:

| Event(s)                                                                          | Suggested idempotency key                                    |
| --------------------------------------------------------------------------------- | ------------------------------------------------------------ |
| `order.completed`, `order.refunded`                                               | `order.id` (and `order.paymentIntentId`)                     |
| `payment_intent.succeeded`, `invoice.payment_succeeded`, `invoice.payment_failed` | `paymentId` (`pay_…`)                                        |
| `customer.subscription.created`, `customer.subscription.deleted`                  | `subscription.customerId` (each fires once per subscription) |
| `customer.created`                                                                | `customer.id`                                                |

Before processing, check whether you've already handled that key; if so, skip. If
not, record it and proceed. This protects you from re-processing during
reconciliation and from any duplicate delivery.

<Warning>
  **Do not dedupe `customer.subscription.updated` on the customer id.** It is a
  *state-change* signal and fires repeatedly over a subscription's life — past
  due, recovery, cancel-scheduled, pause, resume, cancellation. Keying it on
  `subscription.customerId` alone would make you drop every change after the
  first. Treat it as a state sync instead: **upsert** from the payload's
  `subscriptionCustomer.status` / `cancelAtPeriodEnd` / `paused` fields, which
  are safe to apply more than once. The same applies to `invoice.upcoming` — key
  it on the billing period (`renewalAt`), not the customer.
</Warning>

## Ordering

Events are not guaranteed to arrive in the order they occurred. Don't assume, for
example, that `payment_intent.succeeded` always lands before the first
`invoice.payment_succeeded`. Make each handler tolerant of out-of-order arrival
(upsert state; use the payload's own fields and the API rather than relying on
sequence).

## Error handling

Plan for invalid payloads, downstream outages, and transient connectivity
issues:

* Log every delivery with enough context to debug (event type, idempotency key,
  timestamp, and `webhookId`).
* Return `2xx` only after you've safely **recorded** the event for processing —
  not after the full downstream work completes.
* Alert when your handler's error rate crosses a threshold.

## Recommended flow

<Steps>
  <Step title="Read the raw body">
    Capture raw bytes before parsing so signature verification is exact.
  </Step>

  <Step title="Verify the signature">
    Recompute the HMAC-SHA256 and compare in constant time. Reject on mismatch.
  </Step>

  <Step title="Enqueue and acknowledge">
    Persist the raw event (keyed by its idempotency key) and return `200`.
  </Step>

  <Step title="Process asynchronously">
    A worker parses `event.type`, skips already-processed keys, performs the
    action, and marks the key done.
  </Step>
</Steps>

For concrete language examples, see [Webhook examples](/api-webhooks/examples).

## Related topics

* [Authentication & security](/api-webhooks/authentication-and-security)
* [Webhook events](/api-webhooks/events)
* [Webhook examples](/api-webhooks/examples)
* [API reference](/api-reference/introduction)
