Beta privataMailbeam è in beta privata: l'API pubblica non è ancora aperta.Iscriviti alla lista d'attesa

Webhook

Mailbeam può inviare un evento via POST al tuo server invece di costringerti a interrogarlo tu.

Configurare un endpoint

  1. Vai su Webhooks nel tuo pannello
  2. Scrivi un URL HTTPS e scegli gli eventi che vuoi
  3. Copia il segreto di firma: viene mostrato una sola volta
  4. 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

EventoQuando scatta
batch.completedUn lavoro in blocco finisce di essere elaborato
batch.failedUn lavoro in blocco si ferma con un errore
quota.thresholdArrivi 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(), non express.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.

TentativoAttesa
1º ritentativo5 minuti
2º ritentativo30 minuti
3º ritentativo2 ore
4º ritentativo8 ore
5º ritentativo24 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.