Skip to main content
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).
  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

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

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

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

Read the raw body

Capture raw bytes before parsing so signature verification is exact.
2

Verify the signature

Recompute the HMAC-SHA256 and compare in constant time. Reject on mismatch.
3

Enqueue and acknowledge

Persist the raw event (keyed by its idempotency key) and return 200.
4

Process asynchronously

A worker parses event.type, skips already-processed keys, performs the action, and marks the key done.
For concrete language examples, see Webhook examples.