FurniOpsDocs
CLI

furniops listen

Receive real FurniOps events on your laptop, signed exactly as production deliveries are, without a tunnel.

FurniOps delivers webhooks only to public HTTPS addresses, so a handler on localhost cannot receive them directly. furniops listen turns that around: it holds the event stream open from your machine and POSTs each event to your local handler, signed with the Standard Webhooks v1 scheme exactly as FurniOps signs a real delivery. A handler that verifies these will verify production deliveries unchanged.

furniops listen --forward-to localhost:3000/webhooks
Ready. Forwarding events to http://localhost:3000/webhooks. Your webhook signing secret is whsec_… (^C to quit)
10:00:02  offer.changed evt_034XqBaL2mRLTZPXyRbaUi  -> POST http://localhost:3000/webhooks [204] 12 ms
10:00:09  product.updated evt_034XqBaL2mRLTZPXyRbaUj  -> POST http://localhost:3000/webhooks [500] 3 ms

The local secret

listen signs with a secret of its own, made the first time and kept in the profile, so it stays the same across runs and you set it once. It is not any endpoint's secret: it never leaves your machine. Print it on its own to configure your handler:

export FURNIOPS_WEBHOOK_SECRET=$(furniops listen --print-secret)

Production endpoints have their own secrets, returned when you create them; switch the variable when you deploy.

A full loop

Run your handler

Any handler that verifies with the SDK, such as the one in Your first webhook:

FURNIOPS_WEBHOOK_SECRET=$(furniops listen --print-secret) node server.ts

Start listening

furniops login --with-api-key    # a fo_test_ key, once
furniops listen --forward-to localhost:3000/webhooks --events offer.changed,offer.created

Make something happen

furniops trigger offer.changed

The sample event goes onto your sandbox's stream; listen forwards it and prints your handler's answer.

What each delivery carries

HeaderValue
webhook-idThe event ID.
webhook-timestampWhen listen sent it, in Unix seconds.
webhook-signaturev1, and the HMAC-SHA256 signature with the local secret.
FurniOps-VersionThe API version the stream answered in.
Content-Typeapplication/json

The body is the event envelope exactly as the stream delivered it.

How it differs from production

listen is for development. It forwards what the stream carries, and delivers each event once:

Production deliveryfurniops listen
Signature, headers, bodyStandard Webhooks v1The same, with the local secret
A failed answerRetried for about three daysPrinted, not retried
RedirectsNever followedNever followed
Timeout15 s30 s
FiltersThe endpoint's types, sources and categories--events only
CoalescingThe endpoint's coalesce_secondsNone
When listen is not runningn/aEvents are not forwarded

A handler that answers in time and survives a redelivery behaves the same in both.

Options

FlagMeaning
-f, --forward-to <url>Where to POST each event. localhost:3000/webhooks gets http://.
--events <types>Comma-separated event types to forward. Default: all.
--payload <kind>thin (default) or fat: fat events carry the object as of the event's version.
--print-secretPrint the local signing secret and exit.
--jsonOne JSON line for the ready message (with forward_to and secret), then one per delivery with event_id, event_type, url, status, duration_ms and any error.

listen runs until Ctrl+C. When the stream drops, it reconnects and resumes after the last event it forwarded.

Live or test

listen forwards the events of the mode it acts in: a test key's sandbox, a live key's live data, or, for an account session, live data unless you pass --mode test. Sample events from furniops trigger exist only in test mode, so develop with a test key or --mode test.

On this page