Pour tester un SendGrid Event Webhook sur localhost, exécutez votre gestionnaire localement, exposez son port avec npx portpreview PORT, entrez le point de terminaison HTTPS résultant comme L'URL de publication de SendGrid et vérifiez chaque demande avec la clé publique Signed Event Webhook avant de traiter ses événements.
Ce que le SendGrid Event Webhook envoie
Le Event Webhook rapporte ce qui se passe après que le SendGrid accepte un message. Les événements de délivrabilité incluent processed, delivered, deferred, bounce et dropped. Les événements d'engagement incluent open, click, les rapports de spam et les modifications d'abonnement. Les champs exacts varient selon le type d'événement, donc acheminez principalement sur event et traitez les champs facultatifs comme facultatifs.
A est un JSON array, pas nécessairement un objet. SendGrid peut placer plusieurs événements dans un seul POST. Un gestionnaire qui suppose que req.body.event manquera silencieusement le lot. La référence officielle Event Webhook documente les noms et les champs des événements, notamment sg_event_id et sg_message_id.
Utilisez les événements comme des faits et non comme des commandes. Par exemple, un événement delivered peut mettre à jour l'état du message, tandis qu'un événement click peut ajouter un enregistrement d'engagement. Évitez de faire en sorte qu'un gestionnaire click écrase un état de désabonnement ultérieur simplement parce que les demandes sont arrivées dans le désordre.
1. Create a local endpoint
This Express example deliberately applies a raw-body parser only to the SendGrid route. Signature verification depends on the exact bytes SendGrid signed; parsing and re-serializing JSON can change those bytes.
import express from 'express';
import { EventWebhook, EventWebhookHeader } from '@sendgrid/eventwebhook';
const app = express();
const verifier = new EventWebhook();
const publicKey = verifier.convertPublicKeyToECDSA(
process.env.SENDGRID_WEBHOOK_PUBLIC_KEY,
);
app.post(
'/webhooks/sendgrid',
express.raw({ type: 'application/json' }),
async (req, res) => {
const signature = req.get(EventWebhookHeader.SIGNATURE());
const timestamp = req.get(EventWebhookHeader.TIMESTAMP());
if (!signature || !timestamp || !verifier.verifySignature(
publicKey,
req.body,
signature,
timestamp,
)) {
return res.status(403).send('invalid signature');
}
let events;
try {
events = JSON.parse(req.body.toString('utf8'));
} catch {
return res.status(400).send('invalid JSON');
}
if (!Array.isArray(events)) {
return res.status(400).send('expected an event array');
}
await enqueueNewEvents(events);
return res.sendStatus(204);
},
);
app.use(express.json());
app.listen(3000);
Installez l'assistant officiel avec npm install @sendgrid/eventwebhook. Montez global express.json() après cette route, ou excluez explicitement ce chemin. La même règle s'applique dans les passerelles Next.js, Fastify, NestJS, les fonctions sans serveur et API : conservez le corps d'origine sous forme de chaîne ou de tampon d'octets jusqu'à ce que la vérification réussisse. Le référentiel officiel de nœuds SendGrid contient un exemple signé Event Webhook correspondant..
2. Give SendGrid an HTTPS URL
Keep the application running, then open a second terminal:
npx portpreview 3000
PortPreview prints a public HTTPS origin. If it is https://example.portpreview.dev, the complete Post URL is:
https://example.portpreview.dev/webhooks/sendgrid
The path must match the route exactly. Keep the tunnel process alive while testing. A tunnel forwards traffic; it does not replace your local server, so connection failures usually mean the app is stopped, listening on another port, or bound in a way the tunnel cannot reach.
3. Configurez le Event Webhook dans SendGrid
- Dans l'interface utilisateur SendGrid, open Paramètres > Paramètres de messagerie.
- Sous Paramètres du Webhook, open Event Webhooks et choisissez Créer un nouveau webhook.
- Activez-le, ajoutez l'URL PortPreview comme URL de publication et sélectionnez uniquement les actions dont votre application a besoin.
- Sous Fonctionnalités de sécurité, activez Signed Event Webhook.
- Enregistrez le webhook, reopen ses paramètres, copiez la clé de vérification publique générée et stockez-la sous
SENDGRID_WEBHOOK_PUBLIC_KEY. - Utilisez Test Your Integration, puis envoyez un message réel pour exercer les types d'événements qui comptent.
SendGrid
note que le test envoie des exemples d'événements plutôt que des données provenant d'un véritable envoi de courrier. Enregistrer avant de tester la vérification signature : la paire de clés est générée lorsque la configuration Signed Event Webhook est enregistrée.Comment fonctionne la vérification du webhook signé par SendGrid
Signed Event Webhook utilise ECDSA. SendGrid conserve la clé privée et vous affiche la clé de vérification publique correspondante. Chaque livraison comprend X-Twilio-Email-Event-Webhook-Signature et X-Twilio-Email-Event-Webhook-Timestamp. La vérification couvre le timestamp concaténé avec les octets de charge utile brute et un hachage SHA-256 ; le signature est codé en Base64. L'assistant officiel gère la conversion de clé publique, le décodage signature, le hachage et la vérification ECDSA.
Il s'agit d'une vérification asymétrique : la valeur affichée est une clé publique, pas un secret HMAC. N'exécutez pas la charge utile via JSON.stringify(), ne supprimez pas les espaces, n'ajoutez pas de nouvelle ligne ou ne vérifiez pas un élément du tableau à la fois. Vérifiez d'abord les octets complets de la requête, puis analysez le tableau. Voir la documentation des fonctionnalités de sécurité de SendGrid pour l'algorithme et les en-têtes.
A signature valide établit que les octets signés proviennent du détenteur de la clé privée de SendGrid et n'ont pas été modifiés. Il ne rend pas le traitement des événements idempotent, n'autorise pas des actions arbitraires et ne prouve pas qu'un événement est nouveau. Ce sont des contrôles distincts.
Make batch processing idempotent
SendGrid retries failed POSTs, and networks can lose a successful response. Therefore, duplicate delivery is normal. Use each event's sg_event_id as the primary deduplication key, with a unique database constraint. If your product combines multiple SendGrid accounts or environments, namespace the key by provider and account or environment.
async function enqueueNewEvents(events) {
for (const event of events) {
await db.transaction(async (tx) => {
const inserted = await tx.webhookReceipts.insertIfAbsent({
provider: 'sendgrid',
eventId: event.sg_event_id,
receivedAt: new Date(),
});
if (!inserted) return;
await tx.jobs.enqueue({
type: 'process-sendgrid-event',
payload: event,
});
});
}
}
L'insert de reçu et la mise en file d'attente durable doivent être validés ensemble. Ne renvoyez que 2xx après que le lot soit durablement accepted. Si un événement échoue après la validation d'autres événements, une réponse non-2xx peut entraîner le retour de l'intégralité de la requête ; La déduplication permet à la prochaine tentative d'ignorer les événements déjà accepted et de continuer en toute sécurité. N'utilisez pas un Set en mémoire en production, car les redémarrages l'effacent et plusieurs instances ne le partagent pas. Le guide plus large de tentatives de webhook et d'idempotence couvre les modèles durables.
Comprendre les tentatives avant de choisir les codes d'état
Selon la documentation Event Webhook de SendGrid, une réponse 2xx marque le succès du POST. Une réponse autre que 2xx entraîne des tentatives à intervalles croissants jusqu'à 24 heures après l'événement ; il s'agit d'une fenêtre continue pour chaque nouvel événement défaillant. Ce comportement signifie qu'un échec permanent du signature peut également générer des tentatives répétées, tandis que le renvoi de 2xx pour un événement que vous n'avez jamais stocké le perd.
- 2xx: le lot complet a été authentifié et durablement accepted, ou chaque événement est déjà connu.
- 4xx : entrée mal formée ou non authentifiée. Enregistrez uniquement les diagnostics sécurisés ; attendez-vous au comportement général de nouvelle tentative non-2xx de SendGrid.
- 5xx : un échec passager de base de données, de file d'attente ou d'application qui doit être réessayé.
Gardez le chemin de la requête court : vérifiez, validez la forme extérieure, dédoublonnez et mettez en file d'attente de manière atomique, puis répondez. Effectuez des mises à jour d'analyse des e-mails, une synchronisation CRM et des notifications dans les travailleurs.
Dépannage des webhooks SendGrid locaux
Le signature est toujours invalide
La cause la plus courante est que le middleware JSON consomme le corps avant la vérification. Confirmez que le vérificateur reçoit le Buffer d'origine, y compris tout espace de début ou de fin. Vérifiez ensuite que la clé publique appartient à cette configuration exacte Event Webhook et que les deux en-têtes Twilio atteignent l'application sans modification. Redémarrez le processus local après avoir modifié son environnement.
L'intégration du test réussit, mais les événements réels n'apparaissent pas
Vérifiez que le webhook est activé et que les actions souhaitées sont sélectionnées. Les ouvertures nécessitent un suivi open et les click nécessitent un suivi click. N'oubliez pas non plus que la demande de test contient des exemples ; utiliser un envoi réel pour valider les champs et le séquençage de type production.
Le point de terminaison renvoie 404 ou 502
Pour 404, comparez le chemin configuré avec /webhooks/sendgrid. Pour les erreurs de passerelle, assurez-vous que l'application locale s'exécute sur le même port transmis à PortPreview. Si les demandes arrivent mais renvoient 500, inspectez les journaux locaux et réduisez temporairement le gestionnaire à la vérification et à la capture durable.
Les événements sont dupliqués ou hors service
Il s’agit d’une réalité du système de livraison, et non d’une preuve que le tunnel a dupliqué le trafic. Dédupliquez par sg_event_id, rendez les transitions d'état monotones lorsque cela est possible et stockez l'heure de l'événement séparément de l'heure de réception. Utilisez le workflow de débogage des webhooks locaux pour isoler les échecs de transport, d'authentification et de logique métier.
Liste de contrôle de sécurité pour une utilisation locale et en production
- Utilisez HTTPS et vérifiez chaque signature avant d'analyser ou de consigner les détails de l'événement.
- Conservez la clé de vérification publique dans la configuration afin qu'elle puisse être mise à jour proprement lorsque la clé du webhook change.
- Accceptez POST uniquement, limitez la taille de la requête, validez que la valeur analysée est un tableau et autorisez uniquement les noms d'événements que vous gérez.
- Ne placez pas les informations personnelles dans les catégories SendGrid ou dans les arguments uniques ; La référence de SendGrid avertit explicitement que ces champs sont stockés et ne sont pas traités comme des informations personnelles.
- N'exposez pas une session d'administration, une console de débogage ou des routes locales non liées via la même origine temporaire.
- N'enregistrez pas les adresses des destinataires, les charges utiles, les signature ou les valeurs d'environnement, sauf si cela est nécessaire et correctement rédigé.
- Remplacez l'URL du tunnel temporaire par un point de terminaison de production stable HTTPS après le test et désactivez les configurations de webhook obsolètes.
SendGrid peut également utiliser le OAuth 2.0 pour la sécurité du Event Webhook, seul ou aux côtés des signature. Si votre déploiement nécessite des contrôles de cycle de vie Bearer-token, suivez le guide de sécurité officiel plutôt que d'inventer un échange token. La vérification de la signature reste précieuse car elle lie le timestamp exact et les octets de charge utile.
A test d'acceptation prêt pour la production
- Envoyez une demande de test signée et confirmez une réponse 2xx.
- Modifiez un octet de charge utile et confirmez un 403 sans écriture dans la base de données.
- Rejouez la demande valide identique et confirmez qu'il n'y a pas de tâche ou d'action commerciale en double.
- Envoyer un objet JSON au lieu d'un tableau et confirmer un 400 contrôlé.
- Arrêtez brièvement la base de données, confirmez un 5xx, restaurez-la et vérifiez qu'une nouvelle tentative est accepted une fois.
- Envoyez un véritable e-mail et confirmez que les événements de livraison et d'engagement sélectionnés suivent le même chemin.
OUne fois ces contrôles réussis, déplacez le point final en production sans modifier la logique de vérification et d'idempotence. Pour des modes de défaillance cryptographique plus approfondis, lisez le guide de vérification webhook signature.
