Skip to main content

Overview

Instead of polling for order status, you can configure Henry to push events directly to your server via webhooks. When an order transitions state - placed, completed, cancelled - Henry fires an HTTP POST to your endpoint with a signed payload. Webhooks are configured per-cart via the settings.events array on cart.create.

How it works


1. Register a webhook endpoint

Go to the Henry Dashboard, open your app settings, and add a webhook endpoint URL. Henry will generate a webhook UUID and a webhook secret for that endpoint.
  • The UUID is used in cart settings to reference the webhook
  • The secret is used to verify incoming requests (keep it private, like an API key)

2. Configure a cart with webhook events

Pass the webhook UUID in settings.events when creating a cart. Henry fires that webhook when the specified event occurs.
To fire on specific outcomes, use a more granular event type:

3. Receive and verify the webhook

Henry sends a POST request with three important headers: Always verify the signature before processing the payload.

Verification logic

Always read the raw request body before parsing it as JSON. Parsing first can change the string representation and break signature verification.

Full server example (Node.js / Express)

Full server example (Bun)


Webhook payload structure

Henry sends a consistent envelope for all events. Each delivery is a single attempt with a 10-second timeout and is not retried.

Example payload


Event types reference

Events follow a dot-notation hierarchy. Subscribing to a parent event fires for all child events - so order.purchase also receives the legacy full / partial events, and one settled order produces more than one delivery.

Order events

A purchase settles once and fires exactly one of order.purchase.complete (every item purchased), order.purchase.partially_complete (some items purchased) or order.purchase.failed (nothing purchased). After an item fails, the event can wait up to an hour before it fires, so it always reports the final outcome. Until then the order and that item read processing in every event payload, as they do when polled. The full and partial events are kept for existing integrations. While the purchase is pending / processing they say whether every cart item was attempted together. Once it settles they follow the outcome: complete also fires order.purchase.full.complete, partially_complete also fires order.purchase.partial.complete and order.purchase.partial.failed, and failed also fires order.purchase.full.failed. orderId is the same refId that cart.checkout.purchase returns. details.email, details.phone and details.shippingAddress are included only when the cart collects them. items[].recoveryUrl is a link back to the merchant for that item. On order.purchase.cancelled, results carries cancelledAt and reason instead of costs.

Points & tier events

For most use cases, subscribe to order.purchase.complete, order.purchase.partially_complete and order.purchase.failed directly (not a parent like order.purchase) — exactly one of them fires per purchase, once its outcome is final.

Conditionals

Each event in settings.events accepts an optional conditional that filters when the trigger fires. Two types are supported:

Next steps

Polling

Prefer polling for product search and details jobs

Universal Cart

Configure webhook events when creating a cart