Documentation

Webhook history and replay

Every delivery kept and searchable; replay it through its skill, on another machine or skill, or send it anywhere from the CLI.

What is recorded

Every request to a skill's URL, the machine's own or its hosted URL, becomes a delivery on the machine and reaches the cloud on its next sync: the skill, when it arrived, the outcome, the HTTP status the sender got, the code and the reason when it did not run, the sender's own delivery id, the address, the method, the path, the user agent, the content type and size, the request headers (signatures and credentials are replaced on the machine before anything is uploaded) and the job it started. The record stays as long as the organisation does; nothing prunes it.

OutcomeWhat happened
acceptedThe signature checked out and the filters let it through: a job started, and the record names it.
duplicateThe sender's delivery id had been seen in the last 24 hours; the sender got the earlier job's id and nothing ran.
in_flightAn identical payload for the same skill was still queued or running; the sender got that job's id and nothing ran.
skippedA when-filter of the skill did not match; the reason says which.
rejectedThe signature, the authentication or the request itself failed; the HTTP status, the code and the reason say what.
challengeA Slack url_verification request, answered at once; nothing ran.
errorThe machine could not handle the request; the reason carries the error.

The body is uploaded with the record while the organisation keeps webhook bodies (Settings → keep webhook bodies, or update_settings {store_payloads}), the machine's cloud.upload_payloads is on and the body is at most 262,144 bytes. The cloud keeps it for the plan's retention window (Plans and limits), then deletes the body and keeps the record. A replay on the delivery's own machine reads the machine's own delivery log, not the cloud's copy; a replay on another machine or through another skill, and sending a stored webhook anywhere, need the copy.

PlanWebhook bodies kept
Free7 days
Pro30 days
Business90 days
Enterprise365 days

Replay

replay_delivery {delivery} has the machine run the stored request again through the skill as it is now: a new job with trigger replay, linked from the delivery. A delivery that was rejected or ended in error needs force, which skips the checks it failed (the signature, the authentication); skip_filters ignores the skill's when filters, for a delivery that was skipped. On the delivery's page these are the Replay, Replay anyway and Replay, skip filters buttons. The machine must accept the command: control mode, or delivery.replay in its cloud.allow_commands; a fleet:run key or the member role on this side.

On another machine or through another skill

With machine or skill, replay_delivery takes the stored body and the redacted headers and runs them on that target as a fresh run, exactly as run_skill does: no signature check, no when filters, no deduplication, so force and skip_filters do not apply. A webhook that hit production runs on the staging machine; an event that the old skill mishandled runs through the rewritten one. The target machine must accept skill.run (control mode, or its allow list). This needs the cloud's copy of the whole body: without one the answer is 409 no_body, and the replay where it arrived still works. The audit log's entry for the run names the delivery it replays.

On the delivery's page, Replay elsewhere is a machine, one of the skills it last reported, and a button.

Send a stored webhook anywhere from the CLI

For a URL that is not a paired machine (a staging deployment of your own service, a skillhook that is not connected, a colleague's laptop), take the body out with get_delivery and send it yourself. skillhook send signs it the way the sender would, with the skill's secret as it is on the machine you run it on, and POSTs it to <url>/hooks/<skill>; the target has to have the skill with the same secret (or auth: none). Any other endpoint takes the body with curl. --header "Name: value" adds a header the skill's filters or the agent read (the originals are in the delivery's headers); a body stored as base64 is binary (| base64 -d), and one marked truncated is not the whole request.

Test a SKILL.md with a real webhook before saving it

test_skill {machine, skill_md, payload} runs a SKILL.md once on a machine without installing it (a job with trigger test). With a stored body as the payload, a rewritten skill is tried against the event that broke the old one, and save_skill installs it once it does what you want. On the dashboard, the Playground does the same with a text box.

The routes and the tools

Everything above is the same catalogue on the hosted MCP server, over REST and in the terminal (API reference); the resource routes below name the same things.

ToolScopeWhat
list_deliveriesfleet:readList deliveries
get_deliveryfleet:readGet a delivery
replay_deliveryfleet:runReplay a delivery
run_skillfleet:runRun a skill
test_skillfleet:runTest a SKILL.md
RouteWhat
GET/api/v1/deliveriesWebhooks the machines received, newest first, rejected ones included; q searches the sender's delivery id, the machine's id, the reason, code, path, address and user agent; since and until bound received_at; delivery_id and job match exactly. next_before pages.
GET/api/v1/deliveries/{delivery}One delivery with its redacted headers; ?include=body adds the stored body when the organisation keeps bodies.
POST/api/v1/deliveries/{delivery}/replayRun a stored delivery again. force skips the checks it failed; skip_filters ignores the skill's when-filters. With machine or skill, the stored body and redacted headers run on that target as a fresh run (no signature check, no filters); 409 no_body when the body is not stored.
POST/api/v1/machines/{machine}/skills/{skill}/runRun an installed skill as if a webhook arrived with this payload (no signature check, no filters). wait_seconds waits for the job to finish or to ask a person.