Skip to main content
Webhooks push events to your server as they happen in Fluid — an order is placed, a cart changes, a member is created — so you never have to poll for updates. You register an HTTPS endpoint once, and Fluid delivers a JSON payload to it every time a matching event fires. This guide covers registering a webhook, managing it, verifying and handling deliveries, and inspecting the most recent delivered event for a resource.

Base URL and authentication

Webhook management lives on the company surface. Call it against https://api.fluid.app and send a company Bearer token on every request as Authorization: Bearer <your_token>. Unlike the versioned storefront and checkout surfaces, the webhook paths are unversioned: subscriptions sit under /api/company/webhooks and the payload schemas under /api/webhooks/schemas.
Every operation in this guide requires a company Bearer token. In production your delivery url must be HTTPS, and Fluid does not deliver to internal or private (non-public) hosts, so your endpoint must be publicly reachable.

Register a webhook

Create a subscription with POST /api/company/webhooks. A subscription needs three things: the resource to watch, the event on that resource, and the url Fluid delivers to. You can also set active, the http_method Fluid uses to deliver, and an auth_token — a legacy shared secret for verifying deliveries. Fluid also generates a separate signing_secret; see Verify a delivery.
A successful create returns 201 with the new subscription under a webhook key; a body that fails validation returns 422. The response echoes the webhook’s integer id, the fields you set, and an event_identifier — the identifier Fluid uses for this event’s payload schema.

What you can subscribe to

Discover what you can subscribe to before you register. GET /api/company/webhooks/resources lists the resources and the events available on each. It is the complete catalog, not a sample: Fluid builds it from the same registry it checks new subscriptions against, so every resource and event pair it lists is one POST /api/company/webhooks accepts. Read it from the API rather than copying a list into your code, because new events appear there as soon as they ship.
resources maps each resource to the events you can subscribe to on it. deprecated_resources names the resources slated for removal: subscribe to the member resources instead of customer and user. The List webhook resources reference shows the full current list. Orders also have an edited event. It fires after an order edit commits, when items or adjustments are added to an order after checkout, and the delivery carries the order as it stands after the edit. Its event identifier is order_edited. A dry-run preview of an edit doesn’t fire it.

Company health changes

Subscribe to resource company_health, event changed, to hear when your company’s health changes. Fluid checks each area of a company’s health every hour, and sends company_health_changed once for each area whose state differs from that area’s previous check, so an hour in which nothing changed sends nothing. A droplet installation can subscribe too when it holds the settings scope, and its own token then reads only the health areas its scopes cover, never mist_apps. The delivery names no area and no state. Its company_health object carries the check’s uuid, the observed_at time it ran and a next_step pointing at GET /api/company/v2026-10/health. Read that with your own token, and compare each area’s state with its previous_state to see what changed.

Mist app error spikes

Subscribe to resource mist_app, event error_spike, to hear the moment one of your Mist apps starts failing. Vercel compares each route’s five-minute error count, 5xx by default, with that route’s last 24 hours, and Fluid sends mist_app_error_spike as soon as Vercel’s alert arrives. A usage spike sends nothing. Only a company subscription can register for it; Mist refuses droplet tokens, so a droplet installation can’t. The delivery carries the app, Vercel’s severity (low, medium, high or critical), the errors Vercel counted and started_at, with a next_step pointing at the app’s logs. It names no route: read the logs with your own token to find what failed.

List, inspect, update, and delete

List your subscriptions with GET /api/company/webhooks, and fetch one by its numeric id with GET /api/company/webhooks/{id}:
Update a subscription with PUT /api/company/webhooks/{id}. The update body accepts url, active, auth_token, and http_method; it does not accept resource or event. To retarget a subscription at a different resource or event, delete it and create a new one.
Delete a subscription with DELETE /api/company/webhooks/{id}. A successful delete returns 200 and echoes the deleted webhook’s id:

What a delivery contains

Fluid posts the event data itself as the JSON request body. There is no outer envelope around it. In the body, the event data is keyed by the resource’s own name — an order delivery carries an order object, a cart delivery a cart object, a member delivery a member object. There is no generic data wrapper. Alongside the resource, the body carries metadata about the event: event_name, schema_version, schema_hash, company_id, resource_name, resource, event, and actor, which describes who made the change. A sandbox delivery also carries "sandbox": true and an X-Fluid-Environment: sandbox header. Accept keys you don’t use rather than rejecting the body.
Read the resource data from the top-level key named for the resource: order, cart, or member. The separate resource_name metadata field is an internal type identifier and may be namespaced, so don’t use it to index the body.

Verify a delivery

