Ingegneria · 21 settembre 2026

Progettare webhook email per la consegna at-least-once

Scopri come costruire consumer di webhook resilienti per gli eventi email usando nuovi tentativi, chiavi di idempotenza e verifica della firma, così da non perdere mai un evento di consegna.

La sfida di una consegna affidabile degli eventi

Per ottenere una consegna at-least-once (almeno una volta) dei webhook email, devi implementare un sistema in cui il mittente ritenta le richieste fallite con backoff esponenziale e il ricevente garantisce l'idempotenza. Dato che le reti sono inaffidabili e i server vanno in crash, non puoi dare per scontato che un singolo HTTP 200 OK garantisca l'elaborazione dell'evento. L'affidabilità nasce dalla combinazione di una coda di tentativi persistente lato mittente e di uno strato di deduplicazione lato ricevente.

Quando integri un'API email come SendHQ, la tua applicazione deve sapere quando un'email è stata consegnata, è andata in bounce o è stata segnalata come spam. Questi eventi sono asincroni. Se il tuo endpoint webhook resta giù per cinque minuti durante un picco di traffico, potresti perdere migliaia di segnali di consegna critici. Questo crea un buco nei dati delle tue analytics e impedisce al sistema di reagire ai bounce (fondamentale per mantenere la reputazione del mittente).

Anatomia di un webhook affidabile

Un'architettura di webhook solida si basa su tre pilastri principali: verifica della firma, elaborazione idempotente e una strategia di nuovi tentativi.

1. Verifica della firma

Non fidarti mai di una richiesta POST al tuo endpoint webhook basandoti solo sull'indirizzo IP o sulla presenza di una chiave API nel corpo. Un attaccante può falsificarli. Usa invece una firma HMAC (Hash-based Message Authentication Code).

Il mittente firma il payload con un segreto condiviso e allega la firma a un'intestazione (ad es. X-SendHQ-Signature). Il ricevente ricalcola l'hash con lo stesso segreto e lo confronta con l'intestazione.

