Email webhooks let your application receive delivery, bounce, complaint, open, and click events as they happen. Instead of repeatedly asking an email provider whether anything changed, your system exposes an HTTPS endpoint and processes event notifications pushed to it.

For transactional email, webhooks turn sending from a one-way request into an observable workflow. A REST API call or SMTP relay submission tells you that a provider accepted a message for processing; subsequent email webhooks help you understand what happened later in the delivery path.

What are email webhooks?

A webhook is an HTTP callback: when a selected event occurs, a service sends an HTTP request, usually a POST, to a URL you control. With email webhooks, the triggering events relate to a message lifecycle, such as a message being accepted, delivered, deferred, bounced, marked as spam, opened, or clicked.

The receiving application is commonly called the consumer, listener, or webhook endpoint. The email platform is the producer or sender. The producer does not wait for a user to refresh a dashboard or for your application to poll an API. It initiates the notification when it records an event.

A simplified flow looks like this:

  1. Your app sends a password-reset email through an email API or SMTP relay.
  2. The provider accepts the message and attempts delivery to the recipient domain.
  3. A receiving mail server accepts, temporarily defers, or rejects the message.
  4. The provider emits an event and sends JSON to your webhook endpoint.
  5. Your endpoint verifies the notification, stores it, queues follow-up work, and returns a successful HTTP response.

A webhook is not the email itself and it is not a replacement for the send API. It is a separate event channel. Sending and event processing should be designed as related but independent parts of your system.

Why webhooks are better than polling for email events

Polling means your application periodically calls an endpoint such as /messages, /activity, or /events to ask whether an email status changed. It can work for occasional reporting, but it is usually a poor primary mechanism for operational workflows.

Polling creates an unavoidable trade-off. Poll frequently and you waste requests, consume rate-limit capacity, and process many empty responses. Poll less often and information arrives late. A bounce that should suppress an address immediately might remain unnoticed for minutes or hours.

Email webhooks are event-driven. They allow a provider to push data after an event occurs, which has several benefits:

  • Lower latency: A hard bounce can be added to a suppression workflow quickly.
  • Less API traffic: Your application does not need to request unchanged data repeatedly.
  • Better automation: Events can trigger CRM updates, incident alerts, analytics pipelines, or account-security actions.
  • More accurate operational state: A timeline can combine send acceptance, delivery attempts, deferrals, bounces, and recipient engagement.
  • Cleaner architecture: A dedicated endpoint becomes the boundary between external delivery infrastructure and internal business logic.

That said, webhooks do not guarantee that every event will arrive exactly once or in perfect order. A robust integration treats them as an at-least-once delivery stream unless the provider explicitly documents stronger guarantees. That design assumption has consequences for storage, retry handling, and idempotency.

The email event lifecycle

An email message does not have one universal status. Different systems observe different stages, and labels vary by provider. A practical model separates submission, provider processing, remote-server response, and recipient interaction.

Submission and provider acceptance

When your application sends by REST API, a successful HTTP response generally means the provider accepted the request. A 200 OK, 201 Created, or 202 Accepted response can all represent success depending on the API contract. 202 Accepted is especially important: it means the request was accepted for processing, not necessarily that the message has reached the recipient mailbox.

The equivalent SMTP interaction is often a 250 reply after the message data is accepted. Again, this confirms acceptance by the SMTP server you connected to, not inbox placement at the final destination.

Store the provider’s message identifier alongside your own business identifier, such as order_id, user_id, or password-reset token record. This correlation is essential when a later webhook reports a delivery result.

Delivery and delayed delivery

A delivered event usually indicates that the receiving mail server accepted the message. It does not prove a human saw it, that it landed in the inbox rather than a spam folder, or that a mailbox rule did not immediately move it.

A deferred, delayed, or soft bounce event usually means the receiving system did not accept delivery yet but indicated that a later attempt may succeed. Common causes include temporary recipient-server problems, rate limiting, mailbox quotas, or temporary policy restrictions.

SMTP’s three-digit reply code helps distinguish success from failures. A 250 reply represents successful completion. Codes beginning with 4, such as 421 or 450, generally signal a transient negative response, while codes beginning with 5, such as 550 or 554, generally signal a permanent negative response. Enhanced status codes can add detail, for example 5.1.1 is commonly associated with an invalid or unknown recipient mailbox.

Bounces, complaints, and suppression

