Webhook Guides

Webhook Debugging Guide

Debugging webhooks in production is hard when all you have is one failed request, one provider dashboard status, or one receiver log line. A webhook can fail before ingress, during source authentication, while routing, during destination delivery, inside the receiver, or after an apparently successful acknowledgment.

A reliable debugging workflow keeps every stage visible: the inbound request, the routed event, destination attempts, response bodies, retry timing, and the final recovery action.

What is webhook debugging?

Webhook debugging is the process of tracing an event through the delivery path until you can name the exact failure boundary. Instead of asking only whether the provider sent a webhook, you compare what FastHook captured, what routes matched, what destination was called, what response came back, and whether retry or replay is safe.

The best debugging runbooks separate ingress evidence from delivery evidence. This matters because a provider can receive 200 OK while a downstream destination still fails later.

Webhook debugging evidence ladder from provider request to FastHook request record, event record, attempt record, receiver state, and recovery.
Debugging gets faster when each record answers one question: did FastHook receive the request, did a route create an event, did the destination respond, and is retry or request replay the right recovery action?

Common webhook debugging symptoms

  • The provider says it sent the webhook, but no request appears in your receiver logs.
  • The provider shows 200 OK, but the expected downstream side effect never happened.
  • A destination returns 401, 403, 404, 429, or 5xx.
  • Signature verification fails because the receiver uses parsed JSON instead of the raw request body.
  • A payload field changed and the receiver started returning validation errors.
  • Retries created duplicate side effects because the receiver did not enforce idempotency.
  • A route was paused, disabled, filtered, deduplicated, or pointed at the wrong destination URL.
Webhook debugging runbook showing symptoms, FastHook records to inspect, likely causes, and recovery actions.
Start from the symptom, then inspect the narrowest record that can confirm or rule out the cause. Retry only after the receiver, route, or source issue is fixed.

FastHook debugging workflow

  1. Search a narrow time window for failed events or rejected requests.
  2. Open the event detail to read failure_message, response_status, and payload data.
  3. Open attempts for that event to compare each attempt_number, trigger, response body, and requested URL.
  4. Open the original request when you need raw payload, headers, query parameters, method, or rejection cause.
  5. Fix the receiver, destination URL, credentials, filter, transformation, or source authentication issue.
  6. Retry a single event first. Use request replay only when you need to run the original request through current routing again.

FastHook API behavior used by these examples

These examples match the current FastHook API implementation. Control API calls use a project API key or session token, tenancy is selected with x-team-id, and the examples use the /v1 prefix for requests, events, attempts, and retries.

  • Event lists support status, source_id, connection_id, destination_id, from, to, q, cursors, and limit. Event statuses are uppercase, such as FAILED and SUCCESSFUL.
  • Event detail at /v1/events/:id includes the event model and payload data when the payload is available.
  • Attempt lists support event_id, order_by=created_at, dir, and limit. Attempt models include response_status, attempt_number, trigger, error_code, body, requested_url, and http_method.
  • Request lists support status=accepted or status=rejected, source_id, from, to, verified, cursors, and include=data.
  • POST /v1/events/:id/retry redelivers one routed event through the same branch. POST /v1/requests/:id/retry replays the original request through current source routing.

Webhook debugging examples

Find failed delivery events

Find failed events
curl "https://api.fasthook.io/v1/events?status=FAILED&source_id=src_q6z62b6py5o79b&from=now-24h&to=now&limit=20" \
  -H "Authorization: Bearer fhp_xxx" \
  -H "x-team-id: tm_3b5335b627084a838b"

Retrieve one event with payload data

Retrieve event
curl "https://api.fasthook.io/v1/events/evt_01jv8c4n9p3x7r2t6q5m1k0s8b" \
  -H "Authorization: Bearer fhp_xxx" \
  -H "x-team-id: tm_3b5335b627084a838b"

Inspect delivery attempts for one event

Inspect attempts
curl "https://api.fasthook.io/v1/attempts?event_id=evt_01jv8c4n9p3x7r2t6q5m1k0s8b&order_by=created_at&dir=desc&limit=100" \
  -H "Authorization: Bearer fhp_xxx" \
  -H "x-team-id: tm_3b5335b627084a838b"

Retrieve the original request

Retrieve request
curl "https://api.fasthook.io/v1/requests/req_01jv8c3m7b2p4q9x6r5t1n0k8s" \
  -H "Authorization: Bearer fhp_xxx" \
  -H "x-team-id: tm_3b5335b627084a838b"