const crypto = require('crypto'); function verifySignature(payload, signature, secret) { const expectedSignature = crypto .createHmac('sha256', secret) .update(payload) .digest('hex'); // Use timingSafeEqual to prevent timing attacks return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature)); }

2. Idempotenza e deduplicazione

Consegna at-least-once significa che il mittente continuerà a inviare l'evento finché non riceve una risposta di successo. Se il tuo server elabora l'evento ma va in crash prima di inviare il 200 OK, il mittente invierà di nuovo l'evento. Senza idempotenza, nel tuo database potresti contare una singola consegna come due.

Ogni evento deve avere un event_id univoco. Per tenere traccia degli eventi elaborati dovresti usare il pattern della chiave di idempotenza.

Il flusso di lavoro:

  1. Ricevi il payload del webhook.
  2. Controlla se l'event_id esiste nella tabella processed_events.
  3. Se esiste, restituisci subito 200 OK e ignora il corpo.
  4. Se non esiste, elabora l'evento e registra l'event_id in un'unica transazione.

3. La strategia dei nuovi tentativi

Dal punto di vista del mittente, una policy di nuovi tentativi è obbligatoria. Un pattern standard è il backoff esponenziale con jitter. Per esempio: nuovo tentativo dopo 1 minuto, 5 minuti, 30 minuti, 2 ore e 12 ore.

Se il ricevente restituisce un errore 4xx (tranne 429), di solito si tratta di un errore del client (come una firma non valida) e ritentare non serve. Un errore 5xx o un timeout indicano invece un errore transitorio, per cui i nuovi tentativi sono essenziali.

Esempio concreto di payload

Ecco un tipico payload di un evento di consegna che potresti ricevere da SendHQ:

{ "event_id": "evt_12345abcde", "event_type": "delivered", "timestamp": "2026-09-15T10:00:00Z", "message_id": "msg_98765xyz", "recipient": "user@example.com", "metadata": { "order_id": "ord_5544" } }

Gestire errori e casi limite

Il problema del "consumer lento"

Se il tuo gestore di webhook esegue scritture pesanti sul database o chiama in modo sincrono altre API esterne, il tuo endpoint andrà in timeout. Questo attiva la logica di nuovi tentativi del mittente e porta a una "tempesta di retry" che può mandare in crash il tuo server.

La soluzione: separa la ricezione dall'elaborazione.

  1. Ricevi il webhook.
  2. Verifica la firma.
  3. Inserisci il payload grezzo in una coda di messaggi (come RabbitMQ, SQS o Redis).
  4. Restituisci subito 200 OK.
  5. Un processo worker separato consuma la coda e aggiorna il database.

Il problema della prontezza degli agenti

Quando gli agenti AI vengono attivati da webhook, il rischio di loop infiniti aumenta. Se un agente riceve un evento "delivered" e risponde inviando un'altra email, che a sua volta genera un altro evento "delivered", hai un loop.

Tratta l'invio di email come un effetto collaterale esterno. Gli agenti non dovrebbero mai inviare email automaticamente in base a un webhook senza un'approvazione human-in-the-loop o un controllo rigoroso tramite macchina a stati che confermi che l'azione è necessaria.

Il confronto tra le soluzioni

Quando scegli un provider, l'affidabilità dipende spesso da come gestisce questi eventi e da quanto fa pagare il volume di posta che li genera.

Per la posta transazionale ad alto volume la differenza di costo è netta. Secondo i prezzi di Amazon SES, l'invio a consumo costa 0.10 USD ogni 1.000 email. I prezzi di Postmark, invece, partono da 15 USD al mese per 10.000 email, con eccedenze tra 1.20 e 1.80 USD ogni 1.000. Per un volume di 50.000 email, SES a consumo costa circa 5 USD, mentre i piani di Postmark costerebbero circa 66 USD.

Tra le altre opzioni c'è Resend, che offre un piano gratuito da 3.000 email al mese (con un limite di 100 al giorno) e un piano Pro da 20 USD al mese per 50.000 email. Mailgun parte da 15 USD al mese per 10.000 email. SendGrid ha trasformato il suo piano gratuito in una prova di 60 giorni, con Essentials a partire da 19.95 USD al mese.

Qualunque sia il provider, è l'affidabilità con cui consumi questi eventi a determinare l'integrità dei tuoi dati.

Checklist di implementazione per ingegneri

  • Verifica della firma: il payload viene verificato usando un segreto condiviso e una funzione di confronto a tempo costante?
  • Elaborazione asincrona: l'endpoint restituisce 200 OK prima di eseguire una logica di business pesante?
  • Idempotenza: esiste un vincolo di unicità su event_id per impedire elaborazioni duplicate?
  • Gestione del timeout: il timeout è impostato a un valore inferiore al timeout del provider per evitare nuovi tentativi sovrapposti?
  • Monitoraggio: hai avvisi per un picco di risposte 5xx sul tuo endpoint webhook?
  • Stato del DNS: i server riceventi sono configurati correttamente? Usa strumenti come SendHQ Email DNS Checker per assicurarti che l'infrastruttura sia raggiungibile e configurata correttamente.
  • Standard di autenticazione: hai implementato DKIM, SPF e DMARC per assicurarti che la posta in uscita sia accettata, riducendo il numero di webhook di "bounce" da gestire?

Riepilogo dei compromessi

Approccio | Pro | Contro

Elaborazione sincrona | Semplice da implementare, consistenza immediata | Alto rischio di timeout, soggetta a tempeste di retry

Elaborazione basata su coda | Altamente scalabile, resiste ai picchi | Maggiore complessità infrastrutturale, consistenza eventuale

Semplice logging | Basso overhead | Nessun modo di recuperare eventi persi senza log manuali

Tabella di idempotenza | Integrità dei dati garantita | Una scrittura in più sul database per ogni evento

Considerazioni finali

L'affidabilità dei webhook email non consiste nell'evitare gli errori, ma nel progettare tenendone conto. Partendo dal presupposto che la rete fallirà e che gli eventi verranno consegnati più di una volta, costruisci un sistema davvero resiliente. Che tu stia gestendo i record SPF di un piccolo progetto o scalando un enorme sistema transazionale, verifica della firma e idempotenza restano il punto di riferimento.

Costruisci la tua infrastruttura email con SendHQ.