CustomersA worked playbook

How to replay, test and resend any webhook a machine ever received

Every webhook a machine received is on record in the cloud, with the reason when it did not run. Filter them, replay one through the skill as it is now, run its body on another machine or skill, send it to any other location from the CLI, and test a SKILL.md before it is saved.

Based on: A worked playbook on the Deliveries page, the catalogue's delivery operations and the skillhook CLI, as this deployment runs them.

On record
Every delivery, with its outcome and reason
Body kept
7 to 365 days, by plan
Replay
As the skill is now; force and skip_filters when needed
DeliveriesReplayCLIMCPPlayground

The situation

Webhooks fail quietly: a secret rotated on one side, a filter that was too narrow, a machine away longer than the sender's retries, a skill with a bug. The sender's own delivery log shows a status code; it does not say why the skill did not run, and it cannot run the delivery again through the skill as it is now. Skillhook Cloud keeps that record for every machine of the organisation, and the operations to act on it, from the dashboard, the API, the MCP server and the CLI.

How it runs

Every webhook request a machine received is reported to the cloud as a delivery: the skill, the outcome, the HTTP status, the code and the reason, whether it came to the machine's own URL or through a hosted one, the method, the sender's address and user agent, the content type and size, when it arrived, and the job it started. The Deliveries page and list_deliveries filter by machine, skill, outcome and route, newest first.

OutcomeWhat it means
acceptedVerified, filtered in, and a job was queued; the record links the job.
duplicateA delivery id skillhook had seen within a day: the sender retried; the record names the original job.
in_flightThe same payload as a job still queued or running: folded into that job.
skippedAuthenticated, but a when-filter did not match; the reason names the condition. The sender got 200 and does not retry.
rejectedFailed authentication or an IP allow-list; the code and the message the sender got are on the record.
challengeSlack's url_verification, answered at once; nothing to run.
errorThe skill was not configured (a missing secret) or the server could not take it; the HTTP status says which.
  • get_delivery {delivery, include_body}: the record, the redacted headers and the query string, and with include_body the stored body (its text, or base64 for a binary body, and whether it was truncated).
  • replay_delivery {delivery, force, skip_filters}: a command the machine runs, from its own record of the request, through the skill as it is now: a new job with trigger replay whose headers carry x-skillhook-replay-of, never de-duplicated. force is needed for a delivery that failed its checks, since its body was never verified; skip_filters ignores the skill's when filters. replay_job {job} does the same from a job's payload.
  • run_skill {machine, skill, payload, headers}: any body on any machine's skill, no signature check, no filters. With the body from get_delivery, that is a replay on another machine or another skill.
  • test_skill {machine, skill_md, payload}: a SKILL.md that is not installed, run once with trigger test, to try a draft before save_skill; the dashboard's Playground does the same.
  • skillhook send <skill> --payload @body.json --url <base> on the machine: the body signed with that skill's secret the way its auth expects, posted to <base>/hooks/<skill>: staging, a colleague's machine, a second environment.

What is kept, and for how long

  • The record of every delivery stays for as long as the organisation exists; nothing prunes it.
  • The body is kept for the organisation's retention window, 30 days by default and never longer than the plan's, below; it is not kept at all when the organisation keeps no webhook bodies (Settings → keep webhook bodies, or the machine's cloud.upload_payloads off). After that, get_delivery answers without a body.
  • A hosted delivery's sealed copy is deleted once the machine has acknowledged it, or after 72 hours when it never did; the record stays, marked expired.
  • On the machine, skillhook's own delivery log keeps the newest 2,000 records and the bodies of refused deliveries up to 64 KiB by default (deliveries.max, deliveries.body_max_bytes), and the payload of every job it ran under jobs.max_jobs. A replay through the cloud runs from that record, so it needs the machine online, in control mode, and the delivery still in its log.
PlanWebhook bodies kept
Free7 days
Pro30 days
Business90 days
Enterprise365 days

Set it up with your agent

Paste this into Claude Code or Codex with the skillhook plugin. It installs skillhook and the GitHub CLI, tests the skill below with test_skill before saving it, turns on the hosted URL, walks you through GitHub's webhook form, and then runs the exercises: a rejected delivery replayed with force, a skipped one with skip_filters, the stored body on another machine, and the same body sent to staging from the machine. Paste the SKILL.md from the next section as your second message.

The skill

A small skill on GitHub's deployment_status event that posts one Slack message per successful production deployment, written to be replayed: it checks the conditions its filters would have checked, keeps its own record of what it posted, and posts nothing twice. Every frontmatter field is one skillhook validates.

Where a person comes in

  • Replaying is a member's action. replay_delivery, replay_job, run_skill and test_skill need a fleet:run key or the member role, and control mode on the machine; a machine in observe mode runs nothing the cloud asks.
  • force is a decision. It runs a body whose signature was never verified. Use it for a delivery you know is genuine, such as one rejected after you rotated the secret, never for one from a sender you do not recognise. The agents' operating guide says the same: fix the cause, then replay.
  • A replayed job is a job like any other. It reports progress, can ask a question in the inbox, and its outcome and links show there; a skill that asks will ask again on a replay.

What to check after

  • Deliveries. The original keeps its outcome and reason; a replay does not change it. Look for the pattern instead: many rejected deliveries from one sender after a date is a rotated secret, many skipped ones a filter to loosen.
  • The new job. Trigger replay (or test, or api for run_skill), linked from the command's result; its outcome tells whether the skill did the work or found it already done.
  • The secret, once. After a rotation, skillhook cloud secret <machine> <skill> shows the new value in your terminal only; the cloud never holds it, so the sender gets it from you.

Deliveries · Hosted webhook URLs · Replaying a job or a delivery