Test SendGrid Webhooks Locally (SendGrid Event Webhook on localhost)

Test the SendGrid Event Webhook locally on localhost without deploying. Inspect the real JSON event array, forward to your handler, and verify the signed webhook signature.

You are building a SendGrid integration — updating delivery status, tracking opens and clicks, suppressing addresses that bounce — and you need to see your handler react to a real batch of email events. The problem is immediate: SendGrid will only POST to a public URL, and your handler is running on localhost:8080. SendGrid has no way to reach it.

The usual workarounds are painful. Deploying to a staging server for every code change is slow. Pasting payloads from the docs into curl gives you a guess at the real request, not the real headers and body SendGrid actually sends. What you want is to test SendGrid webhooks locally — real events, hitting your local handler, with a URL that does not change every time you restart.

This guide shows how to do exactly that.

Why testing SendGrid webhooks locally is tricky

The SendGrid Event Webhook is just an HTTP request that SendGrid sends to a URL whenever your email moves through its pipeline — processed, delivered, opened, clicked, bounced. SendGrid sits on the public internet; your dev machine usually does not. It is behind a router, a corporate firewall, or both, with no public IP and no inbound ports open.

So you need something in the middle: a public endpoint SendGrid can hit, that relays each request down to your laptop without you opening a single firewall port. That is what Webhook Relay does — and unlike a random tunnel URL, the endpoint is stable, so you configure SendGrid once and never touch it again.

Step 1: Inspect the real payload with Webhook Bin

Before you write any handler code, find out what SendGrid actually sends. Open the free Webhook Bin — no signup — and you get an instant public URL.

  1. Copy the Webhook Bin URL.
  2. In the SendGrid dashboard, go to Settings → Mail Settings → Event Webhook (in some accounts this lives under Settings → Tracking).
  3. Paste the URL into the HTTP POST URL field and select the events you care about — processed, delivered, open, click, bounce, dropped, deferred, spamreport, unsubscribe.
  4. Toggle the Event Webhook on and save.

SendGrid provides a Test Your Integration button that POSTs a sample batch immediately — you will see it land in Webhook Bin right away. Inspect the captured request and note the most important detail: the body is a JSON array of event objects, not a single object. Each object has fields like email, event, timestamp, sg_event_id, sg_message_id, and (for engagement events) a category. The headers include X-Twilio-Email-Event-Webhook-Signature and X-Twilio-Email-Event-Webhook-Timestamp when signing is enabled.

Now you know the exact shape of the data before writing a line of code. For more on this approach, see How to test webhooks and What is a webhook.

Step 2: Forward the events to localhost with the relay agent

Once you know the payload, route those same events into your local handler. Sign up for Webhook Relay, install the relay agent (CLI or Docker), and create a bucket — say sendgrid. The bucket gives you a stable public input endpoint.

Start forwarding to your local server:

relay forward --bucket sendgrid http://localhost:8080/webhook

The agent opens an outbound connection to Webhook Relay and streams every incoming request down to http://localhost:8080/webhook. Because the connection is outbound, there are no firewall ports to open and no public IP needed — this works from your laptop, behind a corporate proxy, or inside a Kubernetes cluster. Running in Docker? The same command works in the official webhookrelay/webhookrelayd image. Full details are in the localhost forwarding docs.

Now update the Event Webhook's HTTP POST URL to your Webhook Relay endpoint (or just create it there from the start). Click Test Your Integration again — or send yourself a real email — and watch the event batch arrive on localhost.

SendGrid-specific configuration and quirks

A few SendGrid details worth knowing:

  • Where to add it: the Event Webhook is configured under Settings → Mail Settings → Event Webhook (or Settings → Tracking in older accounts). Newer accounts can also create multiple Event Webhooks via the API.
  • Batched JSON array: SendGrid groups multiple events into a single POST as a JSON array. Your handler must iterate the array — a parser that expects one object per request will silently drop events.
  • Event types: subscribe only to what you handle. Delivery events (processed, delivered, bounce, dropped, deferred) and engagement events (open, click, unsubscribe, spamreport) are toggled separately.
  • Deduplicate on sg_event_id: SendGrid retries on non-2xx responses, so the same event can arrive more than once. Use sg_event_id for idempotency and return 2xx quickly.
  • Inbound Parse is different: the Inbound Parse webhook is a separate feature for receiving inbound email — it POSTs multipart/form-data to your endpoint, not the JSON event array. You can forward it through the same Webhook Relay bucket, but parse it differently.

Step 3: Verify the Signed Event Webhook signature

SendGrid's signature feature is ECDSA, not HMAC — an important distinction if you have only ever verified HMAC webhooks. When you enable the Signed Event Webhook in Mail Settings, SendGrid generates an elliptic-curve key pair, signs each request with the private key, and gives you a public verification key in the dashboard.

Each request carries the signature in the X-Twilio-Email-Event-Webhook-Signature header and a timestamp in X-Twilio-Email-Event-Webhook-Timestamp. To verify, you reconstruct the signed payload as the timestamp concatenated with the raw request body, then check the ECDSA signature against your public key. As always, use the raw bytes of the body — re-serialized JSON will not match. SendGrid's official libraries ship an eventwebhook helper that wraps this for you.

Because this is ECDSA rather than a shared-secret HMAC, the generic HMAC signature verifier does not apply directly to SendGrid's signed webhook — but it remains a handy tool for the many HMAC-based providers. For the broader concepts, language-specific code, and the common pitfalls (reading the body after a JSON parser has consumed it, forgetting the timestamp, timing-safe comparison), read Verify a webhook signature.

Replay and iterate

This is where local development gets fast:

  • Replay from SendGrid by hitting Test Your Integration again, or by sending a fresh test email.
  • Replay from Webhook Relay — past requests are stored on your bucket, so you can resend a captured event batch without touching SendGrid at all.
  • Iterate on your handler by editing code and replaying the same delivery until it behaves correctly. No commits, no pushes, no deploys just to test a code path.

Because the Webhook Relay endpoint is stable, you can stop and restart the agent, reboot your machine, or come back next week — the SendGrid configuration never needs to change.

Get started

  1. Inspect the real payload in the free Webhook Bin — no signup needed.
  2. Create a Webhook Relay account, install the agent, and run relay forward --bucket sendgrid http://localhost:8080/webhook.
  3. Point your SendGrid Event Webhook's HTTP POST URL at the stable endpoint, click Test Your Integration, and watch the event batch hit localhost.

You will be testing real SendGrid events against your local handler in a few minutes — no deploys, no open firewall ports, and a URL you configure exactly once.