Debug webhooks without triggering the same event again.

Capture webhooks once, forward them to multiple destinations, replay failures locally, test edge cases, and turn fixes into regression tests.

Free plan, no card. Anonymous endpoints work without an account.

~/payments-api
$ hwcli listen payments --forward :3000/webhooks
✓ Connected
Listening: payments
Forwarding → http://localhost:3000/webhooks
12:42:18 POST payment.completed → 500 184ms
$ hwcli replay evt_8f21
Replaying evt_8f21…
POST localhost:3000/webhooks
200 OK 87ms
✓ Success

Inspect webhooks locally

Expose your local application through a public HTTPS URL and inspect incoming webhook requests without deploying.

Illustration · sample data
  1. External requestStripe, GitHub, any HTTP client
  2. Public HookWatcher URLacme.hookwatcher.com
  3. HookWatcher serverroutes it to your tunnel
  4. hwcli tunneloutbound, encrypted connection
  5. Your local applocalhost:3000
  • hwcli opens the connection from your machine, so there is no port forwarding and no deploy for each test.
  • Your app answers each request, and the caller gets its real response.
  • hwcli records every request in its local inspector on 127.0.0.1; HookWatcher doesn't keep tunnel requests.
  • How tunnels work
hwcli local inspector14:02:11
POST/webhooks/stripe200 OK42 ms

Content-Type: application/json · Stripe-Signature: t=…,v1=…

{
  "type": "payment_intent.succeeded",
  "data": {
    "object": {
      "id": "pi_example123",
      "amount": 2499,
      "status": "succeeded"
    }
  }
}

One webhook in. Every destination gets it.

HookWatcher receives your webhook once and forwards it to production, staging, local development, QA or any configured endpoint, while tracking each delivery independently.

  • Production
  • Staging
  • QA
  • Local Development
  • Microservice
  • Backup Processor
One payment.completed event from Stripe is received once by HookWatcher and delivered to four destinations. Production answers 200 in 82 ms, Localhost 200 in 43 ms and QA 202 in 103 ms. Staging fails with 500, is retried on its own — attempt 2 fails, attempt 3 returns 200 — while the other destinations are not sent again. Summary: 4 destinations, 4 delivered, 1 retried.

Incoming · Stripe

POSTpayment.completed

evt_8f21

HookWatcher

1 event received

stored once, unchanged

4 deliveries

  • Production API200 ✓82 ms

    api.example.com/webhooks

  • Staging API200 ✓312 ms

    staging.example.com/webhooks#1 500#2 500#3 200

  • Local DevelopmentCLI200 ✓43 ms

    hwcli · localhost:3000/webhooks

  • QA Environment202 ✓103 ms

    qa.example.com/hooks

4 destinations4 delivered1 retried

  1. 1 incoming event
  2. 1 stored event, never changed
  3. 1 delivery per destination
  4. independent attempts
Forwarding destinationsAdd destination
  • Production APIhttps://api.example.com/webhooksActive
  • Staginghttps://staging.example.com/webhooksActive
  • Local DevelopmenthwcliCLI session · MacBook Pro → localhost:3000/webhooksOnline
  • QA Servicehttps://qa.example.com/hooksActive
payment.completedevt_8f21 · delivered to 4 of 4
Delivery result per destination
Production20082 ms
Staging200312 ms3 attempts
Localhost20041 ms
QA202103 ms

Send again to one destination — the others are not resent

  • Production
  • Staging
  • Localhost(selected)
  • QA

Send again to Localhost

Stop configuring the same provider webhook four times.

Point the provider at HookWatcher once, then route the event to every service and environment you need. One public webhook URL, multiple destinations behind it.

  • Independent delivery status
  • Automatic retries
  • Per-destination history
  • Local CLI forwarding
  • Send again to any destination
  • Centralized debugging

Forwarding destination limits vary by plan. See pricing

Without HookWatcher

Provider
├── webhook → Production
├── webhook → Staging
├── webhook → QA
└── webhook → Local tunnel
  • A provider configuration per environment
  • A different signing secret for each
  • The tunnel URL changes
  • Deliveries are hard to trace

With HookWatcher

Provider
   ↓
HookWatcher
   ├── Production
   ├── Staging
   ├── QA
   └── Localhost (hwcli)
  • One webhook configuration
  • Independent delivery tracking and retries
  • Central history for every destination
  • Local development without a tunnel

