Skip to main content

API reference

Debugging webhooks

Using the deliveries log, replaying failed deliveries, the test-fire endpoint, and the auto-pause threshold.

Last updated

Webhooks fail in mysterious ways. Here's the debugging path that catches 95% of issues.

Test fire

Hit POST /api/v1/webhooks/{id}/test (or jobbydev_webhook_test from your agent) to send a synthetic event with a known payload to your endpoint. Useful when:

  • First wiring up signature verification.
  • Confirming the endpoint accepts POSTs at all.
  • Debugging without waiting for a real match to fire.

The deliveries log

GET /api/v1/webhooks/{id}/deliveries returns the 50 most recent delivery attempts with:

  • Event type and event ID.
  • Attempted-at timestamp.
  • HTTP status returned by your endpoint (or null on timeout).
  • Response body (first 1024 bytes).
  • Next-retry timestamp (null once delivered or dead-lettered).
  • Final state: delivered, retrying, dead-lettered.

Browser UI at /account/webhooks renders this same data with filters by status and event type.

Common failure shapes

Signature verification fails on every delivery

Almost always one of:

  • You're computing HMAC over the parsed-then-stringified body, not the raw bytes. Frameworks like Express auto-parse JSON; you need to opt into raw body for the webhook route.
  • You stored the wrong secret. We only show the secret once on subscription creation. If you lost it, rotate the secret in the dashboard and update your endpoint.
  • You're comparing strings with == instead of a constant-time function. Most stdlib HMAC modules expose one.

Endpoint times out

Your handler has 10 seconds. If you're doing real work (parsing, DB writes, fan-out to other services) inline, you'll hit the cap on busy events. The fix is to accept-and-queue:

  1. Verify signature.
  2. Push event to a local queue (Redis, SQS, in-memory channel).
  3. Return 200 immediately.
  4. Process the queue async with whatever timeout you want.

Subscription auto-paused

After 25 consecutive failures across all events, the subscription is paused — the trip-wire prevents your endpoint from soaking delivery worker bandwidth indefinitely. You get an email notification. Steps to recover:

  1. Pull the deliveries log; identify the systematic failure (502 Bad Gateway, signature error, timeout).
  2. Fix the underlying issue.
  3. Test-fire to confirm a delivery succeeds.
  4. Re-enable the subscription via PATCH or the dashboard.

Duplicate events

Inevitable under at-least-once delivery. We retry on timeout or non-2xx response, and your endpoint may have actually processed the event before timing out. Use the event id field as your idempotency key — store processed IDs for at least 24 hours and skip duplicates.

Out-of-order events

Less common, but possible. We retry failed deliveries on a back-off schedule that can interleave with newer events for the same subscription. Use the created_attimestamp on the payload to detect and either reorder or discard stale events. Don't rely on delivery order.

Replaying a delivery

Today: not directly. Workarounds:

  • Use POST /api/v1/webhooks/{id}/test to fire a synthetic event with the same shape.
  • Use the deliveries log to find the original payload and POST it to your endpoint manually (re-sign with your secret).

Direct replay-from-log is on the Tier 7 follow-up backlog.

Local development

For local-machine endpoints, use a tunneling tool (ngrok, cloudflared, localtunnel) to expose your dev server. Subscribe with the tunnel URL as the target_url; tear down and rotate when done.

Related reading