Live tail & routing decisions
import { Aside } from ‘@astrojs/starlight/components’
Debugging a webhook shouldn’t mean refreshing a page. Hookman can stream a project’s deliveries as they arrive, from the CLI or the dashboard, and explain the routing decision behind each one.
hookman tail
hookman tail --org acme --project paymentsTailing acme/payments — Ctrl+C to stop 14:03:22 POST 200 142ms stripe-test → staging 4f9c2a1e 14:03:25 POST 500 96ms default → main 8d21b07c 14:03:31 ↻ POST 200 118ms stripe-test → staging 4f9c2a1eOne line per delivery: time, method, status, latency, the rule that matched (or default when the active deployment took it), the deployment it went to, and the delivery ID. A ↻ marks a replay. The delivery ID is the same one your handler receives in x-hookman-delivery-id, and a replay or retry keeps the original’s, so you can follow one webhook through its attempts.
tail starts from now: it shows what arrives while it runs, not history (use hookman logs for that). It pairs naturally with hookman listen: run both and watch each webhook reach your machine.
| Flag | Description |
|---|---|
--branch <label> | Only deliveries routed to this deployment |
--rule <name> | Only deliveries matched by this rule |
--status <class> | Status classes, comma-separated, e.g. 4xx,5xx |
--method <list> | HTTP methods, comma-separated |
--show-body | Also print each request body (needs payload storage on) |
--json | One JSON object per line, for piping to jq |
--org, --project and --key work as for the other commands. If the connection drops, tail reconnects on its own and picks up where it left off; Ctrl+C stops it cleanly.
Live in the dashboard
The project’s Logs tab is live by default: new webhooks appear at the top within a couple of seconds, with your filters applied. Click Live to pause (the list stops moving so you can read it) and again to resume. While the browser tab is in the background it stops fetching, and catches up when you return.
Why did it go there?
Open any delivery and the Routing decision section shows how it was routed: which rule matched, and for every rule checked before it, why it didn’t (for example header x-preview equals "pr-42"). If nothing matched, it says the active deployment took it.
Replays, mirrored fan-out copies and webhooks rejected for quota aren’t the result of routing, so they have no decision to show.
How it works
The stream is a long-poll on the API: GET /api/orgs/{org}/projects/{project}/logs/stream. The first call (after=now) returns a cursor; each later call passes the cursor back and the API holds the request open, up to 25 seconds, until newer deliveries exist. The cursor follows the order deliveries were recorded, not their timestamps, so a delivery that’s logged late is still delivered and never skipped. Because logs reach the database through a queue, expect a delay of a few seconds between a webhook arriving and appearing in the stream.