Open source · Apache 2.0 · Self-hostedStandard Webhooks compatible

Webhooks, made for humans and for machines.

A small tool to test webhooks. Catch one, change its payload, reply with any status, and watch what the sender does next. What takes a paid plan or a CLI elsewhere is one click here, or one curl for your scripts. Self-hosted, open source. When your handler runs on localhost, your browser tab is the tunnel.

No account: create an endpoint on the demo and point any service at its URL.

Or run it locally to try it out
$ curl -fsSL https://raw.githubusercontent.com/sarathsp06/testhooks/main/install.sh | sh && DATABASE_URL=memory testhooks

Then open localhost:8080 · No database needed. Linux, macOS, Windows; checks the SHA-256.

A local install is for testing: services on the internet can't reach localhost, so it only gets requests you send from your machine. To receive real webhooks, deploy it or use the live demo.

What's in the boxnot in webhook.site
  • Your localhost answers the sender
  • Forward to localhost from the tab
  • Standard Webhooks signature verification
  • HMAC and Ed25519 signatures
  • Reject bad signatures with 401
  • JavaScript transforms, free
  • Scripted replies, free
  • Live diff before you save
  • Try a script on recent traffic
  • Request forwarding with retries
  • Sync or async forwarding
  • Replay any request
  • Copy as cURL
  • Export requests as JSON
  • Envelope-encrypted secrets, write-only
  • Browser mode: nothing on disk
  • Self-hosted: your server, your limits
  • In-memory mode, no database
  • PostgreSQL when you want to keep it
  • REST API and WebSocket
  • One Go binary, UI embedded
  • Self-host with scripting
  • Rate limiting and trusted proxies
  • Docker image and Vercel deploy
  • Apache 2.0
webhook.site has been here ten years. so why look?

Three things to try in your first minute.

Testing a webhook is mostly poking at it: change a field, fail on purpose, see what the sender does. Elsewhere that needs a paid plan, a CLI or a config file. Here each one is a click on the live demo, no account, and the same call from curl.

01 · the mock

Change a payload and see the diff before it ships

Open a captured request, click Test a transform on this, type two lines of JavaScript. The diff updates as you type, it runs against your recent traffic, and nothing is saved until you say so.

How transforms work
02 · the reply

Make the sender retry, on purpose

Script a 500 reply and watch the provider's retry arrive. Then return 200. You just tested your error handling without touching your app, and it cost nothing.

How scripted replies work
03 · the tunnel

Watch your localhost answer the provider

Point forwarding at localhost, turn on the local reply. The provider gets your handler's real status and body, through your browser tab. No CLI, daemon or account.

Webhooks to localhost

The one-liners above run Testhooks on your machine, which is enough to try it with curl. Real providers can only deliver to a URL they can reach, so for those deploy it on a server or PaaS with a public URL.

How it works

Two modes. Pick your trade-off.

Your browser is the tunnel. Both ways.

The server holds the provider's request open and streams it to your tab over WebSocket. Your tab runs the transform, delivers with fetch() to localhost, and sends your app's reply back. The provider sees that reply.

01Webhook senderAny service that sends webhooks.
HTTP POST
02Testhooks serverReceives at /h/:slug and relays it. Keeps only a short in-memory buffer.
WebSocket
03 · the tunnelYour browser tabRuns the transform, delivers with fetch(), waits for the reply.
fetch() ⇄ reply
04localhostYour app answers. Its status and body travel back to the sender.

The server only relays

Transforms and forwarding run in your tab, which is how it reaches localhost, Docker and anything else your machine can see.

Your app's reply reaches the sender

Turn on Reply to the sender with my app's response. A 200 marks the delivery done; a 500 makes the provider retry, as in production.

Nothing stored unless you ask

Payloads never touch the server's disk. Turn on Persist requests to keep a copy.

Example: your receiver only runs on localhost

  1. 1Create an endpoint on the live demo or your own Testhooks and paste its /h/:slug URL into the provider's webhook settings.
  2. 2In the tab, set forward to http://localhost:[port]/[path] and turn on the local reply.
  3. 3Trigger the event. It lands in your tab and on your local server, and the provider gets your handler's reply.
Standard Webhooks

Verify signatures like a real receiver.

Testhooks checks Standard Webhooks signatures from Sparrow, Svix and other senders that follow the spec. Turn on enforcement and bad signatures are rejected, so you test the same path production takes.

  • Reads the spec headers: webhook-id webhook-timestamp webhook-signature
  • Can reject invalid signatures with 401 instead of accepting everything
  • Works with Sparrow, Svix and other compliant senders
POST /h/[your-slug]example request
webhook-id: msg_[id]
webhook-timestamp: [unix-seconds]
webhook-signature: v1,[base64-signature]
Signature valid: accepted and forwarded
Signature mismatch: rejected, like a real receiver

Everything you need to test a webhook. Nothing you don't.

Not a webhook server, not a gateway: one Go binary with the UI embedded. Every screen is a REST call you can make with curl. Storage is PostgreSQL, or memory for local testing.

Replace the tunnel

Forward from your tab to localhost, Docker or anything your machine reaches, and the sender gets your app's reply. No CLI, daemon or account.

Private by default

In browser mode, payloads are relayed over WebSocket and never written to the server's disk.

Live inspection

Every request appears the instant it arrives: headers, query, body with syntax highlighting. Copy as cURL, export as JSON.

Script transforms

Reshape payloads with a few lines of JavaScript, sandboxed in WASM, before they are forwarded.

Forward and mock

Forward to any URL, sync or async with retries, and script the exact response the sender gets back.

Verify signatures

Check Standard Webhooks signatures from Sparrow, Svix and others, and reject bad ones like a real receiver.

Side by side

Where it's different, and where it isn't.

All three catch a webhook and show it to you. The difference is what happens when you want to poke at it.

Testhookswebhook.sitengrok
Getting a URLOne click, no accountOne click, no accountInstall the agent, sign in
Changing a payloadType JavaScript, watch the diff on real traffic, save when it's rightCustom Actions, on a paid plan; edit, then runTraffic Policy rules in config
Scripting the replyJavaScript, in the browser, free; preview what the sender getsStatic response free; WebhookScript on a paid planPolicy rules; the app's own reply through the tunnel
Seeing what happened to one requestReceived, transformed, forwarded, replied: each step with its result on the requestRequest and your configured responseRequest and response in the inspector
Reaching an app on localhostFrom the tab; your app's reply goes back to the senderFrom the tab (XHR Redirect), fire and forget; or a CLIThrough the agent, full round trip
Checking a signatureStandard Webhooks, marked on every request; secrets write-only, encryptedNot built in50+ providers; metered on the free plan
Self-hostingOne binary with everything aboveOpen-source edition (MIT), without Custom ActionsNot offered
Limits on the hosted free tierDemo: 500 requests or 10 MB per endpoint, URLs public; self-hosted: your limits100 requests, 7 days, URLs public; self-hosted: your limits20k requests a month, 3 endpoints, random URL

From each product's public docs, October 2026: webhook.site's hosted free tier and open-source edition, ngrok's free plan. Paid plans add more on all sides. Corrections welcome on GitHub.

Point any webhook at it. See everything, reply with anything.

Try the live demo now, or run it locally to test with curl.