From a failed webhook to a permanent test, in six steps.

Every step happens on the event your provider already sent. Nothing is triggered twice.

The fix becomes a regression test.

✓ Regression Test Savedcheckout-completed

Stop re-triggering the provider to reproduce a bug.

Reproducing a webhook bug usually means making the provider send it again. With HookWatcher you keep the real request and replay it whenever you need.

Without HookWatcher

  1. 1Trigger the event in the provider
  2. 2Wait for it to arrive
  3. 3Search the logs
  4. 4Copy the payload
  5. 5Change code
  6. 6Trigger the provider again

Repeat for every attempt, waiting on the provider each time.

With HookWatcher

  1. 1Capture once
  2. 2Replay instantly
  3. 3Fix locally
  4. 4Replay again
  5. 5Save as a test

The provider sends the event once. Everything after happens on your machine.

Try it with a real endpoint

Send any HTTP request to the URL below, or press Send test, and watch it arrive. No account needed; anonymous endpoints keep their last 100 requests for 2 days.

Enable JavaScript to get a free webhook URL and watch requests arrive.

A development environment for webhook integrations.

  1. Capture once. Replay whenever you need.

    Send a captured production event to your local server as often as you like: same method, path, headers, signature and raw body. Replay Locally reaches the machine running hwcli; Replay to URL reaches staging.

    • Byte-exact by default; no headers added
    • Replays to production-looking hosts ask first
    Replay a webhook locally
  2. Change one field and replay the edge case.

    Modify & Replay changes JSON fields, headers, query or method, shows original and modified side by side, and sends the result. The captured event stays exactly as it was.

    • hwcli replay --set amount=null --header 'X-Test:1'
    • Re-sign the modified body with your stored secret
    Modify and replay
    Modify & Replayoriginal stays unchanged
    OriginalModifiedamount: 2999amount: nullcurrency: "USD"currency: "INR"customer.email: "[email protected]"customer.email: "[email protected]"
    2 fields changedReplay

    422 · amount is required

  3. Replay until it passes. Keep the proof.

    A Debug Session records every attempt against your local code. When the handler returns 2xx, save that request and response as a regression test.

    • hwcli debug evt_8f21 --forward :3000/webhooks
    • Attempts appear in the terminal and the dashboard
    Debug production failures locally
    Debug Sessionevt_8f21 → localhost:3000
    1. Replay #1500
    2. Replay #2500
    3. Replay #3200 ✓

    Save as Regression Test

  4. Test what providers really do.

    Replay Lab turns one event into duplicates, concurrent deliveries, delays, bursts, invalid signatures and malformed bodies, so idempotency and validation bugs show up before production does.

    • Every Lab delivery is marked synthetic
    • Limits follow your plan
    Replay Lab
    Replay Labmarked synthetic
    Duplicate
    ×10
    Concurrent
    ×20
    Delay
    5s
    Burst
    100
    Invalid signature

    X-HookWatcher-Synthetic: replay-lab

  5. See why one event failed and the other didn't.

    Compare a failed event with the most similar successful one: missing fields, changed values and type changes first, volatile IDs and timestamps hidden.

    • hwcli diff evt_123 evt_456
    • Credential values are never shown
    Compare failed and successful webhooks
    Event Diffvolatile fields hidden
    Failed eventSuccessful eventcustomer.emailmissing"[email protected]"status"past_due""active"
  6. Turn real events into regression tests.

    Tests store the exact request and the response your handler should give. Run them from the dashboard, your terminal or CI.

    • Status, latency, JSON subset and path assertions
    • YAML definitions you can keep in your repo
    Create webhook regression tests
    $ hwcli test run
    ✓ payment-success
    ✓ subscription-renewal
    ✓ refund-created
    3 passed

See what broke in production, then fix it locally.

The Failures inbox collects failed deliveries, replays and tests across a workspace. Every event keeps its full timeline: what arrived, what your server answered, and every replay after it.

Failures3 open

Timeline for evt_8f21

  1. 11:42:01

    Webhook received

  2. 11:42:01

    Forward attempt #1

  3. 11:42:02

    500 Internal Server Error

  4. 11:43:18

    Replay locally

  5. 11:43:18

    500

  6. 11:45:04

    Replay locally

  7. 11:45:04

    200 ✓

  8. 11:45:10

    Regression test created

The original event remains unchanged.

