UseToolSuite UseToolSuite

Webhook & WebSocket Tester

Build and validate webhook payloads against GitHub, Stripe and Slack templates, or open a live WebSocket or Server-Sent Events connection and watch the frames arrive. Both run entirely in your browser.

Build and validate a webhook payload against GitHub, Stripe and Slack templates — size, key count and nesting depth included.

Zero-knowledge architecture

Connections are established directly from your browser to the target server. There is no intermediate proxy and no backend logging your events, payloads or IP, which is what makes this safe to point at authenticated streams and internal endpoints. The event log is rendered as text, never as markup, so nothing an endpoint returns can execute in this page.

A webhook receiver is a security boundary, not just an endpoint

Anyone who learns your webhook URL can POST to it. That makes three properties non-negotiable on the receiving side — build the payload here, but design the handler around these:

  1. Authenticity — verify the provider’s signature so you only act on payloads they actually sent.
  2. Idempotency — the same event will arrive more than once; processing it twice must not double-charge, double-ship, or double-email.
  3. Freshness — reject payloads old enough to be a replay of a captured request.

Verifying signatures correctly

The mechanics are simple and easy to get subtly wrong:

  • Use the raw body. (See the FAQ above — this is the single most common failure.)
  • Compare in constant time. A naive == on signature strings leaks timing information that can be exploited to forge a signature byte by byte. Use a timing-safe comparison: crypto.timingSafeEqual in Node, hmac.compare_digest in Python.
  • Check the timestamp. Stripe-style schemes sign timestamp.payload and include the timestamp in the header. Reject anything outside a tolerance window (five minutes is typical) so a captured request can’t be replayed tomorrow.

Designing for duplicates

Every major provider documents at-least-once delivery — duplicates are a feature of reliability, not a bug. The defence is an idempotency key. Each event carries a stable id (evt_… from Stripe, the X-GitHub-Delivery UUID from GitHub). Record processed ids in a table with a unique constraint; if an insert collides, you’ve seen this event before — acknowledge with 2xx and do nothing else. This single table turns “we got charged twice during a provider retry storm” into a non-event.

Test the payload here, then test the transport

This builder validates and formats the JSON body so you can confirm shape and field names against a provider’s schema before you wire anything up. To exercise the full round trip, copy the generated payload and POST it to your endpoint — the cURL to Code tool will turn a curl -X POST into a snippet in your language. One caveat: a payload you build here won’t carry a valid provider signature, so point your handler’s signature check at test mode (or temporarily log-and-skip) when replaying hand-built payloads, and reserve real signature verification for traffic that genuinely came from the provider.


Live streams: WebSocket and SSE

The WebSocket / SSE mode above, or jump straight to it.

Reading WebSocket close codes like a protocol native

The close code is the first diagnostic fact of any dropped connection — and most dashboards never show it. The ones that matter:

CodeMeaningUsual culprit
1000Normal closureClean shutdown — not an error
1001Going awayServer restart or deploy
1006Abnormal closureNetwork/proxy/TLS failure, no handshake
1008Policy violationAuth rejected, origin not allowed
1009Message too bigFrame exceeded server’s limit
1011Server errorUnhandled exception server-side

A reconnecting client should treat 1000/1001 as expected, back off exponentially on 1006, and stop retrying on 1008 — hammering an endpoint that rejected your credentials just gets your IP rate-limited.

WebSocket or SSE? Choose by direction

Both deliver real-time updates; the decision is traffic direction. Server-Sent Events are one-way (server → client), ride on plain HTTP, reconnect automatically, and pass through proxies and CDNs effortlessly — the right choice for feeds, notifications, progress bars, and LLM token streams. WebSockets are bidirectional with lower per-message overhead — necessary for chat, multiplayer state, and collaborative editing where the client talks back constantly. The honest heuristic: if you only need to receive, SSE is operationally simpler; teams reach for WebSockets by default and inherit connection-management complexity they didn’t need.

The SSE failure modes nobody warns you about

Because this client opens an EventSource when you give it an http:// or https:// URL, you can reproduce the three classic Server-Sent Events failures here rather than guessing at them from your app. All three look identical from inside application code — “the stream just stops” — and have completely different fixes.

The stream connects but nothing arrives until it ends. Your proxy is buffering. Nginx buffers proxied responses by default, so it holds the event stream until the response completes — which for a long-lived stream is never. The fix is proxy_buffering off; on that location, or having the application send X-Accel-Buffering: no. If events appear here but not through your load balancer, this is almost always why.

The seventh tab never connects. Over HTTP/1.1 browsers cap concurrent connections per origin at six, and an open SSE stream holds one for its entire life. Six tabs of your dashboard and the seventh hangs with no error. HTTP/2 multiplexes over a single connection and raises the ceiling to the server’s stream limit, so this failure quietly disappears on HTTP/2 and reappears the moment something in the path downgrades.

It reconnects forever and you never notice. EventSource reconnects automatically — that is the feature — which means a server returning errors produces a silent retry loop rather than a visible failure. Watch the log here: a healthy stream shows one open and then messages; a broken one shows repeated opens. The server controls the interval with a retry: field, and if it has been sending id: values the browser replays the last one in a Last-Event-ID header on reconnect, which your handler must honour or clients will miss events across every reconnect.

None of this applies to WebSockets, which is the point: the two protocols fail in different places, and a tester that only speaks one of them can only show you half the picture.

Testing authenticated endpoints

