Skip to content

Test GitHub webhook deliveries

A GitHub App or repo webhook has to point at a real URL before GitHub will send it anything — which normally means deploying first, or running a tunnel. Point it at a capture endpoint instead and you get the exact payload, headers and signature GitHub sent, without either.

1. Create the endpoint, use it as your webhook URL

curl -X POST "https://gethooklab.dev/api/v1/endpoints" \
  -H "X-API-Key: hkl_your_key_here"
# → { "id": "k3v9x2m1qa", "permanent": true, ... }

# GitHub → Settings → Webhooks → Add webhook
# Payload URL: https://gethooklab.dev/api/h/k3v9x2m1qa
# Content type: application/json
# Secret: (the same one you'll pass to verify-signature below)

Your account's first endpoint is permanent — it does not idle-expire, so it is safe to leave wired into a GitHub App's settings while you develop, not just for one test run.

2. Trigger a real event and read what arrived

Push a commit, open a PR, whatever the event you're building for — then pull the capture back:

curl "https://gethooklab.dev/api/v1/endpoints/k3v9x2m1qa/requests?limit=1" \
  -H "X-API-Key: hkl_your_key_here"

You get the full request GitHub sent: method, every header — X-GitHub-Event tells you which event fired, X-Hub-Signature-256 is the signature — and the raw JSON body. No guessing at GitHub's payload shape from docs; this is the actual bytes for your actual repo.

3. Verify the signature

curl -X POST "https://gethooklab.dev/api/v1/verify-signature" \
  -H "X-API-Key: hkl_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "github",
    "secret": "your_webhook_secret",
    "endpointId": "k3v9x2m1qa",
    "requestIndex": 0
  }'
# { "valid": true, "reason": "OK", ... }

GitHub signs the raw body with HMAC-SHA256 and sends it as sha256=<hex> — this checks it the same way your handler will, using the header GitHub actually sent rather than one you constructed by hand.

4. Replay it once your handler exists

Once you've written the route, replay the same captured delivery against it — no need to trigger a fresh GitHub event for every code change:

curl -X POST "https://gethooklab.dev/api/v1/replay" \
  -H "X-API-Key: hkl_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "endpointId": "k3v9x2m1qa",
    "requestIndex": 0,
    "targetUrl": "https://staging.your-app.dev/webhooks/github"
  }'

targetUrl has to be a real, reachable address — a private or local one (localhost, an internal IP) is rejected outright, redirects included. See the replay guide for why, and what to point it at instead.

Also supported

The same verify-signature call checks Stripe and Shopify signatures too. See the Stripe walkthrough or the full API docs.

Free tier is 1,000 captured requests a month, one permanent endpoint, no credit card — get a key.