Replays, modified replays, debug sessions, diffs and tests are derived records linked to the captured event. You can always go back to exactly what the provider sent.

Original Eventevt_8f21 · unchanged
  • Delivery
    • Attempt #1
    • Attempt #2
  • Replay
  • Modified Replay
  • Debug Session
  • Event Diff
  • Regression Test
  • Byte for byte

    Bodies are stored exactly as received, with headers in their original order and a SHA-256 fingerprint.

  • Changes are deltas

    A modified replay stores what you changed, not a second copy of the payload.

  • Every action is attributed

    Who replayed what, when and to where, from the dashboard, hwcli or CI.

Built for your terminal.

hwcli is one self-contained binary. Sign in once in the browser; then listen, replay, diff and test from wherever your code runs, including CI with an API key, and open tunnels to services on your machine.

macOSLinuxWindowsDownload hwcli
$ hwcli test run
✓ payment-success
3 passed

A public HTTPS URL for localhost

When a caller needs your local app's real answer (OAuth callbacks, URL verification, demos), run a tunnel. hwcli opens an outbound connection to HookWatcher, so there is no port forwarding, and requests are recorded only in hwcli's local panel.

How tunnels work
$ hwcli tunnel http 3000 --name payment-test

Status        ● Connected
Public URL    https://payment-test.hookwatcher.com
Local target  http://127.0.0.1:3000

Install with Homebrew or Scoop, or download the latest version for your platform. CLI documentation

A fixed bug stays fixed.

Save the event that broke as a test with the response your handler should give. hwcli test run sends each one to your app in CI and fails the build when a webhook regresses.

Webhook Regression Testspull request #418
  • ✗ payment-successExpected: 200
    Received: 500
  • ✓ refund-created
  • ✓ order-created
  • ✓ subscription-renewal

23 passed1 failedexit 1

  • Tests come from real captured events, or YAML files in your repository.
  • Assert status, latency, JSON fields and signature handling.
  • JUnit output and clear exit codes for GitHub Actions, GitLab CI, Jenkins and Bitbucket.
Run webhook tests in CI/CD

More than a webhook catcher.

Seeing a request arrive is where HookWatcher starts. The rest of the loop is what fixes the bug.

A basic webhook catcher

  1. Capture
  2. Inspect

HookWatcher

  1. Capture
  2. Inspect
  3. Replay
  4. Debug
  5. Test
  6. CI

Know when providers change their payloads.

Secondary to debugging, but it saves days: HookWatcher learns each event's structure and tells you when a field changes type or disappears. Secrets stay masked everywhere, including links you share.

Schema Drift Detectedorder.created

customer.phone

Previously

string | null

Now

object

Compare PayloadsAccept ChangeIgnore Field

Shared event
Authorization:
••••••••••••••••
access_token:
••••••••••

What a shared link shows: secrets redacted

Share debugging context without exposing secrets.

How HookWatcher handles your data

From local debugging to team-wide reliability.

Invite your team to a workspace. Developers replay and debug, viewers inspect, admins manage endpoints and settings, and every action is in the activity log.

  • Shared workspaces
  • Event lineage
  • Audit trail
  • Redaction
  • Role-based access
  • Longer retention
  • Failure visibility
  • Enterprise custom limits

Works with any service that sends webhooks.

These providers are recognized automatically from their headers and payloads; signatures are verified for Stripe and GitHub. Anything that can send an HTTP request works.

  • Stripe
  • GitHub
  • Shopify
  • Twilio
  • Razorpay
  • PayPal
  • Slack
  • Discord
  • Any HTTP sender
HookWatcher

Start free. Upgrade for more history and limits.

Compare every limit
  • Free

    $0

    Try it and debug on your own

    10 days request retention

    2 forwarding destinations

  • Developer

    $5/mo

    For side projects and one integration

    30 days request retention

    3 forwarding destinations

  • ProRecommended

    $12/mo

    For individual developers

    60 days request retention

    5 forwarding destinations

  • Team

    $39/mo

    For engineering teams

    90 days request retention

    10 forwarding destinations

  • Enterprise

    Custom

    Custom scale, security and support

    Custom request retention

    Custom forwarding destinations

    Contact Sales

Stop triggering the same webhook again and again.

Capture it once. Replay it locally. Fix the bug. Turn the fix into a test.

  1. CapturedPOST payment.completed
  2. Replayed locally500 → 200 OK
  3. Saved as test✓ payment-success