Zur Dokumentation springen
API

Eine E-Mail senden

POST /emails: eine Nachricht, jetzt oder später.

POSTapi.openemail.uk/emails

Führt den echten Aufruf gegen Ihren Workspace aus, mit Ihrem eigenen Schlüssel.

Die Anfrage

from ist Pflicht. Anders als im Verfassen-Fenster gibt es keinen Ersatzabsender, denn dieser Ersatz ist die Standardadresse des Workspace, und die ändert sich unsichtbar, während Adressen kommen und gehen.

FeldErforderlichHinweise
fromjaEine reine Adresse oder Name <addr>. Muss eine sein, als die der Schlüssel senden darf.
tojaBis zu 50 Empfänger über to, cc und bcc zusammen.
cc, bccneinBcc-Empfänger werden in den Bytes, die andere erhalten, nie genannt.
subjectneinStandardmäßig leer.
html, texteines vonBeides ist möglich. HTML ist das, was Empfänger sehen.
templateeines von{ id, version?, props?, slots? }. Ein gespeicherter Text, per id oder per slug. Wird zusammen mit html, text oder draftId abgelehnt. Siehe Mit einer Vorlage senden.
replyToneinEine einzelne Adresse.
headersneinX-*, List-*, Reply-To, Precedence, Auto-Submitted, Importance, Priority, Feedback-Id.
attachmentsnein{ filename, content, contentType } als base64, insgesamt 5 MB, oder { fileId } mit Verweis auf eine Datei, die bereits im Workspace liegt. 20 Dateien.
attachmentDeliveryneinmime, link oder auto. auto verlinkt Dateien, sobald sie 2 MB überschreiten, auf einer Domain mit aktiver Dateien-Domain. Standard ist die Einstellung des Postfachs.
threadIdneinAntwort in einen bestehenden Thread.
draftIdneinEinen bestehenden Entwurf senden.
scheduledAtneinISO-Zeitpunkt oder Dauer. Siehe Planung.
cancellableForSecondsneinEin Undo-Fenster von 0 bis 900 Sekunden bei einem sofortigen Versand. Wird zusammen mit scheduledAt abgelehnt, das ohnehin bis zum Versand abbrechbar bleibt. Siehe Planung.
signatureneinfalse lässt die Signatur bei dieser Nachricht weg. Andernfalls trägt sie die Signatur der Adresse, von der sie gesendet wird, also deren eigene oder sonst die für Alle Adressen festgelegte.
tagsneinBis zu 10 eigene Labels. Werden zurückgegeben, nie interpretiert.
trackingnein{ opens?, clicks? }. Jedes von beiden überschreibt die Einstellung für diese Nachricht; lassen Sie ein Feld weg, fällt diese Hälfte auf die Einstellung der Absenderadresse zurück, sonst auf Alle Adressen, und sie ist aktiv, sofern nicht eine davon sie abgeschaltet hat.
translatenein{ to, from?, subject?, includeOriginal? }. Versendet sie in der Sprache des Empfängers. Wird bei Annahme der Anfrage aufgelöst, zusammen mit draftId abgelehnt.

Unbekannte Felder werden abgelehnt statt ignoriert; ein falsch geschriebener Name ist also jetzt ein 422 statt später eine Überraschung. Header, die die Absenderautorisierung aushebeln würden (From, Sender, Bcc, Message-ID, Return-Path und andere), werden mit reserved_header abgelehnt.

Die Antwort

200, wenn die Nachricht bereits hinaus ist, 202, wenn noch etwas mit ihr geschehen muss. Ein Aufrufer, der über den Statuscode verzweigt, liegt in beiden Fällen richtig.

200 OK
{  "object": "email",  "id": "msg_c5f21cc6bfec4e848caf905b",  "status": "sent",  "mode": "live",  "from": "[email protected]",  "subject": "Your September invoice",  "messageId": "<2598…@acme.com>",  "transport": "ses",  "sentAt": "2026-08-29T08:19:08.000Z",  "source": "api",  "replayed": false}

id ist der dauerhafte Bezeichner, den Sie behalten, und derjenige, auf den ein Zustellereignis zurückverweist, denn ein Bounce-Webhook nennt ihn als emailId. messageId ist die RFC 5322 Message-ID und ist null, solange das MIME nicht existiert. Korrelieren Sie nicht darüber: Der Sendedienst schreibt diesen Header beim Hinausgehen um, sodass der Wert hier in keinem Bounce- oder Zustellbericht auftaucht und ein Abgleich darüber nie greift.

