Saltar para a documentação
API

Enviar um email

POST /emails: uma mensagem, agora ou mais tarde.

POSTapi.openemail.uk/emails

Executa a chamada real contra o seu espaço de trabalho, com a sua própria chave.

O pedido

from é obrigatório. Ao contrário do compositor, não há remetente alternativo, porque essa alternativa é o endereço predefinido do espaço de trabalho e muda de forma invisível à medida que os endereços vão e vêm.

CampoObrigatórioNotas
fromsimUm endereço simples ou Name <addr>. Tem de ser um em nome do qual a chave possa enviar.
tosimAté 50 destinatários no total, entre to, cc e bcc.
cc, bccnãoOs destinatários em bcc nunca são nomeados nos bytes que qualquer outra pessoa recebe.
subjectnãoVazio por omissão.
html, textum deAmbos é aceitável. O HTML é o que os destinatários veem.
templateum de{ id, version?, props?, slots? }. Um corpo guardado, por id ou por slug. Recusado em conjunto com html, text ou draftId. Ver Enviar com um template.
replyTonãoUm único endereço.
headersnãoX-*, List-*, Reply-To, Precedence, Auto-Submitted, Importance, Priority, Feedback-Id.
attachmentsnão{ filename, content, contentType } em base64, 5 MB no total, ou { fileId } a nomear um ficheiro que já está no workspace. 20 ficheiros.
attachmentDeliverynãomime, link ou auto. auto passa os ficheiros a ligação a partir dos 2 MB, num domínio com um domínio de ficheiros ativo. Por omissão, segue a definição da caixa de correio.
threadIdnãoResponder dentro de uma conversa existente.
draftIdnãoEnviar um rascunho existente.
scheduledAtnãoInstante ou duração ISO. Ver Agendamento.
cancellableForSecondsnãoUma janela de anulação de 0 a 900 segundos num envio imediato. Recusada em conjunto com scheduledAt, que se mantém cancelável até ser enviada. Ver Agendamento.
signaturenãofalse deixa a assinatura de fora desta mensagem. Caso contrário, leva a assinatura do endereço a partir do qual é enviada, que é a própria desse endereço ou então a definida para Todos os endereços.
tagsnãoAté 10 etiquetas suas. Devolvidas tal e qual, nunca interpretadas.
trackingnão{ opens?, clicks? }. Qualquer um deles sobrepõe-se à definição para esta mensagem; omita um campo e essa metade recai sobre a definição do endereço a partir do qual é enviada, ou então sobre Todos os endereços, e está ligada a menos que uma dessas a tenha desligado.
translatenão{ to, from?, subject?, includeOriginal? }. Envia-a na língua do destinatário. Resolvido quando o pedido é aceite, recusado em conjunto com draftId.

Os campos desconhecidos são rejeitados em vez de ignorados, pelo que um nome mal escrito é um 422 agora, em vez de uma surpresa mais tarde. Os cabeçalhos que derrotariam a autorização do remetente (From, Sender, Bcc, Message-ID, Return-Path e outros) são recusados com reserved_header.

A resposta

200 quando a mensagem já saiu, 202 quando ainda lhe tem de acontecer alguma coisa. Quem ramifica pelo código de estado acerta nos dois casos.

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 é o identificador duradouro que guarda, e aquele com que um evento de entrega volta, já que um webhook de devolução o nomeia como emailId. messageId é o Message-ID de RFC 5322 e é null até o MIME existir. Não faça a correlação por ele: o serviço de envio reescreve esse cabeçalho à saída, pelo que o valor aqui não aparece em nenhum relatório de devolução ou de entrega e uma correspondência por ele nunca dispara.

Na língua do destinatário

translate escreve a mensagem na língua de outra pessoa antes de ela sair. O corpo, e o assunto a não ser que desligue isso, é traduzido no momento em que o pedido é ACEITE, que é a mesma regra que template segue e é determinante pelos mesmos motivos: uma mensagem agendada leva as palavras que foram aprovadas em vez do que um modelo produzir na terça-feira, e uma tradução que não pôde ser produzida recusa o envio antes de existir uma linha. Nada é entregue numa língua que o remetente não escolheu.

translate

