API-Referenz
POST /api/v1/send ist die gesamte transaktionale Send API — ein einziger versionierter, stabiler Endpunkt. Anders als der Rest der Routen von MailInApp (die immer nur unser eigenes Frontend als Aufrufer haben und sich frei ändern können), ist dies ein Vertrag, auf den Code von Drittanbietern angewiesen ist, weshalb er von Anfang an versioniert ist.
Authentifizierung
Authorization: Bearer mia_live_...
Ein fehlender oder ungültiger Schlüssel liefert 401. Schlüssel werden unter Entwickler im Dashboard verwaltet — siehe den Schnellstart. Ein widerrufener Schlüssel funktioniert sofort nicht mehr.
Anfrage
Content-Type: application/json. Zwei Anfrageformen teilen sich den Endpunkt — welche zutrifft, wird dadurch entschieden, ob projectId vorhanden ist.
Freiform
| Feld | Typ | Erforderlich | Hinweise |
| --- | --- | --- | --- |
| to | string | ja | Eine einzelne Empfängeradresse. |
| subject | string | ja | Auf 200 Zeichen gekürzt. |
| html | string | ja | Wird unverändert gesendet — kein Rendering, keine Merge-Tags, keine Studio-Pipeline. |
| text | string | ja | Reiner Textteil. |
Vorlage
| Feld | Typ | Erforderlich | Hinweise |
| --- | --- | --- | --- |
| to | string | ja | Eine einzelne Empfängeradresse. |
| projectId | string | ja | Muss ein Projekt sein, das Ihnen gehört — die Studio-URL lautet /studio/<projectId>. |
| mergeData | object | nein | Flaches { key: value }-Objekt, bis zu 100 Felder. Steht für eine Datenquellen-Zeile — löst sich in die {{field}}-Merge-Tags des Projekts auf. Verschachtelte Objekte/Arrays werden verworfen; null/undefined werden zu leeren Strings; alles andere wird in einen String umgewandelt. |
| subject | string | nein | Standardmäßig der Name des Projekts, falls weggelassen. Merge-Tags im Betreff lösen sich aus mergeData auf. Auf 200 Zeichen gekürzt. |
Ein Body, der zu keiner der beiden Formen passt (z. B. fehlendes to, oder fehlendes html/text und projectId zugleich), liefert 400.
Gemeinsame Felder
| Feld | Typ | Erforderlich | Hinweise |
| --- | --- | --- | --- |
| type | "transactional" | "marketing" | nein | Standardmäßig "transactional". Siehe unten. |
| from | object | nein | Override der Absenderadresse/des Namens pro Aufruf — { "email": string, "name"?: string }. Siehe unten. |
| senderId | string | nein | Override pro Aufruf, indem eine der gespeicherten Absenderidentitäten Ihres Kontos ausgewählt wird, statt from inline auszuschreiben. Muss zu Ihrem Konto gehören, sonst 400. from gewinnt, wenn beide angegeben sind. |
| replyTo | string | nein | Reply-To-Adresse pro Aufruf. Siehe unten. |
Header
| Header | Erforderlich | Hinweise |
| --- | --- | --- |
| Authorization | ja | Bearer <apiKey>. |
| Idempotency-Key | nein | Siehe Idempotenz. |
Antwort
{ "id": "abc123", "status": "sent" }
status ist einer der folgenden Werte:
| Status | Bedeutung |
| --- | --- |
| sent | Erfolgreich an Ihre Versandmethode übergeben (SMTP-Relay oder natives Senden). |
| suppressed | Der Empfänger steht auf Ihrer Sperrliste — siehe Transaktional vs. Marketing. Es wurde keine E-Mail gesendet; das ist kein Fehler. |
| failed | Der Versandversuch ist fehlgeschlagen (z. B. ist Ihr SMTP-Relay nicht konfiguriert, oder es hat die Nachricht abgelehnt). Ein Feld error enthält eine für Menschen lesbare Begründung. |
Fehlercodes
| Status | Bedeutung |
| --- | --- |
| 400 | Fehlerhaftes JSON, eine Anfrage, die weder zur Freiform- noch zur Vorlagen-Form passt, ein ungültiger type, ein ungültiges oder nicht erlaubtes from (siehe From address), ein ungültiges replyTo, oder (im Vorlagenmodus) eine projectId, die nicht existiert oder Ihnen nicht gehört. Wird auch zurückgegeben, wenn für das Konto keine funktionierende Versandmethode konfiguriert ist. |
| 401 | Fehlender oder ungültiger Authorization-Header, oder der Schlüssel wurde widerrufen. |
| 404 | (Vorlagenmodus) das Projekt existiert nicht oder gehört nicht Ihrem Konto — bewusst dieselbe Antwort wie „existiert nicht", um nicht zu verraten, welche Projekt-IDs für andere Konten gültig sind. |
| 429 | Rate Limit überschritten — siehe Rate Limits. |
Ein 200 mit status: "failed" (statt eines Nicht-2xx-Status) wird zurückgegeben, wenn die Anfrage selbst gültig war, der eigentliche Versandversuch aber im weiteren Verlauf fehlgeschlagen ist. Wenn Sie unterscheiden müssen zwischen „wir haben Ihre Anfrage abgelehnt" und „wir haben es versucht, und es ist nicht rausgegangen", prüfen Sie status im Body, nicht nur den HTTP-Statuscode.
Transaktional vs. Marketing
Das Feld type steuert, welche Sperrliste geprüft wird, und spiegelt, wie ESPs transaktionale und Marketing-Streams trennen:
type: "transactional"(Standard) — übergeht die Abmeldungs-Sperrung. Ein Passwort-Reset oder eine Bestellbestätigung sollte nicht blockiert werden, nur weil sich der Empfänger von Ihrem Newsletter abgemeldet hat. Sie übergeht nie die Bounce-Sperrung — eine tote Adresse ist tot, unabhängig von der Absicht.type: "marketing"— verhält sich genau wie ein Kampagnenversand aus dem Dashboard: blockiert sowohl durch Abmeldungs- als auch durch Bounce-Sperrung.
Beide Fälle liefern bei einer Blockierung status: "suppressed" statt eines Fehlers.
From address
Standardmäßig verwendet jeder Versand die konfigurierte Absenderidentität Ihres Kontos — die Karte „From address" unter Einstellungen → Domains, falls Sie natives (SES-)Senden nutzen, oder ansonsten die konfigurierte From-Adresse Ihres SMTP-Relays. Übergeben Sie from, um sie für einen einzelnen Aufruf zu überschreiben:
{
"to": "[email protected]",
"subject": "Your one-time code",
"html": "<p>Your code is 123456</p>",
"text": "Your code is 123456",
"from": { "email": "[email protected]", "name": "FitConsent Sales Team" }
}
from.email ist immer erforderlich, sobald from überhaupt vorhanden ist — es gibt kein reines Namens-Override, ein Wert hier ersetzt für diesen Aufruf also immer sowohl die Adresse als auch den Anzeigenamen vollständig. from.name ist optional; lassen Sie es weg, um nur mit der bloßen Adresse zu senden.
Falls Ihr Konto über eine verifizierte Domain sendet (natives/SES-Senden), muss from.email eine Adresse auf einer Ihrer eigenen verifizierten Sende-Domains sein (z. B. [email protected], nicht [email protected]) — ein Konto kann mehr als eine Domain verifizieren, sodass jede davon erlaubt ist, nur nie die eines anderen. Natives Senden läuft auf einem einzigen, plattformweiten AWS-Konto, das sich alle MailInApp-Kunden teilen, weshalb diese Einschränkung ein Konto davon abhält, Mail zu senden, die scheinbar von der verifizierten Domain eines anderen Kontos stammt. Beim SMTP-Senden gibt es keine solche Einschränkung: Der Versand läuft über Ihr eigenes Relay/Ihre eigenen Zugangsdaten, es ist also bereits genauso vertrauenswürdig, wie es die eigenen Absenderverifizierungsregeln Ihres Relays bereits sind.
senderId (siehe die Tabelle der gemeinsamen Felder oben) ist meist die einfachere Wahl, wenn Sie im Dashboard bereits eine Absenderidentität eingerichtet haben. Es löst sich in das eigene {email, name, reply-to} dieser Identität auf, ohne dass Sie diese bei jedem Aufruf wiederholen müssten.
Ein ungültiges oder nicht erlaubtes from liefert 400, bevor überhaupt ein Versandversuch unternommen wird.
Reply-To-Adresse
Standardmäßig gehen Antworten an das konfigurierte Standard-Reply-To Ihres Kontos, festgelegt auf der Seite Sending oder Domains unter Einstellungen, je nach Ihrer Versandmethode, oder nirgendwohin, falls Sie keines festgelegt haben. Übergeben Sie replyTo, um es für einen einzelnen Aufruf zu überschreiben — nützlich, wenn das sichtbare from eine No-Reply-Adresse ist, Sie aber trotzdem möchten, dass ein Mensch die Antworten sieht:
{
"to": "[email protected]",
"subject": "Your order shipped",
"html": "<p>Your order is on its way.</p>",
"text": "Your order is on its way.",
"from": { "email": "[email protected]", "name": "FitConsent" },
"replyTo": "[email protected]"
}
Anders als from.email hat replyTo beim nativen (SES-)Senden keine Domain-Einschränkung — es beeinflusst nie die Sendeidentität oder den Zustellbarkeits-Ruf, es ist nur ein Header, den der Mail-Client des Empfängers berücksichtigt, wenn er auf Antworten klickt. Ein ungültiges replyTo liefert 400, bevor überhaupt ein Versandversuch unternommen wird.
Idempotenz
Übergeben Sie einen Idempotency-Key-Header bei jedem Aufruf, der wiederholt werden könnte — eine erneute Zustellung eines Checkout-Webhooks, ein Queue-Consumer, der eine Nachricht erneut verarbeitet. Ein wiederholter Aufruf mit demselben Schlüssel gegen dasselbe Konto liefert das {id, status} des ursprünglichen Aufrufs zurück, ohne eine zweite E-Mail zu senden — selbst wenn der erste Aufruf noch läuft.
Schlüssel sind pro Konto begrenzt und laufen nie ab; die Wiederverwendung eines Schlüssels, den Sie bereits für einen anderen Payload verwendet haben, zu vermeiden liegt in Ihrer eigenen Verantwortung (es wird trotzdem das Ergebnis des ersten Aufrufs zurückgegeben, der neue Payload wird nicht gesendet). Wenn Sie keinen Schlüssel übergeben, sendet jeder Aufruf.
Rate Limits
Drei unabhängige Obergrenzen gelten:
- Pro API-Schlüssel: eine Obergrenze für Anfragen pro Minute. Ein Überschreiten liefert
429speziell für diesen Schlüssel — andere Schlüssel desselben Kontos sind nicht betroffen. - Pro Konto, Send API-Kontingent: Ihr Tarif enthält eine Anzahl von Send-API-Aufrufen pro Kalendermonat (500/Monat bei Free). Ein Überschreiten liefert
429, bis das Kontingent am 1. zurückgesetzt wird. - Pro Konto, Versandvolumen: die kumulative monatliche Versandvolumen-Obergrenze Ihres Tarifs, geteilt über jeden Versandweg (Dashboard-Versände, geplante Versände, Lifecycle-E-Mails und diese API). Das ist dieselbe Obergrenze, die den restlichen Versand Ihres Kontos schützt — die Send API bekommt kein separates Budget.
Besonderheiten des Vorlagenmodus
Ein Versand im Vorlagenmodus rendert das verknüpfte Projekt genau wie ein normaler Empfängerversand: die drei Stufen der Fallback-Engine, interaktive Blöcke und ein persönlicher, signierter Live-Ansicht-Link, aufgebaut aus mergeData statt aus einer gespeicherten Kontaktzeile. Alles Nachgelagerte verhält sich genauso wie bei einem aus dem Studio gesteuerten Versand:
- Abstimmungsstimmen, Bewertungen, Formularabsendungen und Öffnungen werden erfasst und erscheinen in der Antworten-Ansicht des Projekts, zugeordnet zu diesem konkreten API-Aufruf statt zu einer Kontaktzeile.
- Der Webhook Ihres Projekts feuert pro Interaktionsereignis, genau wie bei jedem anderen Empfänger.
- Aktuelle Aufrufe (Status, Empfänger, Zeitstempel) werden auf der Dashboard-Seite Entwickler als Audit-Trail aufgeführt.
Der eine Unterschied zu einem Dashboard-Versand: Es gibt keine gespeicherte Datenquellen-Zeile hinter der Interaktion, weshalb Joins auf Ihrer eigenen Seite sich an der Empfänger-Kennung der Antwort orientieren sollten statt an einem Zeilenindex.