A hard bounce is a permanent failure for the attempted delivery. An invalid recipient address, non-existent mailbox, or recipient-domain policy rejection may create one. A permanent event should usually prevent automatic future sends to that address until reviewed or corrected.

A soft bounce is temporary. Do not immediately delete a recipient or permanently suppress every address after one temporary failure. Instead, rely on provider retry behavior, retain the event history, and establish escalation rules for repeated failure patterns.

A spam complaint occurs when a recipient flags a message as unwanted through a mailbox provider’s feedback mechanism. Treat complaints seriously. For marketing mail, remove or suppress the recipient immediately. For transactional mail, investigate why the message was unexpected, whether it contained promotional content, whether account activity was fraudulent, or whether the address was entered incorrectly.

Opens and clicks

Open and click events are engagement signals, not delivery guarantees. Open tracking commonly depends on a small remote image being requested; privacy features, image blocking, message forwarding, caching, and security scanners can distort results. Link-scanning systems can also create apparent clicks before a person opens the message.

Use opens and clicks for broad product analytics and non-critical engagement workflows, not as proof that a user personally read, approved, or completed a legally significant action. For security-sensitive actions, track the action in your own application, such as a verified password reset or completed login challenge.

A practical webhook payload model

Webhook JSON schemas differ, but reliable email event payloads usually include an event name, a stable event identifier, a timestamp, a message identifier, recipient information, and metadata about the response or action.

Here is an illustrative payload shape. Field names are examples, not a universal provider contract:

{
  "id": "evt_01JX8M9X2B9ZQ7R4K2P6",
  "type": "email.delivered",
  "created_at": "2026-10-22T14:23:08Z",
  "data": {
    "message_id": "msg_01JX8M6D7E4C2A9F",
    "to": "alex@example.net",
    "from": "receipts@example.com",
    "subject": "Your receipt #4821",
    "tags": ["receipt", "order-4821"],
    "metadata": {
      "order_id": "4821",
      "customer_id": "cus_93817"
    },
    "smtp_response": "250 2.0.0 Accepted"
  }
}

Do not build your handler around the exact example above. Read the provider’s published event schema, distinguish required from optional fields, and retain the raw request body for a limited period when appropriate for debugging. If your sending stack includes both REST API and SMTP relay options, normalize provider message IDs, internal IDs, tags, and metadata so either submission method feeds the same event-processing model. Provider-specific setup and request details belong in the relevant email API reference and setup guides.

Use your own correlation keys

A provider’s message ID is useful, but it should not be the only identifier you retain. Include a safe internal correlation value when sending, such as an order ID, notification ID, or tenant-scoped event ID. Avoid putting secrets, raw authentication tokens, health information, or unnecessary personal data into tags or metadata because that data can be copied into logs, webhook payloads, support tickets, and analytics systems.

For example, notification_id: "ntf_8d0c" is better than embedding a password-reset token. Your database can map the notification ID to the sensitive record under your own access controls.

Building a reliable webhook endpoint

The best webhook endpoint does very little during the request itself. Its first job is to authenticate the request, deduplicate it, persist or enqueue it durably, and return a success response quickly. Expensive downstream work should happen asynchronously.

Receive over HTTPS

Use a public HTTPS URL with a valid TLS certificate, for example:

https://api.example.com/webhooks/email

Do not use plain HTTP for production webhooks. HTTPS protects data in transit and prevents many straightforward interception risks. Your endpoint should accept the HTTP method and content type documented by the provider, commonly POST and application/json.

Avoid redirecting webhook requests. While HTTP has 301, 302, 307, and 308 redirect codes, webhook providers vary in whether they follow redirects and how they preserve request methods and bodies. Configure the final HTTPS URL directly instead.

Acknowledge quickly

After successfully accepting an event for processing, respond with a 2xx status, commonly 200 OK or 204 No Content. A 204 response is convenient when you have no response body.

Do not wait for slow database reports, CRM synchronization, fraud checks, or email-verification calls before responding. If processing exceeds the provider timeout, the provider may conclude delivery failed and retry even if your application eventually completed the work. That can create duplicates and unnecessary load.

A dependable request path is:

  1. Read the unmodified raw request body.
  2. Validate the signature, timestamp, and expected headers.
  3. Parse the JSON only after authentication requirements are met.
  4. Check whether the event ID has already been processed.
  5. Insert the event into durable storage or publish it to a queue.
  6. Return 200 or 204 promptly.
  7. Process business actions from the queue with independent retry and monitoring logic.

Return meaningful failure codes

