Ga direct naar de documentatie
API

Een e-mail versturen

POST /emails: één bericht, nu of later.

POSTapi.openemail.uk/emails

Voert de echte aanroep uit op je workspace, met je eigen sleutel.

Het verzoek

from is verplicht. Anders dan in de composer is er geen terugvalafzender, want die terugval is het standaardadres van de workspace en dat verandert onzichtbaar naarmate adressen komen en gaan.

VeldVerplichtOpmerkingen
fromjaEen kaal adres of Name <addr>. Moet er een zijn waarvandaan de sleutel mag verzenden.
tojaMaximaal 50 ontvangers over to, cc en bcc samen.
cc, bccneeBcc-ontvangers worden nooit genoemd in de bytes die iemand anders ontvangt.
subjectneeStandaard leeg.
html, texteen vanAllebei mag. HTML is wat ontvangers zien.
templateeen van{ id, version?, props?, slots? }. Een opgeslagen inhoud, op id of op slug. Wordt geweigerd samen met html, text of draftId. Zie Verzenden met een sjabloon.
replyToneeEén enkel adres.
headersneeX-*, List-*, Reply-To, Precedence, Auto-Submitted, Importance, Priority, Feedback-Id.
attachmentsnee{ filename, content, contentType } als base64, in totaal 5 MB, of { fileId } dat een bestand aanwijst dat al in de workspace staat. 20 bestanden.
attachmentDeliveryneemime, link of auto. auto maakt er links van zodra bestanden boven de 2 MB komen op een domein met een actief bestandsdomein. Standaard de instelling van de mailbox.
threadIdneeAntwoorden in een bestaande thread.
draftIdneeEen bestaand concept verzenden.
scheduledAtneeISO-tijdstip of duur. Zie Plannen.
cancellableForSecondsneeEen venster van 0 tot 900 seconden om een directe verzending ongedaan te maken. Wordt geweigerd samen met scheduledAt, dat tot het verzenden annuleerbaar blijft. Zie Plannen.
signatureneefalse laat de handtekening weg bij dit bericht. Anders draagt het de handtekening van het adres waarvandaan het wordt verzonden: de eigen handtekening van dat adres of anders die is ingesteld voor All addresses.
tagsneeMaximaal 10 eigen labels. Worden teruggegeven, nooit geïnterpreteerd.
trackingnee{ opens?, clicks? }. Elk van beide overschrijft de instelling voor dit bericht; laat een veld weg en die helft valt terug op de instelling van het adres waarvandaan het wordt verzonden, of anders op All addresses, en hij staat aan tenzij een van die twee hem heeft uitgezet.
translatenee{ to, from?, subject?, includeOriginal? }. Verstuurt het in de taal van de ontvanger. Wordt bepaald wanneer het verzoek wordt geaccepteerd, en geweigerd samen met draftId.

Onbekende velden worden geweigerd in plaats van genegeerd, dus een verkeerd gespelde naam is nu een 422 in plaats van later een verrassing. Headers die de afzenderautorisatie zouden ondermijnen (From, Sender, Bcc, Message-ID, Return-Path en andere) worden geweigerd met reserved_header.

De respons

200 wanneer het bericht al weg is, 202 wanneer er nog iets mee moet gebeuren. Een aanroeper die op de statuscode vertakt heeft het in beide gevallen goed.

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 is het duurzame handvat dat je bewaart, en degene waarop een bezorggebeurtenis terugkomt, want een bounce-webhook noemt het emailId. messageId is de RFC 5322 Message-ID en is null totdat de MIME bestaat. Correleer er niet op: de verzenddienst herschrijft die header onderweg naar buiten, dus de waarde hier komt in geen enkel bounce- of bezorgrapport voor en een match erop treedt nooit op.

In de taal van de ontvanger

translate schrijft het bericht in de taal van iemand anders voordat het vertrekt. De body, en het onderwerp tenzij je dat uitzet, wordt vertaald op het moment dat het verzoek wordt GEACCEPTEERD. Dat is dezelfde regel die template volgt en die om dezelfde redenen dragend is: een gepland bericht draagt de woorden die zijn goedgekeurd en niet wat een model op dinsdag produceert, en een vertaling die niet tot stand kwam weigert de verzending voordat er een rij bestaat. Niets wordt bezorgd in een taal die de afzender niet heeft gekozen.

translate

