Prompt
How do I set up transactional email webhooks for bounces and deliveries?
Latest observation
Oct 10, 2026
OpenAI APIWeb search: off
To set up transactional email webhooks for bounces and deliveries, the general flow is:
- Create a webhook endpoint on your app/server
- Register that endpoint with your email provider
- Subscribe to bounce/delivery events
- Verify and process incoming webhook payloads
- Update your database/logs based on the event
1) Create a webhook endpoint
Expose an HTTPS route in your app, for example:
POST /webhooks/email
This endpoint should:
- accept
POSTrequests - parse JSON payloads
- respond quickly with
200 OK - handle retries/idempotency safely
2) Register the endpoint with your provider
Most transactional email services let you configure webhooks in their dashboard or API.
You’ll usually provide:
- the webhook URL
- a signing secret / verification token
- event types you want:
bounce,delivery(sometimes alsoopen,click,spamreport, etc.)
3) Subscribe to bounce and delivery events
Make sure the provider is configured to send the correct event types:
- Delivery: email successfully accepted by recipient’s mail server
- Bounce: email could not be delivered
Some providers distinguish:
- Hard bounce: permanent failure
- Soft bounce: temporary failure
4) Verify webhook authenticity
Always verify incoming webhook requests using:
- a signature header
- HMAC verification with a shared secret
- or provider-specific verification mechanism
This prevents spoofed webhook requests.
5) Process the event payload
Typical actions:
On delivery
- mark message as delivered
- store delivery timestamp
- update notification status
On bounce
- mark address as invalid or risky
- record bounce reason/code
- suppress future sends to that address if it’s a hard bounce
Example handler
Here’s a simple example in Node.js/Express:
import express from "express";
const app = express();
app.use(express.json());
app.post("/webhooks/email", (req, res) => {
const event = req.body;
switch (event.type) {
case "delivery":
console.log("Delivered:", event.message_id);
break;
case "bounce":
console.log("Bounced:", event.message_id, event.reason);
break;
default:
console.log("Unhandled event:", event.type);
}
res.sendStatus(200);
});
app.listen(3000);
Recommended best practices
- Use HTTPS only
- Return 2xx quickly
- Store event IDs to prevent duplicate processing
- Log raw payloads for debugging
- Handle retries gracefully
- Suppress hard-bounced addresses
Example payload shape
A provider might send something like:
{
"type": "bounce",
"message_id": "abc123",
"email": "user@example.com",
"reason": "Mailbox full",
"timestamp": "2026-10-10T12:34:56Z"
}
For delivery:
{
"type": "delivery",
"message_id": "abc123",
"email": "user@example.com",
"timestamp": "2026-10-10T12:35:10Z"
}
If you tell me which email provider you’re using (SendGrid, Mailgun, Amazon SES, Postmark, SparkPost, etc.) and your backend language, I can give you exact setup steps and code.