HTTP status classes matter because they communicate whether the producer should treat delivery as successful, broken, or temporarily unavailable.

  • 2xx: The endpoint accepted the notification. The provider normally stops retrying that delivery.
  • 400 Bad Request: The request cannot be processed as formed. Use carefully; a malformed provider event could otherwise be retried forever.
  • 401 Unauthorized or 403 Forbidden: Authentication or authorization failed. Log these as security events, but understand that a configuration mismatch may cause repeated failures.
  • 404 Not Found: The configured path is wrong or no longer deployed.
  • 409 Conflict: Can be useful internally, but for a duplicate event it is often better to record the duplicate and still return 2xx; otherwise the provider may retry a harmless duplicate.
  • 429 Too Many Requests: Signals rate limiting. Only use it if the provider documents retry behavior that works with it.
  • 500, 502, 503, or 504: Indicate server or dependency problems. Providers commonly retry these, but retry schedules are provider-specific.

Do not return 2xx before you have durably accepted the event. If the process crashes after acknowledging but before storing the event, the provider may not retry and you can lose the notification.

Webhook security: authenticate every request

A webhook endpoint is an internet-accessible HTTP route. Obscurity is not authentication. A random-looking URL can reduce noise, but it does not prove that a request came from your email provider.

Verify signatures against the raw body

Many webhook systems use an HMAC signature. The sender calculates a cryptographic signature over the exact request bytes, sometimes combined with a timestamp, using a shared secret. Your server repeats the calculation and compares the expected and received signatures.

Conceptually, an HMAC-SHA-256 scheme might work like this:

signed_payload = timestamp + "." + raw_request_body
expected = HMAC_SHA256(webhook_secret, signed_payload)

The sender may include values in headers such as a timestamp and signature. Header names, encoding, version prefixes, and signed-payload construction are provider-specific, so use the provider’s documented algorithm exactly. Do not invent a signature format based on another service’s documentation.

The raw body requirement matters. JSON parsers can change whitespace, key order, Unicode escaping, or line endings when they deserialize and reserialize data. Verifying a reconstructed JSON object rather than the original bytes can produce incorrect signature failures.

Use a timing-safe comparison function supplied by your language or cryptography library. A normal string comparison can sometimes reveal information through timing differences; constant-time comparison reduces that risk.

Prevent replay attacks

A valid signed request can still be replayed if an attacker captures it or if an internal system accidentally resends it. Use multiple controls:

  • Reject timestamps outside a short permitted window, such as five minutes, if the provider supplies a signed timestamp.
  • Store processed event IDs with a unique database constraint or atomic cache operation.
  • Treat an already-seen event as a successful no-op after confirming it is genuinely the same event.
  • Rotate webhook secrets according to your provider’s supported process, allowing an overlap window if multiple active secrets are documented.

Never log the signing secret. Also avoid logging full request bodies by default if they include recipient addresses, subject lines, IP addresses, or metadata that may be personal data.

Layered controls, not brittle controls

IP allowlists can be useful when a provider publishes stable source ranges, but they should not be your only verification method. Cloud infrastructure changes, proxy layers, IPv6, and provider routing can make an allowlist incomplete. Mutual TLS, basic authentication, bearer tokens, or network controls may also be available depending on the provider, but signed requests remain a strong general pattern because they validate message integrity as well as sender knowledge of the secret.

Idempotency, duplicates, and ordering

The most common webhook reliability mistake is assuming an event arrives once and in chronological order. Network timeouts, successful responses lost in transit, load balancer retries, provider recovery processes, and manual redelivery can all produce duplicates.

Make each event handler idempotent: processing the same event twice should have the same effective result as processing it once. The simplest pattern is an email_events table keyed by the provider event ID.

BEGIN TRANSACTION
  INSERT INTO email_events (event_id, type, payload, received_at)
  VALUES (...)
  ON CONFLICT (event_id) DO NOTHING;

  if row_was_inserted:
      enqueue downstream processing
COMMIT

In application code, use the database insert outcome—not an in-memory variable—to decide whether a downstream action should run. This remains safe across multiple application instances.

Do not assume chronological delivery

A delivered event may reach your endpoint before a prior processed event. A later retry could arrive after a more recent status. Events can also be generated by different delivery systems with clocks that are not perfectly aligned.

Store both the provider’s event timestamp and your own receipt timestamp. Build status projections with an explicit precedence model rather than blindly overwriting the last status received. For example, a permanent bounce should not be replaced by a late “queued” event merely because it arrived afterward.

