Saltar para a documentação
PHP

Enviar um email

`emails->send`: uma mensagem, agora ou mais tarde.

emails->send

send_email.php
$email = $client->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' => new \SplFileInfo('invoice.pdf')]],    'threadId' => 'CAHk7pQ2x9LmZ4-mail.example.com',    'scheduledAt' => 'PT1H',    'tags' => ['order' => '4021'],    'tracking' => ['opens' => true, 'clicks' => true],]); echo $email['id'], ' ', $email['status'], PHP_EOL;

to, cc e bcc aceitam um destinatário ou uma lista deles, e um único é envolvido automaticamente. Cada um pode ser um endereço simples, Name <addr@host> ou um array com email e name.

A mensagem é um único array com as chaves dos nomes de campo da API, e é por isso que replyTo e scheduledAt continuam em camelCase, enquanto idempotencyKey: e apiKey: são argumentos nomeados da chamada e nunca fazem parte da mensagem. Para alterar um campo de uma mensagem que construiu antes, espalhe-a num novo array: $client->emails->send([...$message, 'subject' => 'Re: your invoice']) mantém todos os outros campos e substitui o assunto.

Parâmetros

fromstring or arrayobrigatório
O remetente. Um endereço simples, `Name <addr@host>`, ou um array com `email` e `name`. Tem de ser um a partir do qual esta chave possa enviar, ou a chamada lança um 403 `from_address_forbidden`. Não há remetente alternativo, por isso um envio indica sempre o endereço de onde sai.
tostring or arrayobrigatório
Um destinatário ou uma lista deles, e um único é envolvido automaticamente. No máximo 50 no total de `to`, `cc` e `bcc`, e mais do que isso é um 422 `too_many_recipients`.
ccstring or array
Conta para o limite de 50 destinatários.
bccstring or array
Nunca aparece nos bytes que qualquer outra pessoa recebe, porque é transmitido um envelope por destinatário. Também conta para os 50.
replyTostring or array
Um único endereço, enviado como cabeçalho Reply-To.
subjectstring
No máximo 998 caracteres, o limite de linha do RFC 5322. Vazio por omissão, e um assunto vazio recorre ao do modelo ou ao do rascunho.
htmlstring
É 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. No máximo 1 000 000 de caracteres.
textstring
A parte de texto simples, no máximo 1 000 000 de caracteres.
templatearray
Renderizar um modelo guardado no servidor: um array com `id`, que aceita um id ou um slug, e `version` (um int), `props` e `slots` opcionais. `version` fixa uma revisã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.
draftIdstring
Enviar um rascunho guardado com este envelope, tal como foi escrito. Não pode ser combinado com `template` nem com `translate`.
headersarray
Nome de cabeçalho para valor string, limitado a `X-*`, `List-*`, Reply-To, Precedence, Auto-Submitted, Importance, Priority e Feedback-ID. Tudo o que o transporte define por si próprio é recusado com um 422 `reserved_header` em vez de ser descartado silenciosamente.
attachmentsarray
Uma lista, em que cada entrada é um array com `filename`, `content` e um `contentType` opcional, ou um array só com `fileId`, que refere um ficheiro já existente no espaço de trabalho, como um de `files->upload`. `content` é base64: um stream de `fopen`, um `SplFileInfo` ou um stream PSR-7 é lido e codificado por si, e uma string tem de já estar em base64. 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.
attachmentDeliverystring
`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`.
threadIdstring
Responder numa conversa existente. O transporte escreve In-Reply-To e References.
scheduledAtDateTimeInterface or string
Um `DateTimeInterface`, enviado como instante ISO 8601 em UTC, um instante ISO 8601 como string, ou uma duração como `PT1H`. Até um ano no futuro, nunca no passado. Não pode ser combinado com `cancellableForSeconds`. Uma string de data sem hora, como `2027-01-01`, é lida como meia-noite UTC desse dia, por isso passe um instante quando a hora importa.
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.
tagsarray
Até 10 etiquetas, com chaves de 1 a 64 letras, algarismos, `_` ou `-` e valores string de até 256 caracteres. Devolvidas tal como estão em cada leitura e 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-o como 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. Os envios com modelo e os envios cifrados nunca a levam.
trackingarray
Um array com `opens` e `clicks` opcionais, cada um um bool: 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 uma das chaves indicadas aqui decide para essa mensagem, independentemente da definição do endereço.
translatearray
Enviar na língua do destinatário: um array com `to` e `from`, `subject` e `includeOriginal` opcionais. `to` aceita um código, um nome em inglês ou o nome da língua na própria língua, e `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`.
idempotencyKeystring
Um argumento nomeado da chamada e não um campo da mensagem. A sua própria chave para este envio, de 1 a 255 caracteres entre letras, algarismos, `_`, `.`, `:` ou `-`. Sem ela, o cliente gera uma chave para cada chamada, por isso as suas próprias repetições nunca enviam duas vezes, e com ela um envio que corre de novo noutro processo é reproduzido em vez de repetido.
apiKeystring
Também um argumento nomeado. Envia com esta chave em vez da do cliente, para um processo que envia em nome de vários espaços de trabalho.

