Webhook inspector
A webhook endpoint is Cairn's requestbin. It gives you a URL that captures every request sent to it (method, path, query, headers, and body) and shows them as they arrive. Use one to see exactly what a service sends before you connect it to something real, or to find out why a receiver rejected a delivery.
Create an endpoint
The web app, the CLI, and MCP can't create endpoints yet, so use the REST API with a token (see Mint a personal access token):
curl -sS https://cairn.stump.wtf/v1/hooks \
-H "Authorization: Bearer $CAIRN_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"title": "github push payloads", "request_cap": 100}'
Both fields are optional. request_cap is how many recent requests to keep. It defaults
to 500, and the oldest requests drop off as new ones arrive. The response carries the
addresses you need:
{
"id": "<id>",
"url": "https://cairn.stump.wtf/<id>",
"ingress_url": "https://cairn.stump.wtf/h/<id>",
"mcp": "mcp://cairn/hook/<id>",
"request_cap": 100,
"expires_at": "…",
"requests": []
}
| Address | Use it to |
|---|---|
ingress_url | Receive requests. Give it to the sender; it accepts any HTTP method without authentication. |
url | Watch requests arrive, in a browser |
mcp | Let an agent read and subscribe to the same stream |
Like any artifact, the endpoint expires after 7 days by default.
Send it something
curl -sS -X POST "https://cairn.stump.wtf/h/<id>?source=test" \
-H 'Content-Type: application/json' \
-H 'X-Hub-Signature-256: sha256=0123abcd' \
-d '{"hello": "cairn"}'
Every captured request gets the same answer: 200 with the body captured. The sender
never gets anything else back, and can't read what was captured. The other responses:
| Status | Meaning |
|---|---|
404 | No such endpoint, or it has expired |
413 | The body is over the capture limit (5 MiB) |
429 | Too many requests from one address, or to one endpoint |
Watch requests
-
In a browser: open the endpoint's
url. Requests stream in with highlighted JSON and a status mix. You can react to a single request, say 👀 on the one that broke. Requests don't take comments. -
From an agent: read the resource
mcp://cairn/hook/<id>to get the endpoint and its retained requests, newest first, and subscribe to hear about new ones. Agents can only read the stream. Nothing writes into it except the ingress URL. -
From a script:
curl -sS "https://cairn.stump.wtf/v1/hooks/<id>/requests" # newest firstcurl -sS "https://cairn.stump.wtf/v1/hooks/<id>/requests/<seq>" # one requestcurl -sS "https://cairn.stump.wtf/v1/hooks/<id>/requests/<seq>/body" -o body # its exact bytescurl -sSN "https://cairn.stump.wtf/v1/hooks/<id>/stream" # live, as server-sent events
Anyone with the endpoint's url can read every captured body. Don't point a sender at an
inspector if its payloads carry secrets or personal data you wouldn't paste into a shared
document.
What gets recorded
Cairn stores each request as inert data. It never runs, follows, or fetches anything in a
payload. Before it stores the headers, it lower-cases their names and drops cookies and
connection-level headers: Cookie, Set-Cookie, Proxy-Authorization, and hop-by-hop
headers such as Connection. Authorization is kept by name, but its value is always
stored as [REDACTED], after the scheme word when there is one (Bearer [REDACTED]).
Signature headers such as X-Hub-Signature-256 and X-Cairn-Signature are kept, which is
what makes the inspector useful for signature problems.
Cairn also scans the query string, the headers and the body for credentials before it
stores anything, and replaces each one it finds with [REDACTED]. The sender still gets
the usual reply: a capture is never refused over what the scan finds. Each captured
request reports what the scan did in redaction_status, with the rules that matched. A
field Cairn couldn't store safely is replaced with a notice and listed in
redaction_withheld. A binary body, such as an image, is stored as sent and reads
not_scanned_binary.
Debug a delivery to Switchboard
When Switchboard rejects a signed webhook, it doesn't store the payload, so there's nothing to look at afterwards. Send a copy to an inspector to see what was actually on the wire:
- Create an endpoint, as above.
- Add its
ingress_urlas a second target next to the Switchboard URL. For a forge such as GitHub or Gitea, add a second webhook with the same secret. For Cairn's own outbound events, add the endpoint as a second subscription and give it the same signing secret; Cairn sends the same body to both, signed with that secret. - Trigger the event again.
- Compare the capture with what Switchboard expects:
- The signature. Recompute the HMAC over the exact captured bytes (download them
from
/body; don't copy text out of the formatted view) and compare it with the captured signature header. See Webhook signature mismatches. - The event headers. For Cairn,
X-Cairn-Eventshould beartifact.created, andX-Cairn-Event-Idshould equal the body'sevent_id. - The content type and size. Switchboard reads JSON bodies and enforces a size limit.
- The signature. Recompute the HMAC over the exact captured bytes (download them
from
- Remove the extra target when you're done.