Vai al contenuto
Docs

Guida pratica

Elabora le email in entrata con i webhook

Iscrivi un webhook a email.received, verifica ogni richiesta, poi recupera con l’API corpo, header e allegati di ogni messaggio ricevuto.

Aggiornato il 1 ott 2026

Questa guida mostra come reagire alle email ricevute nel tuo codice. Emailit notifica il tuo endpoint con un evento email.received, e il tuo codice recupera il messaggio completo con l’API. Gli esempi verificano la firma, gestiscono i batch di eventi, scaricano gli allegati e saltano i duplicati.

Prima di iniziare

  • Le email in entrata sono configurate e un messaggio di prova compare nella scheda Incoming.
  • Una chiave API con Full Access. Le chiavi Sending Only non possono leggere il contenuto delle email. Vedi Chiavi API.
  • Un endpoint HTTPS pubblico che accetti richieste POST. Per lo sviluppo in locale, usa un tunnel come ngrok o Cloudflare Tunnel.

Come funziona il flusso

Il webhook ti dice che è arrivato un messaggio. Non contiene il corpo né gli allegati: così le richieste restano leggere e recuperi il contenuto solo quando ti serve.

email.received (un evento nell’array della richiesta)
{
  "event_id": "evt_2xGk9Tb4QmF6wN2cJpR7uZsE1kD",
  "type": "email.received",
  "object": {
    "id": "em_2xGk9Rz2NcV7bL4pWqH8sYdT5fA",
    "object": "email",
    "from": "ada@example.com",
    "to": "support@inbound.acme.com",
    "subject": "Question about order 1042",
    "created_at": "2026-10-01T09:14:05.317000+00:00"
  },
  "data": {
    "object": {
      "id": "em_2xGk9Rz2NcV7bL4pWqH8sYdT5fA",
      "object": "email",
      "from": "ada@example.com",
      "to": "support@inbound.acme.com",
      "subject": "Question about order 1042",
      "created_at": "2026-10-01T09:14:05.317000+00:00"
    }
  }
}

Usa data.object.id con Recupera un’email per ottenere il messaggio analizzato:

GET /v2/emails/em_2xGk9Rz2NcV7bL4pWqH8sYdT5fA (abbreviato)
{
  "object": "email",
  "id": "em_2xGk9Rz2NcV7bL4pWqH8sYdT5fA",
  "type": "inbound",
  "status": "received",
  "from": "ada@example.com",
  "to": "support@inbound.acme.com",
  "subject": "Question about order 1042",
  "message_id": "<CAF3x1@mail.example.com>",
  "headers": {
    "From": "Ada Lovelace <ada@example.com>",
    "Reply-To": "ada@example.com",
    "Date": "Thu, 01 Oct 2026 09:14:02 +0000"
  },
  "body": {
    "text": "Hi, my order hasn't arrived yet...",
    "html": "<p>Hi, my order hasn't arrived yet...</p>"
  },
  "attachments": [
    {
      "filename": "receipt.pdf",
      "content_type": "application/pdf",
      "size": 48213,
      "content_id": null,
      "content_disposition": "attachment",
      "content": "JVBERi0xLjcKJcfsj6IK..."
    }
  ]
}

Il content degli allegati è codificato in Base64. I nomi degli header mantengono le maiuscole e minuscole originali, e quando un header compare più volte (come Received) viene mantenuto solo l’ultimo valore. Se ti serve solo una parte del messaggio, usa gli endpoint più specifici: Recupera il corpo, Elenca gli allegati, oppure Recupera il MIME grezzo per il sorgente originale.

Crea il webhook

  1. Aggiungi il webhook. Vai a Email APIWebhooks, seleziona Add webhook, inserisci un nome e l’URL del tuo endpoint e seleziona Create.

  2. Copia il secret. La finestra mostra il secret del webhook (whsec_…) una sola volta. Memorizzalo come EMAILIT_WEBHOOK_SECRET nell’ambiente della tua app.

  3. Iscriviti solo a email.received. Nella scheda Settings del webhook, disattiva All events, seleziona email.received in Emails e seleziona Save.

Nei piani Pro, Business e Custom puoi aggiungere un filtro sul payload, così il webhook riceve solo la posta per alcuni indirizzi, ad esempio quelli in cui to termina con @inbound.acme.com.

Scrivi il gestore

Il corpo di ogni richiesta è un array JSON di massimo 100 eventi. Il gestore qui sotto verifica la firma, scorre l’array, salta tutto ciò che non è email.received o che è già stato elaborato e recupera ogni messaggio.

server.js
import crypto from 'node:crypto';
import express from 'express';

