Documentation
docMay 18, 20262 min read

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

EventFires when
deliveryReceiving server accepted the message.
bounceHard or soft bounce reported by the receiving server.
openRecipient opened the email (tracking pixel loaded).
clickRecipient clicked a tracked link in the body.
complaintRecipient 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.
Tagswebhookseventsintegration