Eine E-Mail senden
`emails.send`: eine Nachricht, jetzt oder später.
emails.send
from openemail import openemail email = openemail.emails.send({ 'from': {'email': '[email protected]', 'name': 'Acme Billing'}, 'to': ['[email protected]', 'Grace <[email protected]>'], 'cc': '[email protected]', 'bcc': [{'email': '[email protected]'}], 'replyTo': '[email protected]', 'subject': 'Your September invoice', 'html': '<p>Invoice attached.</p>', 'text': 'Invoice attached.', 'headers': {'X-Campaign': 'invoices'}, 'attachments': [{'filename': 'invoice.pdf', 'content': pdf_bytes}], 'threadId': 'thread_…', 'scheduledAt': 'PT1H', 'tags': {'order': '4021'}, 'tracking': {'opens': True, 'clicks': True},})to, cc und bcc nehmen einen oder mehrere Empfänger entgegen, und ein einzelner wird für Sie verpackt. Jeder darf eine blanke Adresse, Name <addr@host> oder {'email': ..., 'name': ...} sein.
Parameter
fromRecipientInputerforderlich- Der Absender. Eine bloße Adresse, `Name <addr@host>` oder ein dict. Muss eine sein, als die dieser Schlüssel senden darf. Es gibt keinen Ersatzabsender, ein Versand nennt also immer die Adresse, von der er ausgeht.
toRecipientInput | list[RecipientInput]erforderlich- Ein oder mehrere Empfänger; ein einzelner wird für Sie verpackt. Höchstens 50 über to, cc und bcc zusammen.
ccRecipientInput | list[RecipientInput]- Zählt gegen das Limit von 50 Empfängern.
bccRecipientInput | list[RecipientInput]- Wird in den Bytes, die andere erhalten, nie genannt, denn pro Empfänger wird ein eigener Umschlag übertragen.
replyToRecipientInput- Eine einzelne Adresse, wird als Reply-To-Header gesendet.
subjectstr- Höchstens 998 Zeichen, das Zeilenlimit von RFC 5322. Standardmäßig leer.
htmlstr- Eines von html, text, draftId oder template ist erforderlich. HTML ist das, was Empfänger sehen, wenn sowohl html als auch text angegeben sind.
textstr- Der Nur-Text-Teil.
templateEmailSendTemplate- Rendert ein gespeichertes Template serverseitig. `version` schreibt eine Revision fest; lassen Sie es weg, um das zu verwenden, was bei Annahme der Anfrage veröffentlicht ist. Ein unbekanntes oder fehlendes prop ergibt ein 422 und keine Lücke in der Nachricht.
draftIdstr- Sendet einen gespeicherten Entwurf unter diesem Umschlag.
headersdict[str, str]- `X-*`, `List-*`, Reply-To, Precedence, Auto-Submitted, Importance, Priority und Feedback-Id. Alles, was der Transport selbst setzt, wird abgelehnt statt stillschweigend verworfen.
attachmentslist[AttachmentInput]- `{'filename': ..., 'content': ...}` mit einem optionalen `'contentType'` oder `{'fileId': ...}`, das eine bereits im Workspace liegende Datei benennt, etwa eine aus `files.upload`. Übergeben Sie Bytes als content, dann werden sie für Sie base64-kodiert. 20 Dateien, wobei eingebettete Dateien nach dem Dekodieren zusammen auf 5 MB begrenzt sind. Eine gespeicherte Datei darf größer sein und reist als Download-Link.
attachmentDeliveryAttachmentDeliveryMode- `mime`, `link` oder `auto`. `auto` überträgt Dateien als Download-Links, sobald sie 2 MB überschreiten und die Domain eine aktive Dateien-Domain hat, und andernfalls innerhalb der Nachricht. Weggelassen gilt die Postfacheinstellung, die standardmäßig `auto` ist.
threadIdstr- Antwort in einen bestehenden Thread. Der Transport schreibt In-Reply-To und References.
scheduledAtdatetime | str- Ein `datetime`, ein ISO-8601-Zeitpunkt oder eine Dauer wie `PT1H`. Bis zu ein Jahr voraus, nie in der Vergangenheit. Lässt sich nicht mit cancellableForSeconds kombinieren.
cancellableForSecondsint- 0 bis 900. Ein Rückgängig-Fenster bei einem sofortigen Versand: der Rückgängig-Mechanismus des Composers, offengelegt statt fest verdrahtet.
tagsdict[str, str]- Bis zu 10 Labels, werden zurückgegeben und sind filterbar. Werden nie interpretiert.
signaturebool- Ob diese Nachricht die Signatur der Absenderadresse trägt: die eigene dieser Adresse, sonst bei einer von einem Catch-all aufgefangenen Adresse die des Catch-all, sonst die OpenEmail-Fußzeile, sofern diese Adresse sie nicht ausgeschaltet hat. Ohne Angabe geht ein `html`-Text genau so hinaus, wie er geschrieben ist, ohne Signatur, und ein reiner `text`-Text trägt sie. Setzen Sie `False` für Mail, die ein Programm im Namen einer Person sendet, etwa eine Quittung, ein Passwort-Reset oder eine Zusammenfassung, unter denen niemand die Unterschrift einer Person erwartet.
trackingTrackingRequest- Ob für diese Nachricht ein Öffnungs-Pixel hinzugefügt und Links umgeschrieben werden. Inaktiv, sofern das Tracking nicht für die Absenderadresse (oder den Catch-all, der sie aufgefangen hat) eingeschaltet wurde, und jedes hier angegebene Feld entscheidet diese eine Nachricht, unabhängig davon, wie die Adresse eingestellt ist.
translateSendTranslateOptions- Sendet sie in der Sprache des Empfängers. `to` nimmt einen Code, einen englischen Namen oder den Eigennamen der Sprache entgegen; `subject` und `includeOriginal` sind beide standardmäßig true. Wird bei Annahme der Anfrage aufgelöst, eine geplante Nachricht trägt daher genau die freigegebenen Worte. Wird zusammen mit `draftId` abgelehnt.
Antwort
idstr- Die Versand-id, `msg_…`. Verwenden Sie sie für `get`, `cancel`, `reschedule` und `get_tracking`.
statusEmailStatus- queued, scheduled, sending, sent, partial, bounced, cancelled oder failed. Lesen Sie dies und nicht die Tatsache, dass der Aufruf zurückgekehrt ist. `partial` ist ein eigener Zustand: Einige Empfänger haben die Nachricht, und das lässt sich nicht rückgängig machen, ein erneuter Versuch ist daher falsch und einen Fehlschlag zu melden ist eine Lüge.
modeApiKeyMode- Welche Art von Schlüssel sie gesendet hat. Ein Testversand wird erfasst und nie übertragen.
fromstr- Die tatsächlich autorisierte und auf die Leitung gegebene Adresse, die nicht immer die angeforderte ist.
subjectstr | None- Wie gesendet.
messageIdstr | None- Die Message-ID nach RFC 5322. Null, bis das MIME existiert. Der Versanddienst schreibt den Header auf dem Weg hinaus um, kein Bounce und kein Zustellbericht trägt daher diesen Wert. `id` ist das, worüber ein Ereignis zurückkommt.
threadIdstr | None- Der Thread, in dem sie gelandet ist.
transportEmailTransport | str | None- Wie die Nachricht hinausging. Null bis zum Versand.
attemptsint- Wie oft der Versand versucht wurde.
lastErrorstr | None- Warum der letzte Versuch fehlschlug, wörtlich.
scheduledAtstr | None- ISO-Zeitpunkt, zu dem sie hinausgehen soll.
cancellableUntilstr | None- Solange die aktuelle Zeit davor liegt, funktioniert cancel noch.
sentAtstr | None- ISO-Zeitpunkt, zu dem sie hinausging.
tagsdict[str, str]- Was Sie gesendet haben, zurückgegeben.
sourceEmailSource | str- composer, api, mcp, ai, oauth oder form: welche Oberfläche angefragt hat. `api` ist dieser Client mit einem API-Schlüssel, und `oauth` ist dieser Client mit einem Zugriffstoken.
createdAtstr- ISO-Zeitpunkt, zu dem der Datensatz geschrieben wurde.
replayedbool- True, wenn ein Idempotency-Key auf einen bereits existierenden Versand passte. Es wurde nichts Neues gesendet, und dies ist die ursprüngliche Nachricht.
translationNotRequired[EmailTranslationResource]- Nur bei einer übersetzten Nachricht vorhanden, und nur dort, wo die gesamte gespeicherte Anfrage mitgeführt wird: in dieser Antwort und bei `get`. Ein dict aus `language`, `languageName`, `detectedSourceLanguage`, `subject` und `includeOriginal`, durchweg Codes statt Sprachzeilen. Eine Listenzeile hat es nie, sein Fehlen dort sagt daher in keine Richtung etwas aus. Lesen Sie es mit `email.get('translation')`.
In der Sprache des Empfängers
translate verfasst die Nachricht vor dem Versand in der Sprache eines anderen. Der Body und, sofern Sie das nicht abschalten, der Betreff werden übersetzt, sobald die API die Anfrage annimmt, und was dabei herauskam, geht hinaus: Eine Übersetzung, die nicht erzeugt werden konnte, lässt den Versand scheitern, statt die Nachricht in der Sprache zu verschicken, in der Sie sie geschrieben haben.
from openemail import openemail email = openemail.emails.send({ 'from': '[email protected]', 'to': '[email protected]', 'subject': 'Your September invoice', 'html': '<p>Invoice attached. Payment is due on the 14th.</p>', 'translate': {'to': 'de'},}) print(email.get('translation'))Niemand hat das gelesen, bevor es hinausging. emails.translate ist derselbe Weg, einen Schritt früher angehalten. Zeigen Sie das Ergebnis einer Person, lassen Sie sie es ändern und senden Sie dann das Freigegebene ganz ohne translate am Aufruf. Es erneut zu übergeben würde ein zweites Mal übersetzen und ihre Änderungen verwerfen.
from openemail import openemail preview = openemail.emails.translate({ 'subject': 'Your September invoice', 'html': '<p>Invoice attached. Payment is due on the 14th.</p>', 'to': 'de',}) print(preview['language']['native'], preview['detectedSourceLanguage'])print(preview['html']) approved_subject = input(f"Subject [{preview['subject']}]: ") or preview['subject'] or '' openemail.emails.send({ 'from': '[email protected]', 'to': '[email protected]', 'subject': approved_subject, 'html': preview['html'] or '',})from openemail import LANGUAGES, is_rtl_language, language_by_code, openemail, resolve_language current = openemail.languages.list() german = resolve_language('Deutsch')traditional = resolve_language('zh-TW')upper = language_by_code('DE') assert len(LANGUAGES) == 200assert german is not None and german['code'] == 'de'assert traditional is not None and traditional['code'] == 'zh-Hant'assert upper is not None and upper['native'] == 'Deutsch'assert is_rtl_language('ar')Die Tabelle ist mitgeliefert, in der Reihenfolge des Auswahlfelds, ein Auswahlfeld lässt sich daher schon vor der ersten Anfrage füllen. languages.list() gibt dieselben Zeilen von der Leitung als einfache Liste zurück, für Aufrufer, denen die aktuellen lieber sind als die, mit denen diese Version ausgeliefert wurde. resolve_language nimmt einen Code, einen englischen Namen, ein Endonym oder einen Alias entgegen (zh-TW ist ein Alias eines nicht mehr gelisteten Codes), language_by_code trifft einen exakten Code ohne Beachtung der Groß- und Kleinschreibung, und sechzehn der Zeilen laufen von rechts nach links. Durchsuchen Sie native, label und code gemeinsam, zeigen Sie native zuerst und speichern Sie den Code.
emails.translate wird nicht automatisch wiederholt. Es verbraucht Modellaufrufe und schreibt nichts, es gibt also nichts idempotent zu machen, und eine Wiederholung nach einer unbeantworteten Anfrage würde dieselbe Antwort nur zweimal bezahlen.
- Eine Sprache, die auf nichts auflöst, ergibt einen
validation_errorauftranslate.to, bevor irgendetwas gesendet wird. translation_too_longbei über 30.000 Zeichen,translation_not_configured, wenn für die Installation keine KI konfiguriert ist, ein 429ai_quota_exceeded, wenn der Workspace die KI-Aktionen für heute verbraucht hat (es wird um Mitternacht UTC zurückgesetzt und nicht erneut versucht),translation_failed, wenn der Anbieter nicht geantwortet hat. Keiner dieser Fälle sendet die Nachricht ersatzweise unübersetzt.- Funktioniert mit
template: Übersetzt wird die GERENDERTE Ausgabe, ein gespeicherter Body bedient daher jede Sprache, in der Ihre Kunden lesen. Ein Template, das ein ganzes Dokument rendert, behält seinen doctype, seine<style>-Blöcke und seine@font-face-Regeln: Nur der Body geht an das Modell, der Rest wird wieder darum gelegt. Sein<title>bleibt unangetastet, was ohnehin nirgends angezeigt wird. - Eine Wiederholung kostet nichts zusätzlich. Die Übersetzung ist nicht Teil des Idempotenz-Fingerabdrucks (die Anfrage schon,
translateeingeschlossen), eine Wiederholung eines unbeantworteten Versands mit demselbenIdempotency-Keyspielt daher die bereits existierende Nachricht erneut ab, statt ein zweites Mal zu übersetzen und zu senden. - Eine übersetzte Nachricht, die queued oder scheduled ist, ist gegen Änderungen am Wortlaut eingefroren.
emails.rescheduleverschiebt sie weiterhin; ihren Inhalt zu ändern bedeutet abbrechen und erneut senden.
Anhänge
content ist auf der Leitung base64. Übergeben Sie Bytes, dann werden sie für Sie kodiert.
from pathlib import Path from openemail.types import AttachmentInput attachments: list[AttachmentInput] = [ { 'filename': 'invoice.pdf', 'content': Path('invoice.pdf').read_bytes(), 'contentType': 'application/pdf', },]to_base64 wird exportiert, falls Sie es anderswo brauchen. Ein str in content wird unverändert gesendet, er muss also bereits base64 sein.