Appearance
Webhooks & Replay
Every request that hits an endpoint is stored as a captured webhook. This page covers what's captured, how long it sticks around, how to replay it, and how to watch new captures arrive in real time.
What gets captured
For every request, SignalBin stores:
- Method, path, and query string
- Content type and a detected body type
- The request headers
- The request body
- Any file attachments from a multipart upload
- The remote IP, recorded with the event but not exposed through the API or UI
Browsing captures
Go to Webhooks in the sidebar. The Received webhooks page lists everything the workspace has captured, newest first, in a table showing the request, its status, body type, size, file count, and when it arrived. Load more at the bottom fetches the next page.
Two dropdowns above the table narrow the list: Method (defaulting to All methods) and Endpoint (defaulting to All endpoints). Clear resets both.
Click a row to open the capture. The detail page has Body, which starts collapsed with a one-line preview and expands when you click the heading, plus Headers and Attached files down the right side and a Deliveries section below the body. Each attached file is a download link showing its name and size. Copy URL and Copy buttons put the request URL, the body, or the headers on your clipboard.
The same list is available over the API:
bash
curl "https://signalbin.work/api/v1/webhooks?endpointID={endpointID}&method=POST&limit=50" \
-H "Authorization: Bearer $SIGNALBIN_TOKEN"bash
signalbin webhooks list --endpoint {endpointID} --method POST --limit 50limit defaults to 50 and is capped at 100. The response carries a nextCursor to pass back as cursor for the next page.
Sensitive values are redacted in what's stored
Before a capture is written, SignalBin replaces sensitive values with [redacted]. This applies to headers, query parameters, JSON bodies, and form-encoded bodies. A name is treated as sensitive when it is authorization, cookie, or set-cookie, or when it contains password, secret, token, api_key, apikey, signature, or credential. Underscores and hyphens are treated the same, and matching is case-insensitive.
Provider signature headers such as Stripe-Signature and X-Hub-Signature-256 match the signature marker, so the stored copy of one reads [redacted]. A JSON body is parsed and re-serialized on the way in, so the stored body is not always byte-identical to what arrived.
Automatic fan-out is not affected. Destinations are sent the original request, not the stored copy. Replay works from the stored copy, which is where this matters. See Replaying a webhook.
The Headers panel shows names lowercased and one value per name, rather than in the order they arrived on the wire.
Retention
Retention is 30 days. You can see it in Settings under the Workspace tab, in the Retention dropdown, which accepts no other value today. Once a capture passes its retention window:
- The request body is deleted.
- Any file attachments are deleted.
- The event record itself is not. Method, path, headers, timestamps, and metadata all persist.
Retention trims payloads, not history. You'll still see that a webhook arrived, when, and with what headers, long after the body and files behind it are gone. Opening one of those older captures shows "Body was removed by retention or was empty." where the payload used to be. Replay a capture before its payload ages out, because a replay sends the stored body and an expired capture no longer has one.
Replaying a webhook
Replay re-delivers a captured request to every enabled destination on its endpoint, the same way fan-out did on first arrival: same strip and inject rules, same method override, same retry behavior. It is not targeted at a single destination.
In the app, open the capture and click Replay at the top right. You can also replay without opening it: on the Webhooks list, open a row's menu (the three-dot button) and choose Replay. Either way a message tells you how many destinations it reached, or how many of them failed.
bash
curl -X POST https://signalbin.work/api/v1/webhooks/{id}/replay \
-H "Authorization: Bearer $SIGNALBIN_TOKEN"bash
signalbin webhooks replay {id}The response returns the recorded attempt for each destination. If the endpoint has no enabled destinations, nothing is sent and the app tells you to add a destination to the endpoint first (over the API, 400 no_destinations).
Replay is the usual way to re-run a webhook against a destination you just fixed, or to put real traffic through a new destination without waiting for the source to fire again.
WARNING
Replay sends the stored request, which is the redacted copy described above. A destination that verifies a provider signature will reject a replayed request, because the signature header arrives as [redacted] and a JSON body may have been re-serialized. Fan-out at capture time is the path that carries the original, verifiable request. See Headers & Signature Passthrough.
Delivery history
Every delivery attempt against a captured webhook is recorded, whether it came from automatic fan-out or from a replay you triggered. The capture's Deliveries section lists them: the response status (or Failed), the target URL, how long it took, how many attempts it needed, and whether it came from a Destination or a Manual replay.
bash
curl https://signalbin.work/api/v1/webhooks/{id}/replays \
-H "Authorization: Bearer $SIGNALBIN_TOKEN"bash
signalbin webhooks replays {id}The API returns the same rows with more detail: the target URL, the destination it belongs to, the status code or the transport error, the duration, how many attempts produced that outcome, the relayed body size, and a source:
source | Where the attempt came from | Shown in the app as |
|---|---|---|
forward | Automatic fan-out at capture time | Destination |
manual | A replay triggered from the UI or the replay endpoint | Manual replay |
cli | A delivery signalbin listen performed on your machine and reported back | Manual replay |
INFO
The history returns the 15 most recent attempts. One capture can already produce 10 fan-out rows on its own, so treat this as recent delivery status rather than a full audit log.
POST /api/v1/webhooks/{id}/replays is the record-only counterpart. It logs a delivery the caller already performed itself, which is how signalbin listen reports local deliveries the server has no way to make. The reported target URL is stored as metadata and never dialed.
Real-time capture notifications
GET /api/v1/events is a Server-Sent Events stream. Connect with a bearer token and SignalBin pushes an event the moment a new webhook is captured, instead of you polling the list endpoint.
bash
curl -N "https://signalbin.work/api/v1/events?endpoint={endpointID}" \
-H "Authorization: Bearer $SIGNALBIN_TOKEN"bash
signalbin listen {endpointID}signalbin listen without --forward connects to this same stream and prints each capture as it arrives, without delivering or recording anything. See CLI for forwarding captures to a local server instead of just watching them.
The endpoint parameter is required, and the stream carries only that endpoint's captures. An optional label parameter names the temporary entry the connection creates in the endpoint's Destinations tab, which is useful when several people are tailing the same endpoint. See the cli destination kind for what that entry is.
The stream opens with a ready event, then sends a webhook event per capture:
event: ready
data: {}
event: webhook
data: {"eventID":"...","endpointID":"..."}The event carries identifiers rather than the payload, so fetch GET /api/v1/webhooks/{eventID} for the body and headers. A : keepalive comment every 25 seconds holds the connection open. A workspace can hold 20 concurrent streams. Beyond that, new connections are refused until one drops (429 too_many_streams).
signalbin listen is an ordinary client of this same endpoint. The web dashboard's live updates run over a separate, cookie-authenticated route backed by the same event source, not this one, but the practical effect is the same: anything the dashboard shows you as it happens, this stream can show your own tooling too.
API and MCP access
| Operation | REST | Scope | MCP tool |
|---|---|---|---|
| List captures | GET /api/v1/webhooks | webhooks:read | list_recent_webhooks |
| Get one capture | GET /api/v1/webhooks/{id} | webhooks:read | get_webhook |
| Download an attachment | GET /api/v1/files/{id}/download | webhooks:read | get_webhook_file |
| Replay | POST /api/v1/webhooks/{id}/replay | webhooks:read | replay_webhook |
| Delivery history | GET /api/v1/webhooks/{id}/replays | webhooks:read | list_webhook_replays |
| Record an external delivery | POST /api/v1/webhooks/{id}/replays | webhooks:read | |
| Event stream | GET /api/v1/events | webhooks:read |
INFO
Replay is a write action gated by the webhooks:read scope. webhooks:write isn't a scope you can grant today, so any token that can read captures can also replay them. Keep that in mind when issuing tokens.