Receiving webhooks
Subscribe to delivery, bounce, open, click, and complaint events. Verify signatures and handle retries.
By SESMetric Docs
Webhooks let your system react to email events in real time — flip a user record to "verified" on first delivery, suppress a bouncing address, or surface complaints to your CS team.
Webhooks are a paid feature. Configure endpoints at Dashboard → Webhooks.
1. Event types
| Event | Fires when |
|---|---|
delivery | Receiving server accepted the message. |
bounce | Hard or soft bounce reported by the receiving server. |
open | Recipient opened the email (tracking pixel loaded). |
click | Recipient clicked a tracked link in the body. |
complaint | Recipient flagged the message as spam. |
2. Register an endpoint
In Dashboard → Webhooks click Add webhook:
- URL — must be HTTPS in production.
- Description — free-form.
- Events — check the events you care about.
We generate a unique signing secret for each endpoint. Save it; you'll need it to verify signatures.
3. Payload format
Each delivery POSTs JSON:
{
"event_type": "delivery",
"user_id": 42,
"payload": {
"sm_mail_id": "msg_a7f0c12e9bd4",
"messageId": "0107018f...",
"subject": "Welcome",
"source": "no-reply@example.com",
"recipients": ["alex@customer.com"],
"eventTimestamp": "2026-05-19T14:23:08Z",
"smtpResponse": "250 2.0.0 Ok: queued"
}
}
The payload shape matches the event schema — varies slightly per event type.
4. Signature verification
Each request includes X-SM-Signature: sha256=<hex>. Verify before processing:
import hmac, hashlib
def verify(secret: str, raw_body: bytes, header: str) -> bool:
if not header.startswith("sha256="):
return False
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, header.split("=", 1)[1])
Always compute the HMAC over the raw request body — not the parsed JSON — and use a constant-time comparison.
5. Responding
- Return any 2xx status within 10 seconds. We treat the event as delivered.
- Anything else (timeout, 4xx, 5xx) triggers a retry.
6. Retries
Failed deliveries retry on a backoff: 30s, 5m, 30m, 2h, 6h. After five total failures the event is abandoned and recorded in the audit log at Dashboard → Webhooks → (endpoint) → Deliveries.
7. Testing
The Send test event button on an endpoint posts a synthetic delivery payload with payload.sm_mail_id = "msg_test". Use it to confirm your verifier and your 2xx response are working before going live.
8. Best practices
- Be idempotent. Retries may produce duplicate deliveries; key off
payload.sm_mail_id + event_type. - Queue, then 2xx. Don't do heavy work synchronously inside the handler — push to a queue and respond fast.
- Subscribe to bounces. Add bouncing addresses to your suppression list immediately to protect your sender reputation.