Saltar para a documentação
PHP

Difusões

`broadcasts->preview`, `send`, `list`, `listAll`, `iterate`, `get`, `listRecipients`, `listAllRecipients`, `iterateRecipients`, `getRecipient`, `stats`, `analytics` e `cancel`.

Todos os métodos

broadcasts.php
use OpenEmail\Constants\BroadcastRecipientFilters;use OpenEmail\Constants\BroadcastStatuses; $draft = [    'audienceIds' => ['aud_4c1b8e2a7d9f05c36b4e8a71'],    'from' => 'Acme <[email protected]>',    'subject' => '{{firstName|Hello}}, the September release is out',    'html' => '<p>Hi {{firstName|there}},</p><p>Here is what changed this month.</p>',    'text' => 'Hi {{firstName|there}}, here is what changed this month. Unsubscribe: {{unsubscribeUrl}}',    'tags' => ['campaign' => 'release-2026-09'],]; $reach = $client->broadcasts->preview($draft);echo $reach['recipients'], ' ', $reach['unsubscribed'], ' ', $reach['suppressed'], PHP_EOL; $broadcast = $client->broadcasts->send($draft); $latest = $client->broadcasts->get($broadcast['id']); while (in_array($latest['status'], [BroadcastStatuses::SCHEDULED, BroadcastStatuses::QUEUED, BroadcastStatuses::SENDING], true)) {    sleep(5);    $latest = $client->broadcasts->get($broadcast['id']);} foreach ($client->broadcasts->iterateRecipients($broadcast['id']) as $copy) {    echo $copy['email'], ' ', $copy['status'], ' ', $copy['opens'], ' ', $copy['clicks'], PHP_EOL;} $bounced = $client->broadcasts->listRecipients($broadcast['id'], filter: BroadcastRecipientFilters::BOUNCED); foreach ($bounced as $row) {    echo $row['emailId'], ' ', $row['email'], PHP_EOL;} $copy = $client->broadcasts->getRecipient($broadcast['id'], 'msg_01dad25067bc4dac966d515d');echo $copy['subject'], ' ', $copy['bouncedAt'] ?? 'not bounced', PHP_EOL; $stats = $client->broadcasts->stats($broadcast['id'], grain: 'day');echo $stats['totals']['opened'], ' ', $stats['totals']['clicked'], ' ', $stats['totals']['unsubscribed'], PHP_EOL; $lately = $client->broadcasts->stats($broadcast['id'], days: 1);echo $lately['window']['opened'] ?? 0, PHP_EOL; $later = $client->broadcasts->send([...$draft, 'scheduledAt' => 'P1D']);$client->broadcasts->cancel($later['id']); $history = $client->broadcasts->list(audienceId: $draft['audienceIds'][0]);echo $latest['status'], ' ', $latest['counts']['sent'], ' ', count($history), PHP_EOL; $month = $client->broadcasts->analytics(days: 30); foreach ($month['broadcasts'] as $row) {    echo $row['subject'], ' ', $row['sent'], ' ', $row['opened'], PHP_EOL;}

Uma difusão envia uma mensagem para todos em uma ou mais audiências, como cópia separada para cada pessoa. Cada cópia tem exatamente um destinatário e nem cc nem bcc, por isso ninguém vê para quem mais foi, e cada cópia é um e-mail normal com o seu próprio id msg_, eventos, seguimento e webhooks. listRecipients lista-as com o que aconteceu a cada uma. As cópias não são arquivadas na pasta Enviados, porque a difusão é o registo.

send devolve de imediato a difusão em queued, ou em scheduled quando o corpo traz scheduledAt, e o envio continua em segundo plano. send precisa de emails:send e audiences:read, e preview precisa de audiences:read. list, listAll, iterate, get, listRecipients, listAllRecipients, iterateRecipients, getRecipient, stats e analytics precisam de emails:read, e cancel precisa de emails:send.

Cada send leva uma Idempotency-Key, a sua através de idempotencyKey: ou uma que o cliente cria, por isso uma repetição após uma falha de rede responde com a difusão que a primeira tentativa criou, com replayed a true, em vez de enviar duas vezes. preview, get, cancel e todas as leituras podem ser repetidos com segurança e são repetidos.