Find rejected ingress requests

Find rejected requests
curl "https://api.fasthook.io/v1/requests?source_id=src_q6z62b6py5o79b&status=rejected&from=now-24h&to=now&limit=20&include=data" \
  -H "Authorization: Bearer fhp_xxx" \
  -H "x-team-id: tm_3b5335b627084a838b"

Retry the failed routed event

Retry event
curl -X POST "https://api.fasthook.io/v1/events/evt_01jv8c4n9p3x7r2t6q5m1k0s8b/retry" \
  -H "Authorization: Bearer fhp_xxx" \
  -H "x-team-id: tm_3b5335b627084a838b"

Replay the original request through current routing

Retry request
curl -X POST "https://api.fasthook.io/v1/requests/req_01jv8c3m7b2p4q9x6r5t1n0k8s/retry" \
  -H "Authorization: Bearer fhp_xxx" \
  -H "x-team-id: tm_3b5335b627084a838b"

Failed event record example

Event failure JSON
{
  "id": "evt_01jv8c4n9p3x7r2t6q5m1k0s8b",
  "request_id": "req_01jv8c3m7b2p4q9x6r5t1n0k8s",
  "connection_id": "web_nbbweTiOtzsm",
  "destination_id": "des_TU9ioCk5EHUU",
  "source_id": "src_q6z62b6py5o79b",
  "status": "FAILED",
  "attempts": 3,
  "response_status": 504,
  "failure_message": "timeout after 15s",
  "error_code": "timeout",
  "last_attempt_at": "2026-05-31T10:12:45.000Z"
}

Failed attempt record example

Attempt failure JSON
{
  "id": "att_01jv8c7r1a9m2k4p6x0z3n5q8s",
  "event_id": "evt_01jv8c4n9p3x7r2t6q5m1k0s8b",
  "destination_id": "des_TU9ioCk5EHUU",
  "attempt_number": 3,
  "trigger": "AUTOMATIC",
  "status": "FAILED",
  "response_status": 504,
  "error_code": "timeout",
  "requested_url": "https://billing.example.com/webhooks",
  "http_method": "POST",
  "body": {
    "error": "receiver timed out before acknowledging the event"
  }
}

Automate a debugging snapshot with Node.js

Node.js triage
const apiBase = "https://api.fasthook.io";
const token = process.env.FASTHOOK_API_KEY;
const teamId = process.env.FASTHOOK_TEAM_ID;
const eventId = "evt_01jv8c4n9p3x7r2t6q5m1k0s8b";

const headers = {
  authorization: `Bearer ${token}`,
  "x-team-id": teamId
};

const event = await fetch(`${apiBase}/v1/events/${eventId}`, { headers }).then((res) => res.json());
const attempts = await fetch(
  `${apiBase}/v1/attempts?event_id=${eventId}&order_by=created_at&dir=desc&limit=20`,
  { headers }
).then((res) => res.json());

console.log({
  event_status: event.status,
  response_status: event.response_status,
  failure_message: event.failure_message,
  latest_attempt: attempts.models[0]
});

Debugging checklist

  • Use a narrow from and to range before expanding the search.
  • Confirm whether the request was accepted or rejected before debugging destination code.
  • Compare event status with the latest attempt status and response body.
  • Check whether the failure is route configuration, credentials, schema drift, receiver timeout, or receiver code.
  • Read destination response bodies before retrying; many receivers explain the exact validation or auth problem.
  • Verify idempotency before retrying traffic that can create external side effects.
  • Retry a single event before replaying a wider request or batch window.

Webhook debugging FAQ

What is webhook debugging?

Webhook debugging is the process of inspecting the inbound request, routing result, destination event, delivery attempts, response status, response body, and retry history to understand why a webhook integration failed.

Why can a provider show 200 OK while my app still fails?

A 200 response only means the webhook request was accepted at the ingress layer. Downstream routing, destination delivery, receiver processing, database writes, and async jobs can still fail after that acknowledgment.

Which FastHook record should I inspect first?

Start with the request record when you are unsure whether traffic reached FastHook. Start with failed events when you know ingress succeeded but a destination did not process the event.

When should I retry a failed webhook event?

Retry a failed event after you have identified the failure, fixed the receiver or route, and verified idempotency so the retry cannot create duplicate external side effects.

Try FastHook

Use FastHook to inspect request history, failed events, destination attempts, response bodies, and replay actions in one place. That gives your team enough evidence to fix webhook failures without guessing from isolated provider logs or receiver logs.

Related guides