Sign inGet Started

E-Mail senden

POST /v1/email/messages sendet eine einzelne E-Mail. Geben Sie Absender, Empfänger und Inhalt in einem JSON-Payload an. Der API gibt 202 Accepted mit einer Nachrichten-ID zurück und stellt die E-Mail anschließend asynchron zu. Die vollständigen Schemas finden Sie in der API-Referenz.

Ein minimaler Versand

Das kleinste gültige Payload besteht aus einem from, mindestens einem to-Empfänger, einem subject und einem Body (html, text oder beides). Die from-Adresse muss zu einer Domain gehören, die Sie in diesem Workspace verifiziert haben, oder zur Onboarding-Domain.
const msg = await bird.email.send({
  from: { email: "onboarding@messagebird.dev", name: "Bird" },
  to: ["delivered@messagebird.dev"],
  subject: "Hello from Bird",
  html: "<p>My first Bird email.</p>",
});
console.log(msg.id, msg.status); // "em_…", "accepted"
Verwenden Sie Ihren regionalen Host (https://us1.platform.bird.com oder https://eu1.platform.bird.com) mit einem passenden bk_{region}_...-Schlüssel.
Das Versandbeispiel verwendet delivered@messagebird.dev, eine Sandbox-Adresse, die E-Mails immer annimmt. Der API lehnt Platzhalter-Domains mit einem 422 ab: example.com, example.net, example.org, example.edu, test.com sowie alles unter den reservierten TLDs .test, .example, .invalid oder .localhost. Ein Versand an diese Domains kann nur bouncen, was Ihre Absender-Reputation kostet.

Versand vor der Domain-Verifizierung

Während des Onboardings können Sie über unsere gemeinsame Onboarding-Domain onboarding@messagebird.dev senden. Diese Sends überspringen die Domain-Prüfung, erreichen aber nur verifizierte Mitglieder Ihres eigenen Workspace und Sandbox-Adressen, mit einem täglichen Empfängerlimit. Der Quickstart enthält die genauen Regeln und Limits.

Payload aufbauen

Empfänger

to, cc und bcc akzeptieren jeweils bis zu 50 Adressen, und to benötigt mindestens eine. Jeder Eintrag ist ein einfacher E-Mail-String, ein RFC-5322-Mailbox-String (Jane <jane@acme.com>) oder ein Objekt mit optionalem Anzeigenamen.
Empfänger auf der Unterdrückungsliste des Workspace lassen den Request nicht fehlschlagen. Er gibt weiterhin 202 zurück, und jeder unterdrückte Empfänger erscheint auf den Lese-Endpunkten als status: rejected mit dem Grund recipient_suppressed – auch wenn das jeden Empfänger des Versands betrifft.

Inhalt

subject ist für Inline-Sends erforderlich, bis zu 998 Zeichen. Geben Sie html, text oder beides an, jeweils bis zu 524.288 Zeichen. Senden Sie nach Möglichkeit beides: Ein Client, der kein HTML rendern kann, fällt auf den Text-Part zurück.
Um Inline-Inhalte zu personalisieren, setzen Sie {{ variable }}-Tokens in Betreff oder Body und übergeben deren Werte in parameters, bis zu 16 KB serialisiert. Ein Wertesatz gilt für alle Empfänger des Versands, und ein Token ohne passenden Schlüssel wird leer gerendert. Für wiederverwendbare Inhalte senden Sie stattdessen ein Template.
Fügen Sie parameters hinzu, auch als leeres Objekt ({}), um Betreff und Body als Liquid zu verarbeiten. Lassen Sie es weg, um Tokens wie {{ animal }} unverändert zu senden. Jeder Parametername ist ein einzelnes Wort, z. B. first_name; Namen mit Punkt und der reservierte Name bird werden abgelehnt. Ungültige Liquid-Syntax und nicht unterstützte Tags oder Filter geben 422 zurück.
In HTML eingefügte Werte werden escaped, damit sie das umgebende Markup nicht verändern können. Für eine vollständige Link- oder Bild-URL verwenden Sie {{ link }} ohne url_encode. Für einen Wert innerhalb einer URL-Query kodieren Sie den Wert explizit, zum Beispiel https://example.com/search?q={{ query | url_encode }}.

Reply-to und benutzerdefinierte Header

reply_to akzeptiert 1 bis 25 Adressen in denselben Formaten wie Empfänger. Jede Empfänger-Antwort geht an alle davon, daher sind ein oder zwei üblich.
headers ist ein String-zu-String-Objekt für Ihre eigenen Header, zum Beispiel {"X-Campaign": "spring-2026"}, begrenzt auf 25 Header mit Werten bis zu 998 Zeichen. Drei Arten von Headern werden mit einem 422 abgelehnt:
  • Adressierungs- und Plattform-Header. Legen Sie die Adressierung der Nachricht über die dafür vorgesehenen Felder fest (from, to, cc, bcc, reply_to, subject). Diese Namen und die Header, die wir für Sie generieren (Content-Type, Content-Transfer-Encoding, DKIM-Signature, Received, Return-Path), können hier nicht gesetzt werden.
  • List-Unsubscribe und List-Unsubscribe-Post bei einem marketing-Send. Wir setzen einen konformen One-Click-Unsubscribe-Header bei diesen selbst. Bei einem transactional-Send belassen wir Ihre genau so, wie Sie sie gesetzt haben.
  • Jeder Wert mit einem Wagenrücklauf oder Zeilenumbruch.

Tracking

track_opens und track_clicks sind standardmäßig true. Setzen Sie eines davon auf false, um die Open-Pixel-Einbettung oder das Link-Rewriting bei diesem Versand zu überspringen. Tracking und Metriken beschreibt, was jede Option in der Nachricht ändert.

Kategorie und IP-Pool

category klassifiziert den Inhalt und legt die Unterdrückungsrichtlinie fest: marketing blockiert die Zustellung bei jedem Unterdrückungsgrund und bei jedem Opt-out, und transactional stellt trotz einer Beschwerde-Unterdrückung oder eines reinen Marketing-Opt-outs zu (ein für alle Nachrichten erfasstes Opt-out blockiert ebenfalls). Der Standardwert ist die Kategorie des Templates bei einem Template-Send und ansonsten marketing. Setzen Sie transactional daher explizit für Quittungen, Passwortzurücksetzungen und andere operationale E-Mails. Kategorien beschreibt die Auswahl. E-Mails, die über SMTP eingereicht werden, übernehmen ihre Kategorie stattdessen aus der SMTP-Konfiguration des Schlüssels.
ip_pool_id wählt den Versandpool: eine Pool-ID (ipp_...) oder ipp_shared, um explizit über den gemeinsamen Pool zu routen. Lassen Sie es weg, um den Standardpool Ihrer Organisation zu verwenden. Ein unbekannter Pool oder einer ohne verfügbare dedizierte IPs zum Senden wird mit einem 422 abgelehnt.

Feldreferenz

FeldTypErforderlichLimits und Hinweise
fromaddressjaMuss auf einer verifizierten Domain oder der Onboarding-Domain liegen
toaddress[]ja1 bis 50
cc, bccaddress[]neinJeweils bis zu 50
subjectstringInline-SendsBis zu 998 Zeichen; bei Template-Sends weglassen
html, textstringmindestens einsJeweils bis zu 524.288 Zeichen; bei Template-Sends weglassen
reply_toaddress[]nein1 bis 25; Antworten gehen an jede aufgeführte Adresse
headersobject (string → string)neinBis zu 25; reservierte Namen werden abgelehnt (siehe Benutzerdefinierte Header)
parametersobjectneinWerte für {{ tokens }} in Inline-Inhalten; bis zu 16 KB serialisiert; gemeinsam für alle Empfänger
tags{name, value}[]neinBis zu 20; Name ≤ 32 Zeichen, Wert ≤ 64 Zeichen; nur [A-Za-z0-9_-]; Namen pro Send eindeutig
metadataobjectneinBeliebige JSON, bis zu 2 KB serialisiert
track_opensbooleanneinStandard true
track_clicksbooleanneinStandard true
categorystringneinmarketing oder transactional; Standard ist die Kategorie des Templates bei einem Template-Send, sonst marketing
ip_pool_idstringneinipp_... oder ipp_shared; weglassen für den Standardpool Ihrer Organisation
templateobjectneinVeröffentlichtes Template per id oder slug senden, mit parameters für seine Variablen und optionalem language
attachmentsobject[]neinBis zu 20; siehe Anhänge
scheduled_atRFC-3339-ZeitstempelneinInline-Inhalt oder template zeitgesteuert senden; siehe Zeitgesteuerter Versand

Versand mit einem Template

Anstelle von Inline-Inhalten senden Sie ein veröffentlichtes Template: Setzen Sie template auf ein Objekt, das es per id (emt_...) oder per slug benennt – genau eines von beiden –, mit den Variablenwerten in template.parameters. Lassen Sie subject, html und text weg, da das Template diese bereits enthält.
const msg = await bird.email.send({
  from: { email: "onboarding@messagebird.dev", name: "Bird" },
  to: ["delivered@messagebird.dev"],
  category: "transactional",
  template: {
    slug: "welcome-email",
    parameters: { first_name: "Jane" },
  },
});
console.log(msg.id, msg.status);
Der Inhalt eines Templates ist Liquid. Neben einfacher {{ variable }}-Substitution können Sie Filter, {% if %}-Bedingungen und {% for %}-Schleifen verwenden. Personalisierung mit Variablen listet die wenigen Konstrukte auf, die eine Veröffentlichung ablehnt. template.parameters enthält die Werte für die eigenen Parameter des Templates, nach Name zugeordnet. Fehlt einer, wird der Versand mit einem 422 abgelehnt, der ihn benennt. Alles andere am Versand verhält sich wie beim Inline-Send, einschließlich Empfänger, tags, metadata, Tracking und Anhänge. Was für einen Template-Send spezifisch ist:
  • Inline oder Template, niemals beides. Das gleichzeitige Senden von template zusammen mit subject, html oder text wird mit einem 422 abgelehnt. Der API lehnt auch Variablenwerte im Top-Level-Feld parameters ab; bei einem Template-Send gehören sie in template.parameters.
  • bird ist der einzige reservierte Name. Ein Platzhalter-Pfad, der mit bird. beginnt, benennt unsere eigenen Daten, wie den Abmeldelink oder den Kontaktdatensatz des Empfängers. Daher darf ein template.parameters-Schlüssel nicht bird heißen. Alle anderen Schlüssel können Sie frei definieren, und jeder ist ein einzelnes flaches Wort: {"order_number": "A-1043"} füllt {{ order_number }}.
  • Ein Template kann sofort oder später senden. Fügen Sie scheduled_at hinzu, um den Versand zu planen. Wir pinnen die veröffentlichte Version, die gewählte Sprache und die Parameterwerte bei der Annahme. Löschen Sie das Template vor dem Versandzeitpunkt, wird die Nachricht mit generation_failure abgelehnt.
  • Ein Versand verwendet die veröffentlichte Version des Templates. Entwürfe werden nie gesendet. Ein unbekanntes Template wird mit einem 404 abgelehnt, und ein Template ohne veröffentlichte Version mit einem 422.
  • language wählt eine der Sprachen des Templates. Lassen Sie es weg, um die Standardsprache des Templates zu senden. Fordern Sie eine Sprache an, die das Template nicht hat, und die eigene on_missing_language-Einstellung entscheidet, ob stattdessen die nächste Übereinstimmung gesendet wird oder der Versand abgelehnt wird. Ein Template, das language_source_required setzt, lehnt einen Versand ab, der gar keine Sprache benennt.
  • Die Kategorie des Templates ist ein Standardwert, und Ihre überschreibt ihn. Lassen Sie category weg, und der Versand erbt die Kategorie des Templates. Ein transaktionales Template muss sie also nicht bei jedem Aufruf wiederholen.
E-Mail-Templates behandelt Erstellung, Veröffentlichung und die Konstrukte, die ein Template enthalten kann.

Tags vs. Metadaten

Beide hängen Ihre eigenen Daten an einen Versand an und unterscheiden sich darin, wie Sie sie später abfragen:
  • tags sind strukturierte {name, value}-Paare: bis zu 20 pro Send, Name bis zu 32 Zeichen, Wert bis zu 64, nur ASCII-Buchstaben, Ziffern, Unterstrich und Bindestrich, und Namen innerhalb des Sends eindeutig. Tags sind Filterdimensionen, sodass Sie die Nachrichtenliste nach Tag filtern und Analytics sowie Dashboard-Rollups nach Tag aufschlüsseln können. Verwenden Sie sie für Labels mit geringer Kardinalität wie campaign, experiment_variant oder source.
  • metadata ist ein beliebiges JSON-Objekt, bis zu 2 KB serialisiert. Wir speichern es, geben es bei API-Lesevorgängen zurück und liefern es bei jedem Webhook-Event mit. Damit eignet es sich für Kontext, den Sie zurückerhalten möchten: interne IDs, Fremdschlüssel, strukturierte Payloads.
Jedes Webhook-Event enthält beides zusammen mit den Korrelations-IDs (email_id, recipient_id), sodass Sie ohne eine zweite Abfrage mit Ihren eigenen Datensätzen abgleichen können. Tag-Namen und Top-Level-Metadaten-Schlüssel, die mit __bird beginnen, werden abgelehnt. Sie müssen Gerät, Geografie, Mailbox-Provider, Bounce-Typ oder Empfänger-Domain nicht in eines der beiden Felder kodieren, da wir jedes davon bereits als Analytics-Dimension erfassen.
Codebeispiel
{
  "tags": [{ "name": "campaign", "value": "onboarding" }],
  "metadata": { "user_id": "usr_12345", "order_id": "ord_98765" }
}

Anhänge

attachments akzeptiert bis zu 20 Dateien pro Nachricht als base64-kodierte Bytes inline. Wir lehnen einen Versand ab, dessen geschätzte generierte Nachrichtengröße 20 MB überschreitet, gemessen nach der base64-Kodierung. Halten Sie den Rohinhalt der Anhänge daher bei maximal 15 MB, um Spielraum zu haben. Anhänge beschreibt den Feldvertrag, Inline-Bilder, die blockierten Dateitypen und wie Sie einen Anhang wieder herunterladen.

Was eine 202 bedeutet

Ein erfolgreicher Versand gibt 202 Accepted mit einer em_-präfixierten Nachrichten-ID und status: accepted zurück:
Codebeispiel
{
  "id": "em_01ky7ma8y2es1s2akzk53tmjn0",
  "status": "accepted",
  "category": "marketing",
  "from": { "email": "hello@yourdomain.com" },
  "to": [{ "email": "delivered@messagebird.dev" }],
  "subject": "Hello from Bird",
  "accepted_count": 1,
  "processed_count": 0,
  "delivered_count": 0,
  "deferred_count": 0,
  "bounced_count": 0,
  "complained_count": 0,
  "rejected_count": 0,
  "open_count": 0,
  "click_count": 0,
  "track_opens": true,
  "track_clicks": true,
  "created_at": "2026-07-23T13:58:20.866Z"
}
Der 202 bedeutet, dass wir den Versand dauerhaft angenommen haben. Fehler, die Sie beheben können, kommen direkt im Request als 422 zurück: eine nicht verifizierte Absender-Domain oder ein Feld, das nicht validiert. Empfängerspezifische Ergebnisse (zugestellt, gebounct, zurückgestellt, Beschwerde) treffen danach über Webhooks und die Nachrichten-Lese-Endpunkte ein.
Daraus folgen zwei Dinge:
  • Lesevorgänge liefern den Status ohne den Body. GET /v1/email/messages/{message_id} gibt den Nachrichten- und Empfängerstatus zurück, nie den html- oder text-Body. Wenn die Inhaltsspeicherung für den Workspace aktiviert ist, bleiben gespeicherte Bodys bis zu 30 Tage über GET /v1/email/messages/{message_id}/content verfügbar.
  • Ein Lesevorgang kann dem Versand kurz hinterherhinken. Ein 404 auf den Lese-Endpunkten direkt nach einem 202 bedeutet, dass die Nachricht noch nicht sichtbar ist. Versuchen Sie es nach einem kurzen Moment erneut.

Sicheres erneutes Senden

Senden Sie einen Idempotency-Key-Header mit einem eindeutigen Wert pro logischem Versand. Wenn ein Request erfolgreich war, Sie die Antwort aber nie erhalten haben, wiederholen Sie ihn mit demselben Schlüssel. Der API gibt das ursprüngliche Ergebnis zurück, anstatt eine zweite E-Mail zu senden, und enthält einen Idempotency-Replay-Header. Idempotenz beschreibt das Schlüsselformat und die Aufbewahrungsdauer.

Batch-Versand

Um API-Requests zu reduzieren, akzeptiert POST /v1/email/batches bis zu 100 unabhängige Nachrichten und validiert sie als eine Einheit. Das Aufrufen des Single-Send-Endpunkts in einer Schleife wird ebenfalls unterstützt. Ein Batch-Element verwendet das Payload auf dieser Seite, einschließlich scheduled_at, sodass ein Batch sofortige und zeitgesteuerte Nachrichten mischen kann.

Abrechnung

E-Mail-Sends werden pro Empfänger gegen das monatliche Kontingent Ihres Plans gezählt. Eine Nachricht an drei Empfänger verbraucht also drei Sends. Abrechnung und Nutzung beschreibt das Zählmodell und den Live-Nutzungsabruf.

Nächste Schritte