schedule_broadcast.php
$broadcast = $client->broadcasts->send([    'audienceIds' => ['aud_4c1b8e2a7d9f05c36b4e8a71'],    'from' => 'Acme <[email protected]>',    'subject' => 'Doors open on Friday',    'text' => 'Hi {{firstName|there}}, doors open at nine. Unsubscribe: {{unsubscribeUrl}}',    'scheduledAt' => new \DateTimeImmutable('+1 hour'),], idempotencyKey: 'doors-open-2026-10'); echo $broadcast['id'], ' ', $broadcast['status'], ' ', $broadcast['replayed'] ? 'replayed' : 'new', PHP_EOL;

Os campos de uma difusão são as chaves de um único array com os nomes em camelCase da API (audienceIds, scheduledAt). idempotencyKey: e apiKey: são argumentos nomeados da chamada e nunca são enviados como campos. Espalhar um rascunho num novo array ao lado de um campo envia o mesmo rascunho com essa única alteração, por isso send([...$draft, 'scheduledAt' => 'P1D']) envia-o um dia mais tarde. scheduledAt aceita um DateTimeInterface, uma string ISO 8601 ou uma duração como PT2H, e um DateTimeInterface sai como instante UTC. preview só envia audienceIds do que lhe der, por isso aceita o mesmo array que send. Uma resposta é um array com chaves em camelCase, por isso $broadcast['status'] lê o estado.

Campos de fusão

subject, html e text são preenchidos para cada pessoa a partir do seu contacto. {{firstName}} é a primeira palavra do nome do contacto, {{lastName}} o resto, {{name}} o nome completo, {{email}} o endereço para onde vai a cópia e {{unsubscribeUrl}} a ligação que lhe cancela a subscrição.

Cada campo aceita um valor alternativo depois de uma barra, usado quando o contacto não tem valor para ele, por isso {{firstName|there}} passa a "there" para um contacto guardado sem nome. Os valores são escapados em html, e qualquer outro {{…}} fica exatamente como foi escrito.

Passe template em vez de html e text para enviar um modelo guardado, como um array com id e, opcionalmente, version, props e slots. Os mesmos cinco valores chegam-lhe como props, mas só as props que o modelo declara, por isso um modelo que declara firstName recebe-o e um que não o declara nunca é recusado por isso. Tudo o que está nas suas props vai igual para todas as cópias.

Cancelar subscrição

Cada cópia leva os cabeçalhos para cancelar a subscrição com um clique, que permitem a um cliente de correio mostrar o seu próprio botão, algo que os grandes fornecedores de caixas de correio exigem ao correio em massa. Um corpo html ou text que não coloque {{unsubscribeUrl}} por si recebe um rodapé de uma linha com a ligação. Um modelo é enviado exatamente como está, por isso ponha {{unsubscribeUrl}} no modelo.

Cancelar a subscrição marca a pessoa sem subscrição em todas as audiências para onde essa difusão foi, e audiences->listContacts mostra-o no unsubscribedAt da sua linha, como descreve a página Audiências. Continua na audiência e no livro de endereços, as outras audiências não são tocadas, e o correio que lhe é enviado uma mensagem de cada vez continua a sair. Retirá-la da audiência e voltar a adicioná-la dá-lhe subscrição de novo.

Quem é ignorado

Uma difusão alcança todos os contactos de pelo menos uma das audienceIds, uma vez por mais audiências que os contenham. Ignora o contacto que cancelou a subscrição de todas essas audiências em que está, e o endereço na lista de supressão após uma devolução ou uma queixa, ou porque alguém o adicionou. Um contacto adicionado a uma das audiências depois de send, mas antes de o envio lá chegar, é incluído.

preview devolve os mesmos números sem enviar: recipients, unsubscribed e suppressed. Um send que não alcançaria ninguém lança um 422 no_recipients como ValidationException.

O envio inteiro é comparado com os envios mensais do plano antes de se escrever qualquer coisa, por isso uma difusão que a quota não consegue cobrir lança um 429 send_quota_exceeded como RateLimitException e não deixa nada para trás. Cada cópia conta como um envio.

Estado e progresso

