Provider guide

SendGrid webhooks, kept properly.

SendGrid batches. One POST can carry dozens of events for dozens of recipients, which makes a single delivery worth far more than it looks and a lost one worth far more than you would expect.

Field notes

How SendGrid behaves, and where it bites

Many events per request, signed with a key pair rather than a shared secret.

How SendGrid sends

SendGrid POSTs a JSON array of event objects, each with its own event and recipient, and signs the request with ECDSA in X-Twilio-Email-Event-Webhook-Signature alongside a timestamp header.

Where it bites

  • One delivery is many events. Losing a single POST can mean losing dozens of bounces or opens, and none of them will be resent individually.
  • It is a key pair, not a secret. You hold the public verification key, which trips up anything expecting an HMAC secret.
  • There is no one event name for the request. The array holds a mix, so labelling and searching happen inside the body rather than at the top of it.

The vault under it

Point a WebhookVault endpoint at SendGrid and every delivery is stored in full: headers verbatim, bodies byte-for-byte, labelled with its event names and searchable. Forward it onward to your own handler if you want the relay pattern, replay any delivery once the bug is fixed, and let a Watchdog notice the day it stops arriving.

01

Create an endpoint

Name it sendgrid-events. Turn on forwarding to your own handler if you want the vault in front of it rather than beside it.

02

Register the capture URL

SendGrid → Settings → Mail Settings → Event Webhook. Paste the capture URL, choose the events, and enable signature verification.

03

Keep everything

Every delivery from here on is on the shelf: searchable, replayable, and watched for the day it goes quiet.

Verify from the record, not just at the door

SendGrid signs the timestamp followed by the body with an ECDSA key pair, and publishes the public half for you to check against. The vault verifies every capture with it.

The vault stores the signature headers verbatim with every request, so a delivery from last month can still be checked today. Verdicts are part of Solo and up. How verification works →

captured · 3 events, one POST✓ verified
# headers, stored verbatim
"X-Twilio-Email-Event-Webhook-Signature": "MEUCIQ…"
"X-Twilio-Email-Event-Webhook-Timestamp": "1757030400"

# body is an ARRAY, one object per event
[
  { "email": "[email protected]", "event": "delivered" },
  { "email": "[email protected]", "event": "bounce",
    "reason": "550 5.1.1 unknown recipient" },
  { "email": "[email protected]", "event": "open" }
]

Event catalogue

SendGrid webhook events you'll see

A few of the event names SendGrid sends, not the full list: its own documentation has every one. A single POST carries an array of them, so the vault labels each capture with the names inside.

EventWhat it tells you
deliveredThe receiving server accepted the message.
deferredThe receiving server delayed the message; SendGrid will try again.
bounceThe receiving server refused the message permanently.
droppedSendGrid did not send it, for example because the address is on a suppression list.
spamreportThe recipient marked the message as spam.
unsubscribeThe recipient unsubscribed.

Questions

SendGrid webhook questions, answered

The three that come up most when SendGrid is wired to a new endpoint.

Why does one SendGrid POST contain many events?

The Event Webhook batches. A single request is a JSON array with one object per event, often for several recipients. That is why one lost POST can mean dozens of lost bounces or opens, and why the vault stores the whole array as one capture.

How do I verify the SendGrid Event Webhook signature?

Turn on signature verification in Mail Settings, copy the public key SendGrid shows you, and verify the ECDSA signature in X-Twilio-Email-Event-Webhook-Signature over the timestamp header followed by the raw body. Use the exact bytes received: re-serialised JSON or trimmed whitespace fails. On Solo and up the vault does this at capture.

How does SendGrid retry a failed Event Webhook?

If your endpoint does not answer with a 2xx, SendGrid keeps retrying the batch for up to 24 hours. A WebhookVault capture URL answers as soon as the batch is stored, so what SendGrid sent is kept even while your own handler is failing.

One POST, dozens of events, all kept.

Free tier, no card. Every batch stored whole; search inside it starts on Pro.