FurniOpsDocs

Webhook endpoints and deliveries

The full reference for webhooks. Endpoints and their modes, event types and filters, thin and fat payloads, burst windows, versions, limits, the delivery log, the retry schedule, automatic disabling, recovery, resending, secrets and the dashboard's controls.

A webhook endpoint (whe_…) is a URL FurniOps pushes events to as they happen, signed and retried. This page is the reference; Your first webhook walks through a first endpoint, and Handle webhooks securely covers verifying them.

Endpoints

Create one with POST /v1/webhook-endpoints (webhooks:write: owners, admins and developers, or a key that holds it). The answer carries the signing secret once: store it.

webhook-endpoint.ts
const endpoint = await furniops.webhookEndpoints.create({
  url: "https://example.com/hooks/furniops",
  description: "Price and stock sync",
  event_types: ["offer.changed", "offer.created", "product.discontinued"],
  payload: "thin",
});
// The signing secret is returned once, on creation (and on rotation). Store it as a secret.
console.log(endpoint.id, endpoint.secret.slice(0, 6));
FieldMeaning
urlWhere events are POSTed. HTTPS in live mode; a test-mode endpoint may use plain HTTP (a local tunnel). It must resolve to a public address, and redirects are not followed.
event_typesThe event types to send, up to 50, or ["*"] for every public type, present and future.
filtersWhat narrows them (below).
payloadthin (default) or fat (below).
coalesce_secondsA burst window, 0 to 300 seconds (below).
api_versionThe API version whose shape the events have. Defaults to the version of the request that creates it. See versions.
descriptionYour note, up to 500 characters.

PATCH /v1/webhook-endpoints/{id} changes any of them, and status; send the ETag you read as If-Match. DELETE stops every delivery and forgets the secrets.

Each mode apart

An endpoint belongs to the mode of the key that created it: a test-mode endpoint receives only test events, a live one only live events. Each mode lists, changes and counts only its own endpoints.

Limits

  • 20 endpoints per organisation in each mode.
  • 50 event types per endpoint (or *).
  • Up to 100 sources and 100 categories in filters.

Filters

FilterNarrows
filters.sourcesEvents about these sources' offers, and their source health events. Product and variant events are not narrowed by source.
filters.categoriesEvents about products filed under these categories or below them, and their variants and offers. Source health events are not narrowed by category.

Omit a filter for everything.

Payloads

  • thin sends what changed: the changed fields, by ID. Read the object back when you need all of it.
  • fat also sends object: the product, variant or offer as of the event's version, so you need no read back. Fat payloads are a plan feature (fat_webhooks); without it, asking for one answers 403 feature_unavailable, and an endpoint that is already fat is sent thin events.

Burst windows

With coalesce_seconds above 0, changes to one product, variant or offer that are still waiting to be sent are combined into one event: each field's first old and last new, under the latest version and event ID. Each delivery waits the window before it is sent. 0, the default, sends every change as it happens. Use a window for a receiver that only needs the latest state of a busy offer.

Deliveries

A delivery is one event for one endpoint. Every delivery and every attempt is kept for 30 days (fewer when your plan keeps fewer delivery days).

GET /v1/webhook-endpoints/{id}/deliveries lists them newest first, filtered by status, event type or one event. GET …/deliveries/{delivery} shows the body exactly as sent and every attempt: when, the status, how long it took, why it failed without an answer (a timeout, a refused address, TLS), and the first 1 KiB of your answer.

statusMeaning
pendingWaiting for its next attempt, or for an earlier event of its subject.
sendingBeing sent.
succeededYour endpoint answered 2xx.
failedNo more attempts: the schedule ran out, your endpoint asked to stop, or it was disabled.

Retries

  • Only a 2xx answer within 15 seconds is a success. Redirects are not followed.
  • A failed attempt is retried: at once, then after 5 s, 5 min, 30 min, 2 h, 5 h, 10 h, 10 h, 14 h, 14 h and 14 h. That is eleven attempts over about 2.9 days.
  • 429, 502 and 504 answers that send Retry-After are retried when it says.
  • A webhook-delivery: abort-message header in your answer stops retrying that one event.
  • Events of one subject (one product, variant or offer) arrive in order. A slow subject never holds up the others, and a slow endpoint never slows anyone else's.

Automatic disabling

FurniOps disables an endpoint that:

  • answers 410 Gone (at once), or
  • has had no successful delivery for 72 hours.

status becomes disabled and status_reason.code says why: gone or failing. A delivery.disabled event goes to your other endpoints that listen for it, so you hear about it. While an endpoint is disabled, no events are stored for it.

Recover after an outage

  1. Fix the receiver.
  2. Set the endpoint's status back to enabled.
  3. Send the failed deliveries of the outage again with POST /v1/webhook-endpoints/{id}/recover and a time range (since, and until, now when omitted). They go oldest first, each with a fresh schedule, and the order per subject is kept.
// Once a broken receiver is fixed: send every failed delivery of the range again, oldest first.
const recovery = await furniops.webhookEndpoints.recoverDeliveries(endpoint.id, {
  since: "2026-10-03T00:00:00Z",
});
console.log(`${recovery.requeued} deliveries queued again`);

Events never stored while the endpoint was disabled are not recovered: read them from the change feed, which keeps 30 days.

Resend one delivery

POST …/deliveries/{delivery}/resend sends a finished delivery (failed or succeeded) again at once, with the same webhook-id, so a receiver that already has it can ignore it. A delivery still pending or being sent is answered as it is. The endpoint must be enabled, or it answers 409.

Test an endpoint

POST /v1/webhook-endpoints/{id}/test sends a webhook.test event now, signed like any other, whatever the endpoint subscribes to. In test mode, POST /v1/test/events emits a sample of a real event type to every test endpoint that wants it. To receive events on your machine, use furniops listen.

Secrets

Every delivery is signed to the Standard Webhooks spec with the endpoint's secret. POST /v1/webhook-endpoints/{id}/rotate-secret makes a new one, shown once; the current one keeps signing beside it for overlap_hours (0 to 72, 24 by default), so deliveries carry both signatures until your receiver has switched. The endpoint lists its secrets by their last characters only. See Rotate a secret without downtime.

In the dashboard

Developers › Webhooks lists the endpoints of the mode you are in, with what each listens to, its last 24 hours of activity, error rate and response time. Opening one shows its details, its signing secrets and its latest deliveries, with these actions:

ActionDoes
Send test eventSends webhook.test.
Rotate secretMakes a new secret; the old one signs for 24 more hours.
Change payloadSwitches between changed fields (thin) and full objects (fat).
Retry failedSends again the failed deliveries of the last 24 hours, 3 days, 7 days or 30 days.
ResendSends one finished delivery again.

Creating, editing and deleting endpoints is done through the API or the SDK.

On this page