Resposta

Um array com as chaves dos nomes em camelCase da API, por isso $email['status'] lê o estado.

idstring
O id do envio, `msg_` seguido de 24 caracteres hexadecimais. Use-o para `get`, `cancel`, `reschedule` e `getTracking`.
statusstring
queued, scheduled, sending, sent, partial, bounced, cancelled ou failed. Leia este campo em vez de se basear no facto de a chamada ter terminado: um envio imediato é despachado dentro do pedido e normalmente volta como `sent`, `partial` ou `failed`, e um retido volta como `queued` ou `scheduled`. `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.
modestring
`live` ou `test`: que tipo de chave o enviou. Um envio de teste é registado e nunca transmitido. Aparece como `sent`, com `transport` igual a `test`, por isso verifique a resposta e não uma caixa de entrada.
fromstring
O endereço efetivamente autorizado e colocado na linha, que nem sempre é o que foi pedido.
subjectstring or null
Tal como foi enviado.
messageIdstring or null
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.
threadIdstring or null
A thread em que ficou.
transportstring or null
Como a mensagem saiu. null até ao despacho.
attemptsint
Quantas vezes o despacho foi tentado.
lastErrorstring or null
Porque falhou a última tentativa, literalmente.
scheduledAtstring or null
O instante ISO 8601 em que deve sair.
cancellableUntilstring or null
Enquanto o momento atual for anterior a este, `cancel` continua a funcionar.
sentAtstring or null
O instante ISO 8601 em que saiu.
tagsarray
O que enviou, devolvido tal e qual.
sourcestring
composer, api, mcp, ai ou queue: que superfície o pediu. `api` é este cliente.
createdAtstring
O instante ISO 8601 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 tal como está agora.
translationarray
Presente apenas numa mensagem que foi traduzida, e apenas onde todo o pedido guardado é transportado: esta resposta e `get`. Contém `language`, `languageName`, `detectedSourceLanguage`, `subject` e `includeOriginal`, com códigos em vez de linhas de idioma completas. Uma linha de listagem nunca o tem, por isso a sua ausência aí não diz nada num sentido nem no outro.

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.

translate.php
$email = $client->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_r($email['translation'] ?? []);

$email['translation'] contém então language igual a de, languageName igual a German, detectedSourceLanguage igual a en, e subject e includeOriginal ambos true.

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.

preview_translation.php
$preview = $client->emails->translate([    'subject' => 'Your September invoice',    'html' => '<p>Invoice attached. Payment is due on the 14th.</p>',    'to' => 'de',]); echo $preview['language']['native'], PHP_EOL, $preview['subject'], PHP_EOL, $preview['html'], PHP_EOL;echo 'Send it as it is? [y/N] '; $answer = fgets(STDIN); if ($answer !== false && strtolower(trim($answer)) === 'y') {    $client->emails->send([        'from' => '[email protected]',        'to' => '[email protected]',        'subject' => $preview['subject'],        'html' => $preview['html'],    ]);}
languages.php
use OpenEmail\Constants\Languages;use OpenEmail\OpenEmail; echo count(Languages::ALL), PHP_EOL; $current = $client->languages->list();echo count($current), PHP_EOL; echo OpenEmail::resolveLanguage('Deutsch')['code'] ?? 'none', PHP_EOL;echo OpenEmail::resolveLanguage('zh-TW')['code'] ?? 'none', PHP_EOL;echo OpenEmail::languageByCode('DE')['native'] ?? 'none', PHP_EOL;var_dump(OpenEmail::isRtlLanguage('ar'));

Essas linhas de código imprimem 200, o número de linhas com que esta versão vem, depois quantas a API tem agora, e depois de, zh-Hant, Deutsch e bool(true). A tabela vem incluída, pela ordem do seletor, como OpenEmail\Constants\Languages::ALL, uma lista de arrays com code, label, native, flag e rtl, 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. OpenEmail::resolveLanguage() 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) e devolve null quando nada corresponde, OpenEmail::languageByCode() faz corresponder um código exato sem distinguir maiúsculas de minúsculas, e OpenEmail::isRtlLanguage() indica se uma língua se lê da direita para a esquerda, como acontece com dezasseis das linhas. 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 a API não consegue identificar é um validation_error em translate.to, antes de seja o que for ser enviado.
  • translation_too_long acima de 30 000 caracteres, translation_not_configured quando a instalação não tem IA configurada, um 429 ai_quota_exceeded quando o espaço de trabalho já gastou as ações de IA de hoje (é reposta à meia-noite UTC e não é repetido), translation_failed quando 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, translate incluído), por isso repetir um envio sem resposta com a mesma Idempotency-Key reproduz a mensagem que já existe em vez de traduzir e enviar uma segunda.
  • Uma mensagem traduzida que esteja em fila ou agendada mantém o texto aprovado. emails->reschedule continua a movê-la, enquanto emails->update recusa um texto novo com um 409 translation_locked, por isso mudar o que diz significa cancelar e enviar de novo.

Anexos

content segue em base64 pela rede. Dê ao cliente algo que ele consiga ler e ele codifica os bytes por si: um recurso de stream de fopen, um SplFileInfo, ou um stream PSR-7 ou ficheiro carregado. Uma string é enviada tal como está, por isso tem de já estar em base64, que é o que OpenEmail::toBase64() faz dos bytes que tem em memória.

attachments.php
use OpenEmail\OpenEmail; $attachments = [    ['filename' => 'invoice.pdf', 'content' => OpenEmail::toBase64(file_get_contents('invoice.pdf')), 'contentType' => 'application/pdf'],    ['filename' => 'report.csv', 'content' => new \SplFileInfo('report.csv')],    ['filename' => 'contacts.csv', 'content' => fopen('contacts.csv', 'rb')],    ['fileId' => 'file_6bb640f5b99e47deb758f1f5'],]; $client->emails->send([    'from' => '[email protected]',    'to' => '[email protected]',    'subject' => 'Your documents',    'text' => 'All three are attached.',    'attachments' => $attachments,]);

Um content em string que não esteja em base64 lança OpenEmail\Exception\InvalidArgumentException antes de qualquer envio. Bytes em bruto que por acaso se leiam como base64 sairiam corrompidos, por isso nunca passe os bytes de um ficheiro tal como estão: envolva-os em OpenEmail::toBase64(), ou passe o próprio ficheiro.

OpenEmail::toBase64() está disponível se precisar da mesma codificação noutro sítio. Aceita uma string de bytes, um recurso de stream, um SplFileInfo ou um stream PSR-7 e devolve base64 sem quebras de linha.