get lê counts ao vivo a partir das cópias, por isso consulte-o enquanto uma difusão envia, com sleep() entre chamadas como faz o exemplo acima. status passa de scheduled ou queued para sending e fixa-se em sent quando cada cópia entregue saiu ou falhou. Continua sending enquanto ainda há cópias à espera, mesmo depois de completedAt dizer que a última pessoa foi alcançada. failed significa que a difusão inteira parou, e lastError diz porquê: o endereço from já não pode enviar, o modelo deixou de se resolver, o plano esgotou-se a meio, o próprio envio falhou repetidamente, ou nem uma cópia pôde ser escrita.

cancel para uma difusão que está scheduled, queued ou sending. Ninguém mais é adicionado e cada cópia ainda à espera é cancelada, enquanto as cópias que saíram não podem ser recuperadas. Quando todas as cópias saíram, cancel lança um 409 broadcast_not_cancellable como ConflictException, e cancelar uma difusão já cancelada devolve-a tal como está.

A quem chegou

listRecipients devolve uma OpenEmail\Result\Page das pessoas para quem uma difusão foi, uma linha por cópia, ordenadas por endereço, com items, hasMore e nextCursor. listAllRecipients percorre todas as páginas para um único array, e iterateRecipients devolve um Generator que entrega uma cópia de cada vez e só vai buscar a página seguinte quando o ciclo a pede. limit: vai de 1 a 200 e é 50 por omissão, e um cursor: é reenviado com os mesmos filter: e q:.

`filter:`Mantém
pendingCópias ainda em fila, agendadas ou a enviar.
sentCópias que saíram.
deliveredCópias que o servidor de destino aceitou.
openedCópias abertas pelo menos uma vez.
not_openedCópias enviadas e nunca abertas.
clickedCópias com pelo menos um clique com seguimento.
bouncedCópias que foram devolvidas.
complainedCópias que a pessoa denunciou como spam.
failedCópias que falharam ou foram canceladas.
unsubscribedPessoas que cancelaram a subscrição depois do envio da difusão.

OpenEmail\Constants\BroadcastRecipientFilters nomeia cada filtro, e q: pesquisa o endereço e o nome, sem distinguir maiúsculas de minúsculas. As aberturas e os cliques excluem proxies de imagens e verificadores de ligações, e ficam em 0 quando a difusão saiu com o rastreio desligado.

getRecipient($id, $emailId) devolve uma cópia: a mesma linha, mais subject, html e text exatamente como essa pessoa os recebeu, com os campos de fusão preenchidos e a sua própria ligação para cancelar a subscrição. Passe o emailId de uma linha como segundo argumento. O HTML é de antes de ser acrescentado o rastreio de aberturas e cliques. Um emailId que não é uma cópia desta difusão lança um 404 recipient_not_found, e uma difusão desconhecida um 404 broadcast_not_found, ambos como NotFoundException.

stats devolve os totais e uma série. totals conta as cópias sent, delivered, bounced, complained e failed, com pending para as que ainda esperam, e as pessoas que opened, clicked e unsubscribed, com opens e clicks como contagens de eventos. series é esparsa e começa pelas mais antigas, com um intervalo por grain: (minute, hour ou day, por omissão hour) em que algo aconteceu, cortado no fuso que fica offsetMinutes: minutos a leste de UTC. Passe intdiv((int) date('Z'), 60) para o fuso local. Conta cada pessoa uma vez, na primeira vez que lhe aconteceu, por isso a soma bate certo com os totais.

Passe days: ou minutes: a stats para ler também o que aconteceu recentemente. window conta então o que foi entregue, devolvido, denunciado como spam, aberto, clicado e com subscrição cancelada dentro desse período, e series mantém apenas os intervalos desse período, enquanto totals continua a abranger toda a difusão. Sem nenhum dos dois, window é null.

Uma chave limitada a determinados endereços ou domínios só alcança as difusões enviadas de um endereço ou domínio que tem. list, listAll e iterate deixam as outras de fora, e get, os métodos de destinatários, stats e cancel lançam um 404 broadcast_not_found para elas.

Resposta: uma difusão

