Marcio Cunha

How to Handle Webhooks to Register Delivery Failures and Invalid Addresses

Learn how to architect a resilient webhook system to process email delivery failures, identify invalid addresses, and protect your domain reputation against blacklists.

Marcio Cunha12 min
Also available in:EspañolPortuguês
Summary
  • Asynchronous messaging systems prevent traffic spikes from crashing the main application during high-volume notification ingestion.
  • Identifying permanent delivery failures stops the system from wasting resources sending messages to non-existent mailboxes.
  • Idempotency ensures that reprocessing the same webhook event does not generate duplicate records in the database.
  • Rigorous filters at the ingestion layer block forged requests and prevent denial-of-service attacks disguised as notifications.
  • Clear retention and retry policies keep the historical log clean and auditable for compliance audits with email providers.

The Silent Challenge of Delivery Notifications

When we dispatch thousands of transactional emails or automated campaigns, the work rarely ends with clicking the send button. In practice, this means a large portion of those messages might encounter full mailboxes, offline servers, or simply addresses that never existed. To alert your application about these issues, sending services use webhooks — which act as automated messengers sending real-time warnings whenever something goes wrong. Handling this flow requires a robust architecture to avoid losing critical data or overloading the database.

A webhook is simply an HTTP POST call that an external server makes to your system when an event occurs. Instead of your application repeatedly asking if the email was delivered (known as polling, which consumes high computational energy), you simply wait for the alert to arrive. The problem is that the internet is a chaotic environment: networks drop, servers restart, and traffic spikes happen without warning. If your application is not prepared to absorb this influx of notifications, the delivery service might give up on sending alerts and you will lose track of errors.

Anatomy of a Failure: Soft Bounces versus Hard Bounces

To build an efficient handling routine, we must first separate the wheat from the chaff in the email providers' dictionary. A soft bounce is a temporary failure — in practice, it means the recipient's mailbox is full or their server was temporarily unavailable at that exact minute. In these scenarios, the golden rule is to retry later, as delivery might still work tomorrow. A hard bounce represents a permanent error, indicating that the address simply does not exist or the domain has been shut down.

When we receive a hard bounce event, the required action is completely different: we need to mark that contact as invalid immediately in the database. In practice, continuing to send messages to a hard bounce address is the fastest way to get your own domain classified as a spam sender. Major email providers monitor how many messages you send to ghost addresses; if this number is high, your future messages will start landing straight in the junk folder of any user, legitimate or not.

Building a Resilient Ingestion Layer with Asynchronous Messaging

The biggest architectural mistake when implementing webhooks is trying to process the complete event — saving logs, updating tables, and triggering business rules — in the exact same fraction of second the HTTP request arrives. If your email provider fires ten thousand simultaneous notifications during a large campaign, your web server will crash due to a lack of available connections. The elegant solution to this bottleneck is decoupling ingestion from processing using a message queue, such as RabbitMQ, AWS SQS, or Redis.

In practice, the flow is divided into two clear and independent stages. In the first stage, the sole responsibility of the webhook endpoint is to quickly validate the authenticity of the request, place it in an internal processing queue, and immediately respond with an HTTP 200 code to the sending service. In the second stage, background workers consume this queue at their own pace, saving the data calmly and updating user statuses without rush. This pattern protects your infrastructure and ensures no alert is lost, even if the database experiences brief slowdowns.

Ensuring Security and Idempotency in Processing

Anyone on the internet who discovers your webhook URL can invent fake requests and pretend to be your email provider. To prevent fake data from corrupting your database, it is mandatory to validate the cryptographic signature that accompanies the header of each request. Legitimate services sign the contents of the data packet using a shared secret key; if the mathematical signature does not match during verification, the request must be summarily rejected before any other validation.

Another indispensable technical detail is idempotency — a fancy concept that simply means processing the same message twice produces the exact same result as processing it just once. Because computer networks are unstable, it is common for a webhook service to send the same alert repeatedly if it does not receive a quick acknowledgment. By logging the unique identifier of each processed event, your application can safely ignore repeated notifications, avoiding unwanted duplicates in customer failure histories.

Implementing the Ingestion Code in Node.js

To turn these concepts into reality, let's look at a practical example using Node.js and the Express framework. The code below demonstrates how to create a secure endpoint that validates the cryptographic signature before accepting the delivery failure event and sending it to the internal queue.

const express = require('express');const crypto = require('crypto');const app = express();app.use(express.json());const WEBHOOK_SECRET = 'your_shared_secret_key';app.post('/webhooks/email-delivery', (req, res) => {  const signature = req.headers['x-signature'];  const payload = JSON.stringify(req.body);  const expectedSignature = crypto    .createHmac('sha256', WEBHOOK_SECRET)    .update(payload)    .digest('hex');  if (signature !== expectedSignature) {    return res.status(401).send('Invalid signature.');  }  const { eventType, email, reason } = req.body;  console.log(`Received event ${eventType} for ${email}: ${reason}`);  // Here you would enqueue the event for asynchronous processing  res.status(200).send('Event received successfully.');});app.listen(3000, () => console.log('Server running on port 3000'));

This snippet illustrates the initial defense barrier: checking the signature header prevents malicious bots from overwhelming your application with digital garbage. Once the signature passes the test, the system extracts the event type, the affected email, and the error reason, setting the stage for updating user registration tables.

Final Considerations and Domain Health Maintenance

Managing delivery failures and invalid addresses through webhooks is not just a technical maintenance task, but a vital strategy for your operation's digital survival. In practice, ignoring these signs means burning your sending server's reputation, resulting in important emails never reaching the correct recipients. By combining asynchronous queues, rigorous security validation, and clear rules to differentiate temporary from permanent errors, you turn an invisible problem into an automated, clean, and predictable process.

Monitoring bounce metrics over time also helps identify issues in lead capture, such as poorly validated forms that allow typing emails with major typos. Keeping this machinery running requires constant log monitoring and periodic tests simulating network failures. With a solid and well-founded architecture, your application gains the necessary resilience to handle the inevitable unpredictability of the connected world.