tostringobrigatório
A língua em que escrever: um código BCP-47 (`de`), um nome em inglês («German») ou o nome da própria língua («Deutsch»), de 2 a 60 caracteres. As três formas são normalizadas para o código da tabela antes de mais nada, pelo que são um só pedido, o que importa porque a impressão digital de Idempotency-Key é tirada sobre o pedido já interpretado. Os aliases também resolvem: `zh-TW` passa a `zh-Hant`. Um que não resolva para nada dá um 422 em `translate.to`.
fromstring
Aquilo em que a escreveu, em qualquer uma das mesmas três formas. É puramente uma otimização. Se for omitido, o corpo é lido e a língua deduzida, o que custa uma chamada curta ao modelo. Vale a pena indicá-la num caminho de grande volume, e vale a pena indicá-la quando o corpo é sobretudo nomes, números e ligações: a deteção abstém-se em vez de adivinhar, e uma origem indeterminada não lhe custa nada além da língua nomeada na legenda sobre o seu original. Não é o `from` de topo, que é um endereço.
subjectboolean
Traduzir também a linha de assunto. True por omissão; false envia o assunto exatamente como o escreveu.
includeOriginalboolean
Colocar o que escreveu de facto por baixo da tradução, atrás de um separador e legendado na língua do destinatário. True por omissão, e vale a pena deixar ligado. É a única coisa que permite a quem lê verificar uma frase que soe estranha, em vez de lhe ser pedido que confie num modelo cujo resultado nenhum dos dois consegue ver.
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 é aditivo e aparece apenas numa mensagem que foi traduzida: nesta resposta e em GET /emails/{id}, nunca numa linha de lista, porque uma lista não vai buscar o pedido guardado e o silêncio dela aí não diz nada num sentido nem noutro. Traz códigos em vez de linhas de língua completas: é um registo do que foi feito, e é em GET /languages que vive o endónimo. O subject na resposta é o traduzido, para que uma consola nunca liste uma mensagem sob uma cadeia que o destinatário nunca viu.

  • Funciona com template, e é esse o caso útil: o que é traduzido é o resultado RENDERIZADO, pelo que um corpo guardado serve todas as línguas em que os seus clientes leem. Um template que renderiza um documento inteiro é primeiro desmontado: só o que está dentro de <body> chega ao modelo, e o doctype, os blocos <style> e as regras @font-face são recolocados à volta da resposta. É também por isso que o limite de 30 000 caracteres mede a prosa e não o documento: uma mensagem de duas linhas embrulhada numa folha de estilos de marca é uma mensagem de duas linhas.
  • A única parte de um template que fica por traduzir é o seu <title>, que nenhum cliente de correio mostra. Um <Preview> de react-email renderiza dentro do corpo e é traduzido com o resto.
  • Recusado com draftId: um 422 em translate, com a mensagem "A draft is sent as it was written; translate a body or send a draft, not both". Um rascunho foi escrito por uma pessoa e é enviado tal como ela o deixou.
  • Deliberadamente fora da impressão digital de idempotência. O que é hasheado é o pedido que enviou, translate incluído; o que o modelo produziu não é. Assim, repetir um envio sem resposta com a mesma Idempotency-Key reproduz o original. A mensagem que já existe volta, sem um segundo envio e sem uma segunda tradução. Hashear o texto produzido faria com que uma repetição honesta tivesse uma impressão digital diferente de cada vez, que é como a mesma mensagem sai duas vezes.
  • Uma mensagem traduzida que esteja em fila ou agendada fica congelada contra alterações de texto. Mude-lhe a hora ou cancele-a; alterar o que diz implica cancelar e enviar de novo, à frente de alguém que consiga ler as palavras novas.
  • Um destino da direita para a esquerda é produzido da direita para a esquerda: a tradução embrulhada em dir="rtl", com o seu original por baixo orientado por si. O atributo sobrevive ao sanitizador de saída, que permite dir exatamente por este motivo, pelo que a mensagem que segue no fio leva a direção que a pré-visualização mostrou.
CódigoEstadoQuando
`invalid_parameter`422translate.to ou translate.from nomeia uma língua que não conseguimos situar. A mensagem diz quais as três formas aceites e remete para GET /languages.
`unknown_language`422A mesma falha apanhada um passo mais à frente, pelo serviço e não pelo esquema. Uma rede de segurança, em translate.to.
`translation_too_long`422Mais de 30 000 caracteres numa das pontas da chamada ao modelo. Uma recusa em vez de um truncamento: meia mensagem traduzida não tem costura que mostre onde parou, e quem a lê age sobre a metade que recebeu.
`translation_not_configured`409O workspace não tem chave de IA e a IA da plataforma está desligada. Um 409 e não um 503, porque a repetição falha de forma idêntica. Nada foi enviado. Envie sem translate se a intenção era enviá-la tal como está escrita.
`translation_failed`503O fornecedor não respondeu, ou respondeu com algo inaproveitável. Nada foi enviado; a mensagem nunca é publicada por traduzir como plano B. Esta falha é nossa e vale a pena repetir o pedido.
`unknown_parameter`422Uma chave não reconhecida dentro de translate, que é um objeto estrito como o resto do pedido.

Um envio a partir de código não tem ninguém a ler a tradução primeiro. POST /emails/translate é a mesma ida e volta parada um passo antes, para mostrar a uma pessoa aquilo que está prestes a enviar. Depois envie o que ela aprovou como um html/subject normal, sem translate nenhum no pedido.