Enviar um email
POST /emails: uma mensagem, agora ou mais tarde.
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.
| Campo | Obrigatório | Notas |
|---|---|---|
| from | sim | Um endereço simples ou Name <addr>. Tem de ser um em nome do qual a chave possa enviar. |
| to | sim | Até 50 destinatários no total, entre to, cc e bcc. |
| cc, bcc | não | Os destinatários em bcc nunca são nomeados nos bytes que qualquer outra pessoa recebe. |
| subject | não | Vazio por omissão. |
| html, text | um de | Ambos é aceitável. O HTML é o que os destinatários veem. |
| template | um 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. |
| replyTo | não | Um único endereço. |
| headers | não | X-*, List-*, Reply-To, Precedence, Auto-Submitted, Importance, Priority, Feedback-Id. |
| attachments | não | { filename, content, contentType } em base64, 5 MB no total, ou { fileId } a nomear um ficheiro que já está no workspace. 20 ficheiros. |
| attachmentDelivery | não | mime, 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. |
| threadId | não | Responder dentro de uma conversa existente. |
| draftId | não | Enviar um rascunho existente. |
| scheduledAt | não | Instante ou duração ISO. Ver Agendamento. |
| cancellableForSeconds | não | Uma 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. |
| signature | não | false 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. |
| tags | não | Até 10 etiquetas suas. Devolvidas tal e qual, nunca interpretadas. |
| tracking | nã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. |
| translate | nã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.
{ "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 -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" } }'{ "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-facesã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 emtranslate, 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,
translateincluído; o que o modelo produziu não é. Assim, repetir um envio sem resposta com a mesmaIdempotency-Keyreproduz 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 permitedirexatamente por este motivo, pelo que a mensagem que segue no fio leva a direção que a pré-visualização mostrou.
| Código | Estado | Quando |
|---|---|---|
| `invalid_parameter` | 422 | translate.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` | 422 | A 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` | 422 | Mais 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` | 409 | O 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` | 503 | O 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` | 422 | Uma 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.