Enviar um email
`emails.send`: uma mensagem, agora ou mais tarde.
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 e bcc aceitam um ou vários destinatários, e um único é envolvido num array automaticamente. Cada um pode ser um endereço simples, Name <addr@host> ou {'email': ..., 'name': ...}.
Parâmetros
fromRecipientInputobrigatório- O remetente. Um endereço simples, `Name <addr@host>`, ou um dicionário. Tem de ser um a partir do qual esta chave possa enviar. Não há remetente alternativo, por isso um envio indica sempre o endereço de onde sai.
toRecipientInput | list[RecipientInput]obrigatório- Um ou vários destinatários; um único é envolvido num array automaticamente. No máximo 50 no total de to, cc e bcc.
ccRecipientInput | list[RecipientInput]- Conta para o limite de 50 destinatários.
bccRecipientInput | list[RecipientInput]- Nunca aparece nos bytes que qualquer outra pessoa recebe, porque é transmitido um envelope por destinatário.
replyToRecipientInput- Um único endereço, enviado como cabeçalho Reply-To.
subjectstr- No máximo 998 caracteres, o limite de linha do RFC 5322. Vazio por predefinição.
htmlstr- É obrigatório um de html, text, draftId ou template. O HTML é o que os destinatários veem quando html e text são ambos fornecidos.
textstr- A parte em texto simples.
templateEmailSendTemplate- Renderizar um modelo guardado no servidor. `version` fixa a versão; omita-o para usar a que estiver publicada quando o pedido for aceite. Uma prop desconhecida ou em falta dá 422 em vez de um espaço em branco na mensagem.
draftIdstr- Enviar um rascunho guardado com este envelope.
headersdict[str, str]- `X-*`, `List-*`, Reply-To, Precedence, Auto-Submitted, Importance, Priority e Feedback-Id. Tudo o que o transporte define por si próprio é recusado em vez de ser descartado silenciosamente.
attachmentslist[AttachmentInput]- `{'filename': ..., 'content': ...}` com um `'contentType'` opcional, ou `{'fileId': ...}`, que refere um ficheiro já existente no espaço de trabalho, como um de `files.upload`. Passe bytes como conteúdo e são codificados em base64 automaticamente. 20 ficheiros, com os ficheiros inline limitados a 5 MB no total depois de descodificados. Um ficheiro guardado pode ser maior e segue como ligação de transferência.
attachmentDeliveryAttachmentDeliveryMode- `mime`, `link` ou `auto`. `auto` envia os ficheiros como ligações de transferência quando ultrapassam 2 MB num domínio com um domínio de ficheiros ativo, e dentro da mensagem nos restantes casos. Se for omitido, aplica-se a definição da caixa de correio, cuja predefinição é `auto`.
threadIdstr- Responder numa conversa existente. O transporte escreve In-Reply-To e References.
scheduledAtdatetime | str- Um `datetime`, um instante ISO-8601 ou uma duração como `PT1H`. Até um ano à frente, nunca no passado. Não pode ser combinado com cancellableForSeconds.
cancellableForSecondsint- De 0 a 900. Uma janela para anular num envio imediato: o mecanismo de anulação do editor de mensagens, exposto em vez de fixo no código.
tagsdict[str, str]- Até 10 etiquetas, devolvidas e filtráveis. Nunca interpretadas.
signaturebool- Se esta mensagem leva a assinatura do endereço de onde é enviada: a dele, senão a do catch-all para um endereço que um catch-all apanhou, senão o rodapé do OpenEmail, a menos que esse endereço o tenha desligado. Se for omitido, um corpo `html` sai exatamente como foi escrito, sem assinatura, e um corpo só `text` leva-a. Defina `False` no correio que um programa envia em nome de alguém, como um recibo, uma reposição de palavra-passe ou um resumo, nenhum dos quais quer a assinatura de uma pessoa por baixo.
trackingTrackingRequest- Indica se deve ser adicionado um píxel de abertura e se as ligações desta mensagem devem ser reescritas. Inativo a menos que o rastreio tenha sido ativado para o endereço a partir do qual é enviada (ou para o catch-all que o apanhou), e qualquer um dos campos indicados aqui decide para essa mensagem, independentemente da definição do endereço.
translateSendTranslateOptions- Enviar na língua do destinatário. `to` aceita um código, um nome em inglês ou o nome da língua na própria língua; `subject` e `includeOriginal` são ambos true por predefinição. Resolvido quando o pedido é aceite, pelo que uma mensagem agendada leva as palavras que foram aprovadas. Recusado em conjunto com `draftId`.
Resposta
idstr- O id do envio, `msg_…`. Use-o para `get`, `cancel`, `reschedule` e `get_tracking`.
statusEmailStatus- queued, scheduled, sending, sent, partial, bounced, cancelled ou failed. Leia este campo em vez de se basear no facto de a chamada ter terminado. `partial` é um estado próprio: alguns destinatários já a têm e não é possível anular o envio, pelo que tentar de novo é errado e reportar falha é falso.
modeApiKeyMode- Que tipo de chave o enviou. Um envio de teste é registado e nunca transmitido.
fromstr- O endereço efetivamente autorizado e colocado na linha, que nem sempre é o que foi pedido.
subjectstr | None- Tal como foi enviado.
messageIdstr | None- O Message-ID RFC 5322. Null até o MIME existir. O serviço de envio reescreve o cabeçalho à saída, por isso nenhuma devolução nem relatório de entrega transporta este valor. É em `id` que um evento regressa.
threadIdstr | None- A thread em que ficou.
transportEmailTransport | str | None- Como a mensagem saiu. Null até ao despacho.
attemptsint- Quantas vezes o despacho foi tentado.
lastErrorstr | None- Porque falhou a última tentativa, literalmente.
scheduledAtstr | None- Instante ISO em que deve sair.
cancellableUntilstr | None- Enquanto o momento atual for anterior a este, cancel continua a funcionar.
sentAtstr | None- Instante ISO em que saiu.
tagsdict[str, str]- O que enviou, devolvido tal e qual.
sourceEmailSource | str- composer, api, mcp, ai, oauth ou form: que superfície o pediu. `api` é este cliente com uma chave de API, e `oauth` é este cliente com um token de acesso.
createdAtstr- Instante ISO em que o registo foi escrito.
replayedbool- True quando uma Idempotency-Key correspondeu a um envio que já existia. Nada de novo foi enviado, e esta é a mensagem original.
translationNotRequired[EmailTranslationResource]- Presente apenas numa mensagem que foi traduzida, e apenas onde todo o pedido guardado é transportado: esta resposta e `get`. Um dicionário com `language`, `languageName`, `detectedSourceLanguage`, `subject` e `includeOriginal`, todos códigos em vez de linhas de idioma. Uma linha de listagem nunca o tem, por isso a sua ausência aí não diz nada num sentido nem no outro. Leia-o com `email.get('translation')`.
Na língua do destinatário
translate escreve a mensagem na língua de outra pessoa antes de ela partir. O corpo, e o assunto a menos que o desative, é traduzido quando a API aceita o pedido, e o que saiu é o que vai sair: uma tradução que não pôde ser produzida recusa o envio em vez de o publicar na língua em que o escreveu.
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'))Ninguém leu aquilo antes de partir. emails.translate é a mesma ida e volta parada um passo antes. Mostre-a a uma pessoa, deixe-a alterá-la e depois envie o que ela aprovou sem qualquer translate na chamada. Passá-lo outra vez traduziria uma segunda vez e deitaria fora as edições dela.
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')A tabela vem incluída, pela ordem do seletor, para que um seletor possa ser preenchido antes do primeiro pedido. languages.list() devolve as mesmas linhas vindas da rede sob a forma de uma lista simples, para quem prefira as atuais às que esta versão trouxe. resolve_language aceita um código, um nome em inglês, um endónimo ou um alias (zh-TW é um alias de um código que já não é listado), language_by_code faz corresponder um código exato sem distinguir maiúsculas de minúsculas, e dezasseis das linhas são da direita para a esquerda. Pesquise native, label e code em conjunto, mostre native primeiro e guarde o código.
emails.translate não é repetido automaticamente. Gasta chamadas ao modelo e não escreve nada, por isso não há nada para tornar idempotente e uma repetição após um pedido sem resposta só compraria a mesma resposta duas vezes.
- Um idioma que não resolve para nada é um
validation_erroremtranslate.to, antes de seja o que for ser enviado. translation_too_longacima de 30 000 caracteres,translation_not_configuredquando a instalação não tem IA configurada, um 429ai_quota_exceededquando o espaço de trabalho já gastou as ações de IA de hoje (é reposta à meia-noite UTC e não vale a pena repetir),translation_failedquando o fornecedor não respondeu. Nenhum deles envia a mensagem por traduzir como alternativa.- Funciona com
template: é o resultado RENDERIZADO que é traduzido, por isso um corpo guardado serve todas as línguas em que os seus clientes leem. Um template que renderiza um documento inteiro mantém o seu doctype, os seus blocos<style>e as suas regras@font-face: só o corpo vai para o modelo e o resto é reposto à volta dele. O seu<title>fica intacto, e nada o mostra de qualquer forma. - Uma repetição não custa nada a mais. A tradução não faz parte da impressão digital de idempotência (o pedido faz,
translateincluído), por isso repetir um envio sem resposta com a mesmaIdempotency-Keyreproduz a mensagem que já existe em vez de traduzir e enviar uma segunda. - Uma mensagem traduzida que esteja em fila ou agendada fica congelada contra alterações de texto.
emails.reschedulecontinua a movê-la; mudar o que diz significa cancelar e enviar de novo.
Anexos
content vai em base64 na rede. Passe bytes e são codificados por si.
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 é exportado se precisar dele noutro sítio. Um str em content é enviado tal como está, por isso já tem de estar em base64.