In der Sprache des Empfängers

translate verfasst die Nachricht vor dem Versand in der Sprache eines anderen. Der Textkörper, und sofern Sie es nicht abschalten auch der Betreff, wird in dem Moment übersetzt, in dem die Anfrage ANGENOMMEN wird – dieselbe Regel, der auch template folgt, und aus denselben Gründen tragend: Eine geplante Nachricht trägt die Worte, die freigegeben wurden, statt dessen, was ein Modell am Dienstag produziert, und eine Übersetzung, die nicht erzeugt werden konnte, verweigert den Versand, bevor eine Zeile existiert. Nichts wird in einer Sprache zugestellt, die sein Absender nicht gewählt hat.

translate

tostringerforderlich
Die Sprache, in der geschrieben werden soll: ein BCP-47-Code (`de`), ein englischer Name ("German") oder der Eigenname der Sprache ("Deutsch"), 2 bis 60 Zeichen. Alle drei werden vor allem Weiteren auf den Tabellencode normalisiert, sodass sie eine einzige Anfrage sind – was zählt, weil der Fingerabdruck des Idempotency-Key über die geparste Anfrage gebildet wird. Auch Aliase werden aufgelöst: `zh-TW` wird zu `zh-Hant`. Ein Wert, der sich zu nichts auflöst, ergibt ein 422 auf `translate.to`.
fromstring
Worin Sie es geschrieben haben, in einer der gleichen drei Formen. Reine Optimierung. Wird es weggelassen, wird der Textkörper gelesen und die Sprache ermittelt, was einen kurzen Modellaufruf kostet. Auf einem Pfad mit hohem Volumen lohnt sich die Angabe, ebenso wenn der Textkörper überwiegend aus Namen, Zahlen und Links besteht: Die Erkennung enthält sich lieber, als zu raten, und eine unbestimmte Ausgangssprache kostet Sie nichts außer der Sprachangabe in der Beschriftung über Ihrem Original. Nicht zu verwechseln mit dem `from` auf oberster Ebene, das eine Adresse ist.
subjectboolean
Auch die Betreffzeile übersetzen. Standardwert true; false sendet den Betreff exakt so, wie Sie ihn geschrieben haben.
includeOriginalboolean
Setzt das, was Sie tatsächlich geschrieben haben, unter die Übersetzung, hinter einen Trenner und in der Sprache des Empfängers beschriftet. Standardwert true, und es lohnt sich, es eingeschaltet zu lassen. Nur so kann die lesende Person einen Satz prüfen, der seltsam wirkt, statt einem Modell vertrauen zu müssen, dessen Ausgabe keiner von Ihnen beiden sehen kann.
curl
curl -X POST "$OE/emails" -H "$AUTH" -H "Content-Type: application/json" \  -d '{    "from": "[email protected]",    "to": ["[email protected]"],    "subject": "Your September invoice",    "html": "<p>Invoice attached. Payment is due on the 14th.</p>",    "translate": { "to": "de" }  }'
200 OK
{  "object": "email",  "id": "msg_c5f21cc6bfec4e848caf905b",  "status": "sent",  "from": "[email protected]",  "subject": "Ihre Rechnung für September",  "translation": {    "language": "de",    "languageName": "German",    "detectedSourceLanguage": "en",    "subject": true,    "includeOriginal": true  }}

