Aller au contenu
Docs

Guide pratique

Traiter les e-mails entrants avec des webhooks

Abonnez un webhook à email.received, vérifiez chaque requête, puis récupérez via l’API le corps, les en-têtes et les pièces jointes de chaque message reçu.

Mis à jour le 1 oct. 2026

Ce guide explique comment réagir aux e-mails reçus dans votre propre code. Emailit notifie votre endpoint avec un événement email.received, et votre code récupère le message complet via l’API. Les exemples vérifient la signature, gèrent les lots d’événements, téléchargent les pièces jointes et ignorent les doublons.

Avant de commencer

  • La réception est configurée et un message de test apparaît dans l’onglet Incoming.
  • Une clé API Full Access. La lecture du contenu des e-mails n’est pas autorisée avec les clés Sending Only. Consultez Clés API.
  • Un endpoint HTTPS public qui accepte les requêtes POST. En développement local, utilisez un tunnel comme ngrok ou Cloudflare Tunnel.

Fonctionnement du flux

Le webhook vous indique qu’un message est arrivé. Il ne contient ni le corps ni les pièces jointes, ce qui garde les requêtes légères et vous permet de ne récupérer le contenu que lorsque vous en avez besoin.

email.received (un événement du tableau de la requête)
{
  "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"
    }
  }
}

Utilisez data.object.id avec Récupérer un e-mail pour obtenir le message analysé :

GET /v2/emails/em_2xGk9Rz2NcV7bL4pWqH8sYdT5fA (extrait)
{
  "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..."
    }
  ]
}

Le content des pièces jointes est encodé en Base64. Les noms d’en-têtes conservent leur casse d’origine et, lorsqu’un en-tête apparaît plusieurs fois (comme Received), seule la dernière valeur est conservée. Si vous n’avez besoin que d’une partie du message, utilisez les endpoints plus ciblés : Récupérer le corps, Lister les pièces jointes, ou Récupérer le MIME brut pour la source d’origine.

Créer le webhook

  1. Ajoutez le webhook. Accédez à Email APIWebhooks, sélectionnez Add webhook, saisissez un nom et l’URL de votre endpoint, puis sélectionnez Create.

  2. Copiez le secret. La boîte de dialogue affiche une seule fois le secret du webhook (whsec_…). Stockez-le sous le nom EMAILIT_WEBHOOK_SECRET dans l’environnement de votre application.

  3. Abonnez-vous uniquement à email.received. Dans l’onglet Settings du webhook, désactivez All events, sélectionnez email.received sous Emails, puis sélectionnez Save.

Sur les forfaits Pro, Business et Custom, vous pouvez ajouter un filtre de contenu pour que le webhook ne reçoive que les e-mails de certaines adresses, par exemple lorsque to se termine par @inbound.acme.com.

Écrire le gestionnaire

Chaque corps de requête est un tableau JSON de 100 événements au maximum. Le gestionnaire ci-dessous vérifie la signature, parcourt le tableau, ignore tout ce qui n’est pas email.received ou a déjà été traité, et récupère chaque message.

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);

Gérer les nouvelles tentatives et les doublons

  • Répondez dans les 30 secondes. Emailit attend une réponse 2xx pendant 30 secondes au maximum. Un timeout, un statut autre que 2xx ou une redirection comptent comme un échec, et tout le lot fait l’objet d’une nouvelle tentative selon le calendrier des nouvelles tentatives. Si la récupération et le traitement peuvent prendre plus de temps, placez les événements dans une file d’attente, renvoyez 200 et traitez-les dans une tâche en arrière-plan.
  • Dédoublonnez par event_id. Un lot renvoyé contient de nouveau tous ses événements, y compris ceux que vous aviez déjà traités avant l’échec. Enregistrez chaque event_id après traitement et ignorez les ID déjà vus. Chaque message reçu a aussi son propre ID d’e-mail, utilisable comme seconde clé.
  • Accusez réception des événements que vous ne traitez pas. Renvoyez 2xx même quand un lot ne contient que des types d’événements que vous ignorez. Sinon, Emailit continue de les renvoyer.
  • Récupérez le contenu rapidement. Le contenu des messages est conservé pendant une durée limitée qui dépend de votre forfait (7 jours sur Pay as you go). Passé ce délai, l’API renvoie l’e-mail sans son corps ni ses pièces jointes. Consultez Conservation des données.

Router les messages selon l’adresse

Comme toutes les parties locales sont acceptées, vous pouvez encoder des informations dans l’adresse et les relire dans to. Par exemple, envoyez des notifications avec reply_to défini sur reply+4821@inbound.acme.com, puis routez les réponses vers le ticket 4821 :

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

Vérifier le résultat

  1. Envoyez un message à une adresse de votre sous-domaine de réception.
  2. Dans l’onglet Requests du webhook, la requête email.received affiche Delivered.
  3. Votre application consigne l’expéditeur, l’objet et les éventuelles pièces jointes.

Si la requête affiche Attempting ou Failed, sélectionnez View sur une ligne en échec pour voir le code de statut et le corps de réponse renvoyés par votre endpoint. Consultez Nouvelles tentatives et échecs.

Cette page vous a-t-elle été utile ?

Merci pour votre retour.

Merci, nous lisons chaque message.