Skip to main content Skip to main content

How to Test SendGrid Webhooks

· 4 min read

When you send transactional emails through SendGrid, you need to know what happens to them after they leave your server. Did the email bounce? Did the user click a link? Did they mark it as spam?

SendGrid provides this data via Event Webhooks. Instead of polling their API for delivery status, SendGrid actively pushes events to your server. However, their payload structure is unique, which can make testing tricky if you are not prepared.

The SendGrid payload: Arrays, not objects

The most important thing to know about SendGrid webhooks is that the payload is almost always a JSON array, not a single JSON object.

Because email infrastructure generates massive amounts of data very quickly, SendGrid batches events together. Every HTTP POST request they send to your endpoint will contain an array of multiple distinct events.

For example, a single webhook might contain a "delivered" event for User A, a "bounce" event for User B, and a "click" event for User C, all in the same payload:

[
  {
    "email": "[email protected]",
    "event": "delivered",
    "sg_event_id": "sendgrid_internal_id_1"
  },
  {
    "email": "[email protected]",
    "event": "bounce",
    "reason": "550 5.1.1 The email account that you tried to reach does not exist.",
    "sg_event_id": "sendgrid_internal_id_2"
  }
]

Your webhook handler must loop over this array and process each event individually. If your code expects a root-level JSON object instead of an array, it will immediately crash.

Capturing real email events

Stop reading raw JSON

Payloader shows you what your SendGrid webhook actually did — in plain English. See the event, amount, status, and more at a glance.

Start free trial →

Before writing the loop to process these events, you need to capture a real payload to see exactly what fields SendGrid includes.

The easiest way to do this is with Payloader:

  1. Create a new endpoint URL in Payloader.
  2. Log in to SendGrid, go to Settings > Mail Settings > Event Webhook.
  3. Enable the webhook, paste your Payloader URL, and check the boxes for the specific events you want to track (e.g., Delivered, Bounced, Opened).

Now, send a test email through your application.

When SendGrid fires the webhook, Payloader will capture it. Because Payloader understands SendGrid's batching structure, it will immediately summarize the array, showing you how many events are in the payload and highlighting the specific email addresses and event types. You do not have to visually untangle the raw JSON array.

Local development workflow

Writing the logic to handle these events requires testing against your local database. You need to ensure that a "bounce" event actually updates the user's status in your system so you stop emailing them.

Use Payloader's forwarding feature to proxy the incoming SendGrid webhook to your local server. Once the payload reaches your local environment, you can step through your array-processing logic with a debugger.

If your logic fails on a specific bounce event, do not trigger a new test email. Just click "Replay" on the captured webhook in Payloader to run the exact same batch of events back through your code.

Securing the Event Webhook

SendGrid supports signed webhooks to verify that the data actually came from them. They use Elliptic Curve Digital Signature Algorithm (ECDSA).

When you enable signature verification in SendGrid, they provide you with a public key. Every webhook they send will include two custom headers: X-Twilio-Email-Event-Webhook-Signature and X-Twilio-Email-Event-Webhook-Timestamp.

Your server must combine the timestamp and the raw JSON payload body, and then use the provided public key to mathematically verify the signature header. If the verification fails, you must reject the payload to prevent attackers from injecting fake "click" or "bounce" data into your analytics.