translation ist additiv und erscheint nur bei einer Nachricht, die übersetzt wurde: in dieser Antwort und bei GET /emails/{id}, nie in einer Listenzeile, denn eine Liste ruft die gespeicherte Anfrage nicht ab, und ihr Schweigen dort besagt in keine Richtung etwas. Sie führt Codes statt ganzer Sprachzeilen: Sie ist eine Aufzeichnung dessen, was getan wurde, und GET /languages ist der Ort, an dem das Endonym steht. Der subject in der Antwort ist der übersetzte, sodass eine Konsole eine Nachricht nie unter einer Zeichenfolge aufführt, die der Empfänger nie gesehen hat.

  • Funktioniert mit template, und das ist der nützliche Fall: Übersetzt wird die GERENDERTE Ausgabe, sodass ein einziger gespeicherter Textkörper jede Sprache bedient, in der Ihre Kunden lesen. Ein Template, das ein ganzes Dokument rendert, wird zuvor zerlegt: Nur was innerhalb von <body> steht, erreicht das Modell, und der Doctype, die <style>-Blöcke und die @font-face-Regeln werden anschließend wieder um die Antwort herum gesetzt. Deshalb misst das Limit von 30.000 Zeichen auch den Fließtext und nicht das Dokument: Eine zweizeilige Nachricht, eingeschlagen in ein gebrandetes Stylesheet, ist eine zweizeilige Nachricht.
  • Der einzige Teil eines Templates, der unübersetzt bleibt, ist sein <title>, den kein Mail-Client anzeigt. Ein react-email-<Preview> rendert in den Textkörper und wird mit dem Rest übersetzt.
  • Zusammen mit draftId abgelehnt: ein 422 auf translate, mit dem Wortlaut "A draft is sent as it was written; translate a body or send a draft, not both". Ein Entwurf wurde von einem Menschen geschrieben und wird so gesendet, wie er ihn hinterlassen hat.
  • Bewusst nicht Teil des Idempotenz-Fingerabdrucks. Gehasht wird die Anfrage, die Sie gesendet haben, translate eingeschlossen; nicht aber, was das Modell produziert hat. Ein erneuter Versuch eines unbeantworteten Versands mit demselben Idempotency-Key gibt daher das Original wieder. Die bereits existierende Nachricht kommt zurück, ohne zweiten Versand und ohne zweite Übersetzung. Stattdessen den Wortlaut zu hashen würde bedeuten, dass ein ehrlicher Wiederholungsversuch jedes Mal einen anderen Fingerabdruck erhält – und genau so geht dieselbe Nachricht zweimal hinaus.
  • Eine übersetzte Nachricht, die in der Warteschlange steht oder geplant ist, ist gegen Änderungen am Wortlaut eingefroren. Verschieben oder stornieren Sie sie; zu ändern, was sie sagt, bedeutet stornieren und erneut senden, vor den Augen von jemandem, der die neuen Worte lesen kann.
  • Ein Ziel mit Rechts-nach-links-Schrift wird von rechts nach links erzeugt: die Übersetzung in dir="rtl" eingefasst, Ihr Original darunter mit eigener Ausrichtung. Das Attribut übersteht den ausgehenden Sanitiser, der dir genau aus diesem Grund zulässt, sodass die Nachricht auf der Leitung die Richtung trägt, die die Vorschau gezeigt hat.
CodeStatusWann
`invalid_parameter`422translate.to oder translate.from nennt keine Sprache, die wir zuordnen können. Die Meldung nennt die drei akzeptierten Formen und verweist auf GET /languages.
`unknown_language`422Derselbe Fehler, einen Schritt später abgefangen – vom Dienst statt vom Schema. Eine Absicherung, auf translate.to.
`translation_too_long`422Über 30.000 Zeichen an einem der beiden Enden des Modellaufrufs. Eine Ablehnung statt einer Kürzung: Eine halb übersetzte Nachricht hat keine Nahtstelle, die zeigt, wo sie abgebrochen ist, und die lesende Person handelt nach der Hälfte, die sie bekommen hat.
`translation_not_configured`409Der Workspace hat keinen KI-Schlüssel, und die Plattform-KI ist aus. Ein 409 statt eines 503, weil ein erneuter Versuch identisch scheitert. Es wurde nichts gesendet. Senden Sie ohne translate, wenn Sie es so verschicken wollten, wie es geschrieben steht.
`translation_failed`503Der Anbieter hat nicht geantwortet oder nichts Brauchbares geliefert. Es wurde nichts gesendet; die Nachricht wird niemals ersatzweise unübersetzt verschickt. Dieser Fehler liegt bei uns und ist einen erneuten Versuch wert.
`unknown_parameter`422Ein nicht erkannter Schlüssel innerhalb von translate, das wie der Rest der Anfrage ein striktes Objekt ist.

Bei einem Versand aus Code liest niemand die Übersetzung vorab. POST /emails/translate ist derselbe Roundtrip, einen Schritt früher angehalten, um einer Person zu zeigen, was sie gleich versendet. Senden Sie dann das Freigegebene als gewöhnliches html/subject, ganz ohne translate in der Anfrage.