Přejít na obsah
Dokumentace

Návod

Zpracování příchozích e-mailů webhooky

Přihlaste webhook k odběru email.received, ověřte každý požadavek a pak přes API načtěte tělo, hlavičky a přílohy každé přijaté zprávy.

Aktualizováno 1. 10. 2026

Tento návod ukazuje, jak na přijatou poštu reagovat ve vlastním kódu. Emailit pošle na váš endpoint událost email.received a váš kód přes API načte celou zprávu. Příklady ověřují podpis, zpracovávají dávky událostí, stahují přílohy a přeskakují duplicity.

Než začnete

  • Příchozí e-maily jsou nastavené a testovací zpráva se zobrazuje na kartě Incoming.
  • API klíč s oprávněním Full Access. Klíče Sending Only obsah e-mailů číst nesmějí. Viz API klíče.
  • Veřejný endpoint s HTTPS, který přijímá požadavky POST. Pro lokální vývoj použijte tunel, například ngrok nebo Cloudflare Tunnel.

Jak celý proces funguje

Webhook vám oznámí, že zpráva dorazila. Neobsahuje tělo ani přílohy, takže požadavky zůstávají malé a obsah načítáte, jen když ho potřebujete.

email.received (jedna událost v poli požadavku)
{
  "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"
    }
  }
}

Pomocí data.object.id a endpointu Načtení e-mailu získáte zpracovanou zprávu:

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

content přílohy je kódovaný v Base64. Názvy hlaviček si zachovávají původní velikost písmen, a když se hlavička vyskytuje víckrát (například Received), zůstane jen poslední hodnota. Pokud potřebujete jen část zprávy, použijte užší endpointy: Načtení těla e-mailu, Výpis příloh, nebo Načtení surového MIME pro původní zdroj.

Vytvořte webhook

  1. Přidejte webhook. Přejděte do Email APIWebhooks, vyberte Add webhook, zadejte název a URL svého endpointu a vyberte Create.

  2. Zkopírujte tajný klíč. Dialogové okno jednou zobrazí tajný klíč webhooku (whsec_…). Uložte ho do prostředí své aplikace jako EMAILIT_WEBHOOK_SECRET.

  3. Odebírejte jen email.received. Na kartě Settings webhooku vypněte All events, v sekci Emails vyberte email.received a vyberte Save.

V tarifech Pro, Business a Custom můžete přidat filtr obsahu, aby webhook dostával poštu jen pro některé adresy, například když to končí na @inbound.acme.com.

Napište obsluhu

Tělo každého požadavku je pole JSON až se 100 událostmi. Obsluha níže ověří podpis, projde pole, přeskočí vše, co není email.received nebo co už bylo zpracováno, a načte každou zprávu.

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

Ošetřete opakování a duplicity

  • Odpovězte do 30 sekund. Emailit čeká na odpověď 2xx až 30 sekund. Vypršení časového limitu, jiný stav než 2xx nebo přesměrování se počítá jako selhání a celá dávka se zopakuje podle plánu opakování. Pokud načtení a zpracování může trvat déle, uložte události do fronty, vraťte 200 a zpracujte je v úloze na pozadí.
  • Odstraňujte duplicity podle event_id. Opakovaná dávka znovu obsahuje všechny své události, včetně těch, které jste před selháním už zpracovali. Po zpracování si každé event_id zaznamenejte a ID, která jste už viděli, přeskakujte. Každá přijatá zpráva má navíc vlastní ID e-mailu, které můžete použít jako druhý klíč.
  • Potvrzujte i události, které nezpracováváte. Vraťte 2xx, i když dávka obsahuje jen typy událostí, které ignorujete. Jinak je Emailit bude opakovat.
  • Načítejte obsah včas. Obsah zpráv se uchovává omezenou dobu, která závisí na tarifu (7 dní v tarifu Pay as you go). Potom API vrací e-mail bez těla a příloh. Viz Uchovávání dat.

Směrujte zprávy podle adresy

Protože se přijímá jakákoli část adresy před zavináčem, můžete do adresy zakódovat informace a přečíst je zpět z to. Posílejte například oznámení s reply_to nastaveným na reply+4821@inbound.acme.com a odpovědi pak směrujte k tiketu 4821:

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

Ověřte, že vše funguje

  1. Pošlete zprávu na adresu na své příchozí subdoméně.
  2. Na kartě Requests webhooku má požadavek email.received stav Delivered.
  3. Vaše aplikace zaloguje odesílatele, předmět a případné přílohy.

Pokud má požadavek stav Attempting nebo Failed, vyberte View v řádku s neúspěšným požadavkem a uvidíte stavový kód a tělo odpovědi, které váš endpoint vrátil. Viz Opakování a selhání.

Byla tato stránka užitečná?

Děkujeme za zpětnou vazbu.

Děkujeme, čteme každou zprávu.