Skip to content
API reference

HookWatch HTTP API reference

Everything the dashboard does, over HTTP. Authenticate with a workspace API key, then manage endpoints, read deliveries, and replay or retry failed webhooks — with worked curl examples and the real response shapes.

The HookWatch HTTP API is what the dashboard and the hookwatch CLI both call. Base URL https://api.hookwatch.dev; every /api/* route speaks JSON. The examples below use a workspace API key — the same credential a script or CI job would use. For the full, normative route list, see the in-repo API contract; this page is the worked-example tour.

Authentication

Mint a workspace API key in the dashboard under Settings → Tokens → New token. The hwk_… secret is shown once — copy it then. It authenticates a workspace (not a user), so it grants member-level reads and non-role-gated actions; owner/admin-only routes and the admin panel refuse it.

curl -s "https://api.hookwatch.dev/api/deliveries?limit=1" \
  -H "Authorization: Bearer $HOOKWATCH_KEY"
# → {"data":[{"id":"evt_…","status":"delivered", …}]}

Responses and errors

Success is always wrapped in a data envelope — a single object, or a bare array for lists (there is no cursor object; lists take ?limit= only). Errors are uniform: {"error":{"code","message","fields?"}}, with fields present only on a validation failure.

HTTPcodeWhen
400bad_requestMalformed JSON or an unknown field.
400validationPer-field failure; fields populated.
401unauthenticatedMissing or invalid key.
403forbiddenAuthenticated, but the key lacks the role.
404not_foundMissing, or outside the key's workspace.
409conflictUniqueness or an illegal state transition.
422unprocessableWell-formed but unactionable (e.g. no stored body to replay).

Endpoints

An endpoint is a capture URL plus its forwarding config. provider is one of stripe, github, telegram, shopify, meta, custom; mode is proxy, tunnel, or capture. The signing secret and public URL are minted for you, and the secret is returned exactly once.

curl -s -X POST "https://api.hookwatch.dev/api/projects/$PROJECT_ID/endpoints" \
  -H "Authorization: Bearer $HOOKWATCH_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"orders","provider":"stripe","mode":"proxy","destination":"https://app.example.com/webhooks/stripe"}'
# → 201 {"data":{"id":"ep_…","slug":"…","public_url":"https://hooks.hookwatch.dev/in/…",
#          "signing_secret":"whsec_…","signing_enabled":true,"active":true, …}}

Read one, or delete it (delete returns 200 with a body, not 204):

curl -s "https://api.hookwatch.dev/api/endpoints/$ENDPOINT_ID" \
  -H "Authorization: Bearer $HOOKWATCH_KEY"    # {"data":{ …EndpointView }}
curl -s -X DELETE "https://api.hookwatch.dev/api/endpoints/$ENDPOINT_ID" \
  -H "Authorization: Bearer $HOOKWATCH_KEY"
# → 200 {"data":{"id":"ep_…","deleted":true}}

Deliveries and recovery

A delivery is one inbound webhook and its forwarding outcome. The workspace feed is a lean list (no payload or headers); the detail route carries the full request, response, and error.

curl -s "https://api.hookwatch.dev/api/deliveries?limit=50" \
  -H "Authorization: Bearer $HOOKWATCH_KEY"
# → {"data":[{"id":"evt_…","endpoint_name":"orders","status":"failed",
#            "error_type":"connection_refused", …}]}

Replay re-sends the exact captured payload (optionally to a different target), records a new attempt, and never mutates the original — safe once your handler is fixed:

curl -s -X POST "https://api.hookwatch.dev/api/deliveries/$DELIVERY_ID/replay" \
  -H "Authorization: Bearer $HOOKWATCH_KEY" \
  -H "Content-Type: application/json" \
  -d '{"reason":"handler fixed"}'
# → {"data":{"replay_id":"rep_…","delivery_id":"evt_…","status":"completed",
#          "attempt_id":"att_…","target_status_code":200,"latency_ms":42, …}}

Retry re-sends the same delivery to its own endpoint and advances its status; analyze runs the deterministic failure classifier (no external LLM):

curl -s -X POST "https://api.hookwatch.dev/api/deliveries/$DELIVERY_ID/retry" \
  -H "Authorization: Bearer $HOOKWATCH_KEY"
# → {"data":{"delivery_id":"evt_…","status":"retrying","next_attempt_number":2, …}}
curl -s -X POST "https://api.hookwatch.dev/api/deliveries/$DELIVERY_ID/analyze" \
  -H "Authorization: Bearer $HOOKWATCH_KEY" \
  -H "Content-Type: application/json" -d '{}'
# → {"data":{"analysis_id":"ana_…","category":"…","severity":"high","retryable":true, …}}

Webhook ingest

Providers send to an endpoint's capture URL, https://hooks.hookwatch.dev/in/<slug>. When signing is on, sign the raw body with HMAC-SHA256 keyed by the endpoint's signing secret and send it in X-Hookwatch-Signature. HookWatch verifies it, persists the delivery, and forwards asynchronously — re-signing the forward with the same scheme.

SECRET='whsec_…'      # the endpoint's signing secret (shown once on create)
SLUG='…'              # from the endpoint's public URL
BODY='{"type":"ping","id":1}'
SIG="sha256=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $NF}')"

curl -s -X POST "https://hooks.hookwatch.dev/in/$SLUG" \
  -H "Content-Type: application/json" \
  -H "X-Hookwatch-Signature: $SIG" \
  --data-binary "$BODY"
# → {"id":"evt_…","received":true}   (bare JSON — forwarding is async)

Ingest errors are plain text: 401 bad or missing signature, 404 unknown slug, 403 paused/archived or a blocked source IP, 413 oversize.

Get started

Start debugging your webhooks.

Point one endpoint at HookWatch, capture a failure, and replay it once it’s fixed. Free during beta.