Webhook
Mailbeam può inviare un evento via POST al tuo server invece di costringerti a interrogarlo tu.
Configurare un endpoint
- Vai su Webhooks nel tuo pannello
- Scrivi un URL HTTPS e scegli gli eventi che vuoi
- Copia il segreto di firma: viene mostrato una sola volta
- Premi Send test per controllare che il tuo gestore risponda
Gli endpoint devono essere URL HTTPS raggiungibili pubblicamente. Rifiutiamo l'HTTP puro e semplice, e rifiutiamo gli indirizzi privati e di link-local, perché la richiesta la fanno i nostri server. Per sviluppare in locale, esponi la tua macchina con ngrok o Cloudflare Tunnel.
Eventi disponibili
| Evento | Quando scatta |
|---|---|
batch.completed | Un lavoro in blocco finisce di essere elaborato |
batch.failed | Un lavoro in blocco si ferma con un errore |
quota.threshold | Arrivi all'80% o al 100% della tua quota mensile |
L'elenco è tutto qui. È corto perché inviamo solo eventi che possiamo osservare
direttamente. Due eventi che questa documentazione annunciava in passato —
email.bounced e email.mx_degraded — sono stati eliminati: un bounce avviene
nel tuo flusso di posta, non nel nostro, e non sorvegliamo i record MX di un
dominio nel tempo. Se uno dei due ti sarebbe utile, diccelo e vediamo che cosa
servirebbe.
Struttura del payload
Tutti i payload dei webhook seguono la stessa busta:
{
"id": "evt_9f2c1d7a-4b83-4a1e-9f0e-2b6c5d8a1f34",
"type": "batch.completed",
"created_at": "2026-08-10T14:32:00Z",
"data": {
"job_id": "job_9f2c1d7a-4b83-4a1e-9f0e-2b6c5d8a1f34",
"total": 5000,
"processed": 5000,
"valid": 4213,
"invalid": 787,
"download_url": "https://api.mailbeam.dev/v1/jobs/job_9f2c1d7a-4b83-4a1e-9f0e-2b6c5d8a1f34/results",
"results_expire_at": "2026-08-13T14:32:00Z"
}
}La consegna è almeno una volta. Preferiamo inviare un duplicato piuttosto che
perdere un evento, quindi se una risposta si perde mentre torna verso di noi
vedrai lo stesso evento due volte. Deduplica in base a id, che è stabile lungo
tutti i ritentativi e verso tutti gli endpoint a cui l'evento è stato inviato.
Verifica della firma HMAC
Ogni richiesta porta un'intestazione X-Mailbeam-Signature. Verificala prima di
fidarti del payload: l'URL è l'unica altra cosa che protegge il tuo gestore, e gli
URL trapelano.
La firma è un riassunto HMAC-SHA256 del corpo grezzo della richiesta, con la chiave presa dal segreto di firma del tuo endpoint.
import crypto from "crypto";
export function verifyWebhookSignature(
body: string | Buffer,
signature: string,
secret: string
): boolean {
const expected = crypto
.createHmac("sha256", secret)
.update(body)
.digest("hex");
// Usa timingSafeEqual per evitare attacchi basati sui tempi
return crypto.timingSafeEqual(
Buffer.from(`sha256=${expected}`, "utf8"),
Buffer.from(signature, "utf8")
);
}
// Nel tuo gestore di Express:
app.post("/webhooks/mailbeam", express.raw({ type: "application/json" }), (req, res) => {
const sig = req.headers["x-mailbeam-signature"] as string;
if (!verifyWebhookSignature(req.body, sig, process.env.MAILBEAM_WEBHOOK_SECRET!)) {
return res.status(400).send("Invalid signature");
}
const event = JSON.parse(req.body.toString());
// Tratta l'evento...
res.json({ received: true });
});Usa
express.raw(), nonexpress.json(). Ti servono i byte grezzi del corpo per verificare la firma. Se analizzi il JSON prima, la firma non tornerà.
Politica dei ritentativi
Qualsiasi risposta fuori dall'intervallo 2xx, o l'assenza di risposta entro 30 secondi, conta come tentativo fallito. Non seguiamo le redirezioni: anche un 3xx è un fallimento.
| Tentativo | Attesa |
|---|---|
| 1º ritentativo | 5 minuti |
| 2º ritentativo | 30 minuti |
| 3º ritentativo | 2 ore |
| 4º ritentativo | 8 ore |
| 5º ritentativo | 24 ore |
I ritentativi sono lanciati da uno scheduler che gira ogni cinque minuti, quindi l'attesa della tabella è un minimo, non un tempo esatto.
Quando sono falliti il tentativo iniziale e i cinque ritentativi — circa 34 ore — l'endpoint viene disattivato e avvisiamo via email chi possiede l'account. Niente altro viene messo in coda per lui finché non lo riattivi dal pannello. Sistema il tuo ricevitore, riattivalo e manda un evento di prova prima di tornare a dipendere da lui.
Un evento di prova inviato dal pannello è un tentativo singolo. Non entra mai nella scala dei ritentativi e non disattiva mai un endpoint.
Come rispondere ai webhook
Restituisci uno stato 2xx il prima possibile. Scarica l'elaborazione pesante su una coda:
app.post("/webhooks/mailbeam", async (req, res) => {
// Verifica prima la firma
if (!verifyWebhookSignature(req.body, req.headers["x-mailbeam-signature"], secret)) {
return res.status(400).send("Invalid signature");
}
// Conferma subito
res.json({ received: true });
// Elabora in modo asincrono
await queue.add("process-mailbeam-event", JSON.parse(req.body.toString()));
});Il tuo segreto di firma
Il segreto viene generato quando crei l'endpoint e viene mostrato una sola volta. Lo conserviamo cifrato e non come hash, perché a differenza di una chiave API dobbiamo usarlo: ogni consegna viene firmata con quello. Se lo perdi, cancella l'endpoint e creane uno nuovo.