send, get e cancel devolvem cada um uma destas como um array com chaves em camelCase, e send acrescenta replayed. list devolve uma OpenEmail\Result\Page delas, das mais recentes para as mais antigas, listAll devolve-as todas num único array e iterate devolve um Generator sobre elas. preview devolve um array com audienceIds, recipients, unsubscribed e suppressed. listRecipients devolve uma Page de linhas de destinatários, getRecipient devolve uma linha com o seu conteúdo, e stats devolve um array com broadcastId, grain, totals, window e series. analytics devolve um array com totals, series e uma linha por difusão em broadcasts. As horas são strings ISO 8601, que new \DateTimeImmutable() lê.

idstring
O identificador duradouro, `brd_` seguido de 24 caracteres hexadecimais.
statusstring
`scheduled`, `queued`, `sending`, `sent`, `cancelled` ou `failed`. `OpenEmail\Constants\BroadcastStatuses` nomeia cada um.
modestring
`live` ou `test`, conforme a chave que a criou. As cópias de uma difusão de teste são marcadas como enviadas e não são entregues a ninguém.
sourcestring
Onde foi iniciada: `api` para uma chave, `oauth` para uma aplicação ligada, `composer` para a aplicação, `mcp` para um assistente.
audienceIdsarray
As audiências para onde foi enviada, cada uma uma vez.
fromstring
O endereço de onde cada cópia é enviada.
subjectstring
O assunto tal como foi escrito, com os campos de fusão. Vazio quando um modelo fornece o assunto.
countsarray
`recipients` é a estimativa tirada no `send`. `created` conta as cópias escritas, `skipped` as pessoas passadas à frente porque o endereço estava suprimido nessa altura, e `failedToQueue` as pessoas cuja cópia não pôde ser escrita. `queued`, `sending`, `sent`, `failed` e `cancelled` contam as cópias pelo estado em que cada uma está agora.
lastErrorstring or null
Porque falhou a difusão, ou a cópia mais recente que não pôde ser escrita e porquê. É null enquanto nada tiver corrido mal.
scheduledAtstring or null
ISO-8601 UTC, quando o envio deve começar. É null para uma difusão enviada de imediato.
startedAtstring or null
ISO-8601 UTC, quando o envio alcançou as primeiras pessoas.
completedAtstring or null
ISO-8601 UTC, quando a última pessoa foi alcançada. Depois disso ainda pode haver cópias à espera de sair.
cancelledAtstring or null
ISO-8601 UTC, quando `cancel` a parou.
createdAtstring
ISO-8601 UTC, quando `send` foi chamado. Fixa a ordem da lista.
updatedAtstring
ISO-8601 UTC, atualizado à medida que o envio avança.

Resposta: uma linha de destinatário

Cada linha de listRecipients, listAllRecipients e iterateRecipients, como um array com chaves em camelCase. O array que getRecipient devolve acrescenta subject, html e text.

emailIdstring
O id `msg_` da cópia desta pessoa. `getRecipient` lê-a com o seu conteúdo, e `emails->get` lê-a como um email enviado, como descreve a página Listar e obter.
contactIdstring or null
O contacto para quem foi, ou null quando o contacto foi eliminado entretanto.
emailstring
O endereço para onde a cópia foi.
namestring or null
O nome no contacto.
statusstring
O estado da cópia: `queued`, `scheduled`, `sending`, `sent`, `failed` ou `cancelled`.
sentAtstring or null
ISO-8601 UTC, quando a cópia saiu.
deliveredAtstring or null
ISO-8601 UTC, quando o servidor de destino a aceitou, o primeiro `email.delivered`.
bouncedAtstring or null
ISO-8601 UTC, quando foi devolvida, o primeiro `email.bounced`.
complainedAtstring or null
ISO-8601 UTC, quando a pessoa a denunciou como spam, o primeiro `email.complained`.
failurestring or null
Porque é que a cópia falhou, se falhou.
opensint
Aberturas registadas, sem as que os proxies de imagens e os verificadores geram. 0 quando o seguimento estava desligado.
firstOpenAtstring or null
ISO-8601 UTC, a primeira abertura.
clicksint
Cliques registados em ligações com seguimento, sem verificadores.
firstClickAtstring or null
ISO-8601 UTC, o primeiro clique.
unsubscribedAtstring or null
ISO-8601 UTC, quando esta pessoa cancelou a subscrição de uma das audiências da difusão depois do envio, pela ligação da difusão ou de outra forma.