tostringverplicht
De taal om in te schrijven: een BCP-47-code (`de`), een Engelse naam ("German") of de eigen naam van de taal ("Deutsch"), 2 tot 60 tekens. Alle drie worden genormaliseerd naar de tabelcode voordat er iets anders gebeurt, dus zijn het één verzoek. Dat is van belang omdat de Idempotency-Key-vingerafdruk over het geparste verzoek wordt genomen. Aliassen worden ook herleid: `zh-TW` wordt `zh-Hant`. Eén die nergens naar herleidt is een 422 op `translate.to`.
fromstring
Waarin je het hebt geschreven, in elk van diezelfde drie vormen. Puur een optimalisatie. Laat je het weg, dan wordt de body gelezen en de taal afgeleid, wat één korte modelaanroep kost. De moeite waard om op te geven op een pad met veel volume, en de moeite waard wanneer de body vooral uit namen, getallen en links bestaat: detectie onthoudt zich liever dan te gokken, en een onbepaalde brontaal kost je niets behalve de taal die in het bijschrift boven je origineel wordt genoemd. Niet de `from` op het hoogste niveau, die een adres is.
subjectboolean
Vertaal ook de onderwerpregel. Standaard true; false verstuurt het onderwerp precies zoals je het hebt geschreven.
includeOriginalboolean
Zet wat je werkelijk hebt geschreven onder de vertaling, achter een scheidingslijn en met een bijschrift in de taal van de ontvanger. Standaard true, en de moeite waard om aan te laten. Het is het enige dat de lezer in staat stelt een zin die vreemd overkomt te controleren, in plaats van te moeten vertrouwen op een model waarvan geen van beiden de uitvoer kan zien.
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 is aanvullend en verschijnt alleen bij een bericht dat is vertaald: op deze respons en op GET /emails/{id}, nooit op een rij in een lijst, want een lijst haalt het opgeslagen verzoek niet op en het zwijgen daar zegt niets in welke richting dan ook. Het draagt codes in plaats van volledige taalrijen: het is een verslag van wat er is gedaan, en GET /languages is waar het endoniem staat. Het subject in de respons is het vertaalde, zodat een console een bericht nooit toont onder een tekst die de ontvanger nooit heeft gezien.

  • Werkt samen met template, en dat is het nuttige geval: de GERENDERDE uitvoer is wat er vertaald wordt, dus één opgeslagen body bedient elke taal waarin je klanten lezen. Een template die een heel document rendert wordt eerst uit elkaar gehaald: alleen wat binnen <body> staat bereikt het model, en de doctype, de <style>-blokken en de @font-face-regels worden er weer omheen gezet. Het is ook waarom de limiet van 30.000 tekens de tekst meet en niet het document: een bericht van twee regels in een stylesheet met huisstijl is een bericht van twee regels.
  • Het enige deel van een template dat onvertaald blijft is de <title>, die geen enkele mailclient toont. Een react-email <Preview> rendert in de body en wordt met de rest meevertaald.
  • Geweigerd in combinatie met draftId: een 422 op translate, met de tekst "A draft is sent as it was written; translate a body or send a draft, not both". Een concept is door een mens geschreven en wordt verstuurd zoals diegene het heeft achtergelaten.
  • Bewust geen onderdeel van de idempotentie-vingerafdruk. Wat gehasht wordt is het verzoek dat je hebt gestuurd, translate inbegrepen; wat het model produceerde niet. Een onbeantwoorde verzending opnieuw proberen met dezelfde Idempotency-Key speelt dus het origineel opnieuw af. Het bericht dat al bestaat komt terug, zonder tweede verzending en zonder tweede vertaling. De formulering hashen zou een eerlijke herhaalpoging elke keer een andere vingerafdruk geven, en zo gaat hetzelfde bericht twee keer de deur uit.
  • Een vertaald bericht dat in de wachtrij staat of is ingepland, ligt qua formulering vast. Verplaats het of annuleer het; veranderen wat er staat betekent annuleren en opnieuw versturen, waar iemand bij is die de nieuwe woorden kan lezen.
  • Een doeltaal die van rechts naar links loopt wordt ook van rechts naar links opgemaakt: de vertaling verpakt in dir="rtl", je origineel daaronder met zijn eigen oriëntatie. Het attribuut overleeft de uitgaande sanitiser, die dir precies hierom toestaat, zodat het bericht op de lijn de richting draagt die de preview liet zien.
CodeStatusWanneer
`invalid_parameter`422translate.to of translate.from noemt geen taal die wij kunnen plaatsen. Het bericht vermeldt welke drie vormen worden geaccepteerd en verwijst naar GET /languages.
`unknown_language`422Dezelfde fout, een stap later opgevangen door de service in plaats van door het schema. Een vangnet, op translate.to.
`translation_too_long`422Meer dan 30.000 tekens aan een van beide kanten van de modelaanroep. Een weigering in plaats van afkappen: een half vertaald bericht heeft geen naad die laat zien waar het ophield, en de lezer handelt naar de helft die hij heeft gekregen.
`translation_not_configured`409De workspace heeft geen AI-sleutel en platform-AI staat uit. Een 409 in plaats van een 503, omdat een nieuwe poging identiek faalt. Er is niets verzonden. Verstuur zonder translate als je het wilde versturen zoals het geschreven is.
`translation_failed`503De provider antwoordde niet, of antwoordde met niets bruikbaars. Er is niets verzonden; het bericht wordt nooit onvertaald verstuurd als terugvaloptie. Deze is van ons en is het opnieuw proberen waard.
`unknown_parameter`422Een niet-herkende sleutel binnen translate, dat net als de rest van het verzoek een strict object is.

Bij een verzending vanuit code leest niemand de vertaling eerst. POST /emails/translate is dezelfde rondgang die één stap eerder stopt, om iemand te laten zien wat hij op het punt staat te versturen. Verstuur daarna wat diegene heeft goedgekeurd als een gewone html/subject, zonder translate op het verzoek.