A useful model keeps two views:

  • An immutable event ledger containing every accepted raw event.
  • A derived message state calculated from event type, timestamp, and defined precedence rules.

The ledger makes investigations possible. The derived state makes product queries fast.

Retries, backoff, and dead-letter handling

Webhook delivery itself can fail. Your service may be down, DNS resolution may fail, a deploy may expose the wrong route, or a database may be unavailable. Providers commonly retry failed webhook deliveries, but their schedules, timeout limits, and maximum attempts vary.

You should build two retry systems: one for receiving provider notifications and another for your own downstream jobs. They solve different problems.

Provider-to-endpoint retries

When your endpoint returns a non-2xx response or times out, a provider may retry. Design for retries by keeping the endpoint simple, returning 5xx only when you truly could not accept the event, and monitoring failures by status code and endpoint version.

During planned maintenance, either keep the receiver and its queue available or ensure you understand the provider’s redelivery window. Do not assume a provider will keep failed notifications indefinitely.

Internal retries

After your endpoint persists an event, background work may still fail. For example, updating a CRM can return 503, a warehouse can be temporarily unavailable, or a billing system can reject a request. Retry such work with exponential backoff and jitter.

A common conceptual sequence is 1 minute, 5 minutes, 30 minutes, 2 hours, and then a dead-letter queue or manual review. The right timings depend on business urgency and dependency limits. A bounce-suppression update may deserve faster retries than a daily analytics aggregate.

Dead-letter queues are not a substitute for monitoring. Create alerts for growing backlog size, repeated signature failures, elevated 5xx rates, and messages that exceed your retry threshold. Include enough context to investigate safely: event ID, provider message ID, event type, receipt time, and a redacted failure reason.

Turning email events into product actions

Email webhooks are most valuable when each event has a deliberate business consequence. Avoid building an integration that stores events only because they are available.

Bounces and address hygiene

For permanent bounces, stop sending to the address until the recipient corrects it or an authorized operator reviews it. This protects reputation and reduces pointless delivery attempts. For temporary bounces, keep event history and identify patterns before making permanent decisions.

An address-verification service can be helpful before adding a new contact, but it cannot guarantee that a message will be accepted or wanted. Verification should complement, not replace, bounce and complaint handling. Tools such as MXToolbox can help inspect domain and DNS behavior, while mail-tester.com can help evaluate a test message’s authentication and content signals.

Account security and transactional flows

For password resets, login links, receipts, and alerts, a delivery event can support observability: “accepted by recipient server” is useful information for customer support. But do not reveal sensitive event details to unauthorized users or automatically assume a delivery failure proves account takeover.

A better support workflow is to combine the event timeline with application actions. For example, distinguish “reset message delivered” from “reset token redeemed.” The first is an email infrastructure event; the second is a confirmed action in your product.

Analytics and customer communication

Events can feed dashboards for delivery rates, bounce categories, complaints, open trends, and click-through behavior. Segment by message type rather than looking only at account-wide totals. A high bounce rate on imported marketing contacts means something different from a sudden failure rate on password-reset mail.

Use tags or metadata to label message purpose, environment, application, tenant, or template version. Keep the tag vocabulary controlled. password-reset, invoice, invite, and system-alert are more useful than dozens of ad hoc labels that cannot be aggregated reliably.

DNS and deliverability context for webhook events

Webhooks report what your email system observed, but they do not repair authentication or reputation problems. When delivery failures increase, check your domain configuration and sender identity alongside the webhook payload.

A typical SPF record uses TXT syntax like this:

example.com. IN TXT "v=spf1 include:spf.example-email-provider.com -all"

The actual include: domain must come from your email provider’s documentation. Do not substitute a guessed hostname. SPF permits only one SPF record per domain; multiple separate v=spf1 TXT records can cause evaluation problems.

A DMARC record is published at _dmarc and might look like this:

_dmarc.example.com. IN TXT "v=DMARC1; p=none; rua=mailto:dmarc-reports@example.com; adkim=s; aspf=s"

p=none is commonly used for monitoring, while p=quarantine and p=reject request progressively stricter receiver handling. Deploy DMARC intentionally: alignment depends on the visible From: domain and authenticated SPF or DKIM identities, so changing policy without understanding alignment can disrupt legitimate mail.

DKIM records are selector-specific. A provider may ask you to create a CNAME or TXT record at a name such as:

selector1._domainkey.example.com. IN CNAME selector1.example-email-provider.com.

The hostname, record type, and target are provider-specific. Copy them exactly from the sending platform rather than adapting an example. A missing or incorrect DKIM record may appear in event data only indirectly, such as increased deferrals, policy rejections, or poor inbox placement.

When diagnosing delivery, combine webhook data with DNS checks, message headers, and test results. A 550 5.7.1 rejection can reflect policy or authentication issues, but the human-readable SMTP response and receiving domain matter. Do not reduce every 5xx response to “invalid address.”

Observability and testing before production

Treat your webhook endpoint as production infrastructure. It needs logs, metrics, tests, access controls, and change management just like a payment callback or authentication endpoint.

Track at least these metrics:

  • Webhook requests received by event type.
  • 2xx, 4xx, and 5xx response rates.
  • Signature verification failures.
  • Processing latency from provider event timestamp to durable storage.
  • Duplicate-event rate.
  • Queue depth, oldest queued event age, and dead-letter count.
  • Delivery, bounce, complaint, and deferral rates by message category and sending domain.

Test with a staging endpoint before enabling production automation. Useful techniques include exposing a controlled development URL through a secure tunnel, capturing requests with a request-inspection service, replaying saved payload fixtures, and testing negative cases deliberately.

Your test matrix should include valid signatures, invalid signatures, expired timestamps, malformed JSON, duplicate event IDs, missing optional fields, unknown event types, database timeouts, queue failures, and delayed downstream services. A handler that only works for the happy-path sample payload is not ready for an event-driven email system.

Keep schema evolution in mind. Providers can add fields or new event types. Parse only fields you need, tolerate unknown fields, and route unknown event types to a monitored path instead of crashing. Conversely, do not silently ignore a newly important event forever; review unknown-event metrics after provider upgrades.

Common email webhook mistakes

Several integration shortcuts create avoidable incidents.

  1. Trusting a secret path instead of verifying signatures. A URL such as /hooks/a-long-random-string is not adequate authentication.
  2. Verifying parsed JSON instead of raw bytes. Serialization changes can break legitimate signatures.
  3. Doing expensive work before returning 2xx. Slow processing causes timeouts and duplicate deliveries.
  4. Treating duplicate events as errors. Duplicates are expected in many webhook systems; make processing idempotent.
  5. Assuming event order. Store an immutable ledger and calculate state with timestamps and precedence.
  6. Suppressing every temporary failure. A 4xx SMTP response is often retriable and needs different logic from a permanent 5xx failure.
  7. Treating opens as proof of reading. Privacy features and scanners make open data approximate.
  8. Logging secrets and full payloads indiscriminately. Logs are a common source of accidental data exposure.
  9. Ignoring endpoint monitoring. A broken route can silently turn into a growing provider retry queue or lost event history.
  10. Using webhook data as the only source of truth. For critical product actions, retain authoritative records in your own application.

Conclusion

Email webhooks provide the feedback loop that transactional sending needs. They let your application respond to delivery outcomes, maintain address hygiene, investigate support cases, measure operational reliability, and route event data into the systems that need it.

The implementation principle is simple: authenticate the raw request, acknowledge only after durable acceptance, process asynchronously, deduplicate by event ID, and expect retries and out-of-order delivery. Once those foundations are in place, delivery, bounce, complaint, and engagement events become dependable inputs to your application rather than opaque provider activity.

FAQ

What is the difference between an email API and an email webhook?

An email API or SMTP relay is used to submit a message for sending. An email webhook sends event notifications back to your application after events occur, such as delivery, a bounce, a complaint, an open, or a click.

Should a webhook endpoint return 200 or 204?

Either can be appropriate if your provider treats all 2xx responses as successful. Use 200 OK when returning a response body and 204 No Content when you have accepted the event but have nothing to return. Follow the provider’s documented expectations.

Can email webhooks arrive more than once?

Yes. Network failures, timeouts, retries, and redelivery tools can create duplicates. Store a unique event ID and make downstream processing idempotent so a duplicate changes nothing.

Does a delivered event mean the recipient read the email?

No. It usually means the recipient mail server accepted the message. It does not prove inbox placement, image loading, or that a person read the message.

Should I suppress an address after every bounce?

No. Permanent failures such as many 5xx recipient rejections often justify suppression, while temporary 4xx failures may resolve after retries. Use the SMTP response, provider classification, event history, and message type to make the decision.