Testing & Debugging Webhooks
Fire a test payload, read the delivery log and retry a failed delivery by hand, without waiting for the production trigger.
You do not need to wait for a real project to change to exercise a webhook endpoint. Zuse Bifrost has three tools for development and on-call.
Fire a synthetic delivery
From the app: Settings → Webhooks, the endpoint's menu, Send test delivery. The REST API and the MCP server offer the same action behind the webhook-endpoint:update ability.
The test delivery:
- Creates a pending delivery row typed as
project.created. - Signs the payload with the current secret.
- Routes it through the same queue that real deliveries use.
- Shows up in the delivery log at once, so you can watch its progress.
The data block is a marker, not a realistic event body:
{
"id": "evt_01HXDELIVERY000000000TST",
"type": "project.created",
"created_at": "2026-11-02T10:05:00+00:00",
"api_version": "2026-11-01",
"project_id": "7f3d1c92-8b45-4e6a-9d21-5c8e0a4b6f13",
"data": {
"synthetic": true,
"endpoint_id": "c62a08f4-1b7d-4e35-9860-a37f5d21e0b9"
}
}Use it to verify DNS, TLS and HMAC verification end to end. It will not exercise your field-mapping logic, and because it is always typed project.created it never exercises any other event's shape. Branch on data.synthetic to skip business logic on test deliveries.
Only the member who registered the endpoint, or the team owner, can fire a test (or rotate, pause, resume and delete it). Other team members see the endpoint but the menu actions answer with a refusal; over the API the same call returns 403.
Inspect the delivery log
Every delivery, real or synthetic, shows up in Settings → Webhooks → Deliveries with:
- The endpoint's name
- The event name
- The status (
pending,delivered,failedordead) - The HTTP status your handler returned
- The number of attempts so far
- When it was created
Filter by status with the tabs at the top; Dead-lettered finds the rows that exhausted their retries. Opening a row shows the full envelope that was posted, the response excerpt (the first 4 KB) your handler returned or the reason the worker refused to post, and the time of the next attempt.
Use a request inbox for a quick inspection
When you need to see exactly what Zuse Bifrost posts:
- Open a request-inbox service such as webhook.site and copy the unique inbox URL.
- Register an endpoint pointing at it with whatever events you want to see.
- Trigger the flow, or fire a test delivery as above.
- The inbox shows the raw headers, the body and your response.
Delete the endpoint when you are done so deliveries do not keep flowing to a public inbox.
Retry a failed or dead row by hand
From the deliveries page, a row's Retry action resets it to pending with zero attempts, clears the recorded response and queues the worker again immediately. A pending or delivered row cannot be retried, so a second click on a row the worker already picked up is refused rather than doubled. Replay dead re-queues every row dead-lettered in the last 24 hours, up to 500 at a time.
Common failure modes
| Symptom | Likely cause |
|---|---|
HTTP 401 or 403 from your handler | Signature verification rejecting the payload: check you are using the latest secret and the raw body, not re-parsed JSON. |
Connection refused | The target is down, or behind a firewall the delivery worker cannot reach. |
SSL handshake failed | An expired certificate or a private CA. Zuse Bifrost validates the full chain. |
Endpoint host resolved to …, outside its allowed IPs. | The hostname moved outside the endpoint's allow-list; fix the list or the record and replay. |
Endpoint inactive or deleted before delivery. | The endpoint was paused, auto-disabled or removed while the delivery waited; resume it and replay. |
| Delivered, but downstream never fires | Your handler returned 200 and crashed before enqueuing the work. Queue it first, then respond. |
Related
How is this guide?
Last updated on