Fluid generates a signing_secret for every webhook and signs each delivery with it. The secret itself is never sent on a delivery. Read it from the create, show, or update response; the list response doesn’t include it. A webhook created before signing secrets existed can return null until Fluid issues one. Every delivery carries these headers:
  • X-Fluid-Shop — your shop identifier.
  • X-Fluid-Timestamp — the Unix time in seconds when Fluid signed the request.
  • X-Fluid-Signature-V2 — a hex HMAC-SHA256 of the request, keyed with the webhook’s signing_secret.
Verify the signature: recompute a hex HMAC-SHA256, keyed with your signing_secret, over the string <X-Fluid-Timestamp>.<raw_request_body>, then constant-time compare it to X-Fluid-Signature-V2. Sign over the raw request body exactly as received; if you re-serialize the parsed JSON the bytes can change and the signature will not match.

Legacy auth_token verification

Older integrations verify with the webhook’s auth_token. When the webhook has an auth_token, each delivery also carries these headers, unchanged:
  • X-Fluid-Signature — the same HMAC-SHA256 over <X-Fluid-Timestamp>.<raw_request_body>, keyed with your auth_token.
  • X-Fluid-Token and AUTH_TOKEN — your raw auth_token.
These headers carry the auth_token itself, so anyone who sees one delivery holds the key to forge X-Fluid-Signature. Verify X-Fluid-Signature-V2 instead. If you still rely on auth_token, use at least 32 random characters, and rotate it periodically: send a new auth_token to the update endpoint, then update the value your handler verifies against.

Handle and respond

Acknowledge every delivery quickly with a 2xx status. Parse the body, enqueue the work, and return 200 right away rather than doing slow processing inline:
Fluid makes one delivery attempt. It waits up to 5 seconds to connect and 15 seconds for your response, and stops the whole delivery after 20 seconds. It does not retry a delivery that times out or returns a non-2xx status. Fluid records the response status it got, but a delivery your endpoint missed is not sent again on its own. To resend it, see Find and resend a failed delivery. Deliveries don’t go out during the request that caused the event. Fluid queues each one and sends it from a background worker. Most arrive within seconds, but under load they can lag by minutes, so don’t build anything that assumes a delivery is instant. To resend a failed delivery, use Retrigger in Webhook Logs. Make your handler idempotent. A delivery carries no delivery id in its body or headers, so key on the content: store a hash of the raw body, and skip a body you have already processed.

Find and resend a failed delivery

Fluid keeps a record of each delivery, with the response your endpoint returned. To find the ones your endpoint missed, call GET /api/company/webhook_events. It lists the newest first, and takes these filters:
  • status: the HTTP status code your endpoint returned, such as 500.
  • name: part of the event name, matched without regard to case.
  • start_date and end_date: the dates the deliveries were sent.
To send a recorded event again, call POST /api/company/webhook_events/{id}/retrigger with the event’s id. Fluid sends the recorded payload again to every active webhook registered for that event, not only to the endpoint that missed it, so make your handler idempotent as described above. A resend returns 422 when no active webhook is registered for the event, or when the record the event was about no longer exists. Listing events needs a company admin token with the developer.view permission; resending needs developer.update. See List webhook events for the response schema.

Inspect the most recent delivery

To confirm what Fluid last sent for a resource, call GET /api/company/webhooks/resources/{resource_name}. It returns the most recent delivered event for that resource as Fluid stored it: an envelope around the body your endpoint received.
integer
The stored event’s identifier — a number, not a prefixed string. It is not sent with the delivery.
string
A generated token for this stored event.
string
The event identifier, matching what you registered.
string
An ISO 8601 timestamp for when Fluid recorded the event.
object
The body Fluid delivered: the event data, keyed by the resource’s name, plus metadata about the event.
Pass a resource that your company has actually registered. An unregistered or unknown resource_name returns 404 with a { "message": ..., "status": ... } body, not an empty response.

Introspect payload schemas

Each event publishes a JSON schema for its payload. List the schemas with GET /api/webhooks/schemas, optionally narrowing to one event with the event_identifier query parameter, and fetch a single schema with GET /api/webhooks/schemas/{event_identifier}:
Use these schemas to generate types or validate payloads when you build a handler.

Callbacks you can register

Webhooks tell you what happened. Callbacks let Fluid ask your server and wait for the answer, for example for a cart’s tax or shipping. GET /api/callback/definitions is the complete catalog of callbacks: each definition carries its name, a description, and the request_schema your endpoint receives and the response_schema it must answer with.
Register your endpoint for one with POST /api/callback/registrations, with the definition’s name as definition_name inside the callback_registration object. The List callback definitions reference shows every request and response schema. Company events (/api/company/events), the company calendar surfaced to members, are documented in the API reference. They use the same company Bearer token.

Next steps

  • Browse the Create a webhook endpoint reference for full request and response schemas.
  • Register a subscription, then use the inspect endpoint to confirm your first delivery.
  • To manage webhooks in the admin instead of the API, see Webhooks, Webhook Logs and Callbacks in the Help Center.