const app = express();
const WEBHOOK_SECRET = process.env.EMAILIT_WEBHOOK_SECRET; // whsec_...
const API_KEY = process.env.EMAILIT_API_KEY; // secret_..., Full Access
const processed = new Set(); // use a database table with a unique key in production

function verify(rawBody, signature, timestamp) {
  if (!signature || !timestamp) return false;
  const age = Math.abs(Math.floor(Date.now() / 1000) - Number(timestamp));
  if (!(age <= 300)) return false; // 5-minute tolerance
  const expected = crypto
    .createHmac('sha256', WEBHOOK_SECRET)
    .update(`${timestamp}.`)
    .update(rawBody)
    .digest('hex');
  const a = Buffer.from(expected);
  const b = Buffer.from(signature);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

async function fetchEmail(id) {
  const res = await fetch(`https://api.emailit.com/v2/emails/${id}`, {
    headers: { Authorization: `Bearer ${API_KEY}` },
  });
  if (!res.ok) throw new Error(`Emailit API returned ${res.status}`);
  return res.json();
}

async function handleReceived(event) {
  const email = await fetchEmail(event.data.object.id);
  console.log(`From ${email.from} to ${email.to}: ${email.subject}`);
  console.log(email.body.text ?? email.body.html);

  for (const file of email.attachments ?? []) {
    const bytes = Buffer.from(file.content, 'base64');
    console.log(`Attachment ${file.filename} (${file.content_type}, ${bytes.length} bytes)`);
  }
}

// Keep the raw body: the signature is computed over the exact bytes.
app.post('/webhooks/emailit', express.raw({ type: 'application/json' }), async (req, res) => {
  if (!verify(req.body, req.get('X-Emailit-Signature'), req.get('X-Emailit-Timestamp'))) {
    return res.status(401).send('Invalid signature');
  }

  const events = JSON.parse(req.body.toString('utf8'));
  try {
    for (const event of events) {
      if (event.type !== 'email.received' || processed.has(event.event_id)) continue;
      await handleReceived(event);
      processed.add(event.event_id);
    }
    res.sendStatus(200);
  } catch (err) {
    console.error(err);
    res.sendStatus(500); // Emailit retries the whole batch later
  }
});

app.listen(3000);

Gestisci nuovi tentativi e duplicati

  • Rispondi entro 30 secondi. Emailit attende una risposta 2xx per un massimo di 30 secondi. Un timeout, uno stato diverso da 2xx o un reindirizzamento contano come errore, e l’intero batch viene ritentato secondo il calendario dei nuovi tentativi. Se il recupero e l’elaborazione possono richiedere più tempo, memorizza gli eventi in una coda, restituisci 200 ed elaborali in un job in background.
  • Elimina i duplicati in base a event_id. Un batch ritentato contiene di nuovo tutti i suoi eventi, compresi quelli che avevi già gestito prima dell’errore. Registra ogni event_id dopo l’elaborazione e salta gli ID già visti. Ogni messaggio ricevuto ha anche un proprio ID email, che puoi usare come seconda chiave.
  • Conferma anche gli eventi che non gestisci. Restituisci 2xx anche quando un batch contiene solo tipi di evento che ignori. Altrimenti Emailit continua a ritentarli.
  • Recupera il contenuto subito. Il contenuto dei messaggi viene conservato per un periodo limitato che dipende dal piano (7 giorni con Pay as you go). Dopo, l’API restituisce l’email senza corpo né allegati. Vedi Conservazione dei dati.

Instrada i messaggi in base all’indirizzo

Poiché ogni parte locale viene accettata, puoi codificare informazioni nell’indirizzo e rileggerle da to. Ad esempio, invia le notifiche con reply_to impostato su reply+4821@inbound.acme.com, poi instrada le risposte al ticket 4821:

JavaScript
const match = email.to.match(/^reply\+(\d+)@inbound\.acme\.com$/i);
if (match) {
  await addReplyToTicket(match[1], email.body.text);
}

Verifica che funzioni

  1. Invia un messaggio a un indirizzo del sottodominio di ricezione.
  2. Nella scheda Requests del webhook, la richiesta email.received risulta Delivered.
  3. La tua applicazione registra nei log mittente, oggetto ed eventuali allegati.

Se la richiesta risulta Attempting o Failed, seleziona View su una riga non riuscita per vedere il codice di stato e il corpo della risposta restituiti dal tuo endpoint. Vedi Nuovi tentativi ed errori.

Questa pagina ti è stata utile?

Grazie del feedback.

Grazie, leggiamo ogni messaggio.