Browsers don’t let WebSocket clients set arbitrary headers, so real-world auth lands in one of three places: a token in the query string (wss://api.example.com/feed?token=... — simplest, but tokens leak into server logs), a ticket pattern (fetch a short-lived ticket over HTTPS, pass it in the URL — the production-grade answer), or the Sec-WebSocket-Protocol field abused as a token carrier. When a connection authenticates in your backend tests but fails from this tester, the server is usually reading auth from a cookie or header your test setup sent implicitly — re-test with the token explicitly in the URL to confirm which mechanism the endpoint really uses.

Last updated Built and maintained by Necmeddin Cunedioglu How tools are tested

How helpful was this tool?

Click to rate

Key Concepts

Payload

The body of data sent in a webhook HTTP request, typically formatted as JSON. The payload contains the event details — for example, a GitHub push webhook payload includes the commit messages, author information, repository details, and the list of changed files.

HMAC Signature

A cryptographic signature (Hash-based Message Authentication Code) used to verify that a webhook payload was sent by the expected service and was not tampered with in transit. The sender computes HMAC-SHA256 of the payload using a shared secret, and the receiver recomputes it to verify authenticity.

Server-Sent Events (SSE)

A standard describing how servers can initiate data transmission towards clients once an initial client connection has been established. Unlike WebSockets, SSE is unidirectional (server to client).

Frequently Asked Questions

Does this tool receive actual webhook requests?

No. This is a client-side payload builder and validator. It helps you construct, format, and validate webhook payloads before sending them to your application for testing. For receiving live webhooks, you would need a server-side endpoint or a service like webhook.site.

What webhook templates are available?

The tool includes templates for common services: GitHub (push, pull request, issue events), Stripe (payment, subscription events), Slack (message, interactive events), and a generic JSON webhook template. You can also create fully custom payloads.

Can I validate that my payload matches a specific schema?

The tool validates JSON syntax and structure. It checks that your payload is valid JSON, displays it formatted for readability, and highlights any syntax errors. For schema-specific validation, you would use the JSON Schema Generator tool in combination.

How do I use the generated payload for testing?

Copy the generated payload and use it with cURL, Postman, or any HTTP client to send a POST request to your webhook endpoint. The cURL to Code tool can help you generate the exact request code in your preferred language.

What protocols does this tool support?

The tool supports both bi-directional WebSockets (ws://, wss://) and uni-directional Server-Sent Events/EventSource (http://, https://) connections.

Is my stream data logged on your servers?

No. The connection is established directly from your browser to your target server. We do not proxy, log, or inspect any of the payloads. It is 100% zero-knowledge.

Does it support authenticated streams?

Yes, if your authentication relies on query parameters (e.g. ?token=XYZ). However, custom headers cannot be set for native WebSocket connections in the browser.

Why must I verify signatures against the raw request body, not the parsed JSON?

Providers like Stripe and GitHub compute the HMAC signature over the exact bytes they sent. The moment you parse JSON and re-serialise it, key order, whitespace, and number formatting can change — and the recomputed HMAC no longer matches, even though the data is identical. Always capture the raw body before any JSON middleware touches it (in Express, that means a raw body parser on the webhook route), verify the signature against those bytes, and only then parse.

How should my endpoint respond so the provider doesn't keep retrying?

Return a 2xx status (usually 200 or 204) as fast as possible — ideally within a couple of seconds. Most providers treat any non-2xx, or a timeout, as failure and retry with backoff, which means slow processing causes duplicate deliveries. The pattern that scales: acknowledge immediately with 2xx, enqueue the event to a background worker, and do the heavy lifting (DB writes, emails, third-party calls) off the request path.

Why does my connection close immediately with code 1006?

Code 1006 means the connection died without a proper close handshake — the server refused the upgrade, a proxy or load balancer in between doesn't speak WebSocket, or a TLS/certificate problem killed the socket. Check that the endpoint actually accepts WebSocket upgrades (not just HTTP), and that any nginx/ALB in front forwards the Upgrade and Connection headers.

Why can't I connect to a ws:// URL from this page?

Browsers block insecure ws:// connections from pages served over HTTPS (mixed content). Use wss:// for any server with TLS. For local development servers without certificates, ws://localhost is the one exception browsers allow — connect to localhost directly rather than a LAN IP.

Troubleshooting & Technical Tips

Invalid JSON syntax in payload

Common JSON errors include trailing commas after the last property, single quotes instead of double quotes, unquoted property names, and missing commas between properties. The tool highlights the exact location of syntax errors to help you fix them.

Webhook signature verification fails

Many services (GitHub, Stripe) sign webhook payloads with HMAC-SHA256. The signature depends on the exact byte content of the payload — even whitespace changes will produce a different signature. When testing, ensure you use the raw (non-prettified) payload for signature computation.

Payload too large or deeply nested

Most webhook receivers have payload size limits (typically 5-25 KB). If your test payload is very large or deeply nested, it may be rejected. Keep test payloads realistic and within the size limits specified by the receiving service.

Connection Refused / Failed to Connect

Ensure the endpoint is publicly accessible or reachable from your current network. Check if the server requires specific Origin headers or blocks browser connections via CORS.

Cannot set custom headers

The native browser WebSocket API does not allow setting custom HTTP headers (like Authorization). Use query parameters for token authentication.

Related Guides

Related Tools

Embed this tool on your site

Paste this snippet into any HTML page or blog post to embed a live, fully working copy of Webhook & WebSocket Tester. Free for any use.