Enviar para audiências
Envia uma mensagem para todos em uma ou mais audiências, como cópia separada para cada pessoa e personalizada a partir de cada contacto. 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. A chamada responde `202` de imediato e o envio continua em segundo plano, por isso acompanhe-o com `GET /broadcasts/{id}`.
Executa a chamada real contra o seu espaço de trabalho, com a sua própria chave.
POST /broadcasts
Envia uma mensagem para todos em uma ou mais audiências, como cópia separada para cada pessoa e personalizada a partir de cada contacto. 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. A chamada responde 202 de imediato e o envio continua em segundo plano, por isso acompanhe-o com GET /broadcasts/{id}.
Exemplo
Precisa de emails:send e audiences:read. audienceIds tem de 1 a 10 ids. O corpo vem de html e/ou text, ou de um template guardado, nunca de ambos, e subject é obrigatório a não ser que o modelo o forneça.
curl -X POST "$OE/broadcasts" -H "$AUTH" -H 'content-type: application/json' -d '{ "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" }, "scheduledAt": "PT2H"}'{ "object": "broadcast", "id": "brd_5a8c1e3f7b2d94a06c8e1f3b", "status": "scheduled", "mode": "live", "source": "api", "audienceIds": ["aud_4c1b8e2a7d9f05c36b4e8a71"], "from": "Acme <[email protected]>", "subject": "{{firstName|Hello}}, the September release is out", "counts": { "recipients": 412, "created": 0, "skipped": 0, "failedToQueue": 0, "queued": 0, "sending": 0, "sent": 0, "failed": 0, "cancelled": 0 }, "lastError": null, "scheduledAt": "2026-09-23T14:00:00.000Z", "startedAt": null, "completedAt": null, "cancelledAt": null, "createdAt": "2026-09-23T12:00:00.000Z", "updatedAt": "2026-09-23T12:00:00.000Z", "replayed": false}A resposta é queued, ou scheduled com scheduledAt, que aceita um instante ISO 8601 ou uma duração como PT2H, no máximo a 365 dias. counts.recipients é a estimativa tirada agora, e os outros contadores começam em 0. O cabeçalho Location indica a difusão.
Pode repetir com segurança com um cabeçalho Idempotency-Key: a mesma chave responde 200 com a difusão que a primeira chamada criou e Idempotency-Replayed: true, e a mesma chave com outro corpo dá 422 idempotency_key_reuse. Sem chave, enviar o mesmo corpo duas vezes envia a difusão duas vezes.
As cópias não são arquivadas na pasta Enviados, porque a difusão é o registo. GET /emails?broadcastId=brd_5a8c1e3f7b2d94a06c8e1f3b lista-as, uma por pessoa.
Quem a recebe
Todos os contactos de pelo menos uma das audiências, contados uma vez por mais audiências que os contenham. Ficam de fora dois tipos de contacto: o que cancelou a subscrição de todas as audiências escolhidas em que está, e o que tem 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 da chamada, mas antes de o envio lá chegar, é incluído.
O envio percorre as audiências 50 pessoas de cada vez e entrega cada cópia ao mesmo circuito que POST /emails usa, por isso cada cópia é repetida, seguida e reportada como qualquer outra mensagem. POST /broadcasts/preview devolve o número de que esta chamada partiria, sem enviar nada.
O envio inteiro é comparado com os envios mensais do plano antes de se escrever qualquer coisa. Uma difusão que a quota não consegue cobrir é recusada com 429 send_quota_exceeded e não deixa nada para trás. Cada cópia conta como um envio.
Campos de fusão
subject, html e text são preenchidos para cada pessoa. 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, são permitidos espaços dentro das chavetas e qualquer outro {{…}} fica exatamente como foi escrito.
| Campo | Preenchido com |
|---|---|
| `{{firstName}}` | A primeira palavra do nome do contacto. |
| `{{lastName}}` | O resto do nome do contacto depois da primeira palavra. |
| `{{name}}` | O nome completo do contacto. |
| `{{email}}` | O endereço para onde vai a cópia. |
| `{{unsubscribeUrl}}` | A ligação que cancela a subscrição desta pessoa nestas audiências. |
Com template em vez de um corpo, os mesmos cinco valores são passados como props, mas só as props que o modelo declara. Um modelo que declara firstName recebe-o, e uma prop que não declara nunca é enviada, por isso as cópias nunca falham por uma prop desconhecida. Tudo o que puser em template.props vai igual para todas as cópias.
Cancelar subscrição
Cada cópia leva List-Unsubscribe e List-Unsubscribe-Post: List-Unsubscribe=One-Click. É isso que permite a um cliente de correio mostrar o seu próprio botão de cancelar subscrição, e o 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: "You are receiving this because you are on this mailing list. Unsubscribe". Um modelo é enviado exatamente como está, por isso ponha {{unsubscribeUrl}} no modelo.
A ligação abre uma página com um botão para cancelar a subscrição, por isso um analisador de ligações que a obtenha não cancela a subscrição de ninguém, enquanto o pedido de um clique feito por um cliente de correio cancela-a de imediato. Em ambos os casos, a pessoa fica marcada sem subscrição em todas as audiências para onde esta difusão foi, o que aparece como unsubscribedAt em GET /audiences/{id}/contacts. As outras audiências, o contacto e o correio que lhe é enviado uma mensagem de cada vez não são afetados.
Recusas
| Estado | Código | Quando |
|---|---|---|
| 403 | from_address_forbidden | A chave não pode enviar como from. |
| 404 | audience_not_found | Um id em audienceIds não indica nenhuma audiência neste workspace. |
| 409 | domain_not_sendable | O domínio de from ainda não consegue assinar correio, tal como em POST /emails. |
| 422 | no_recipients | As audiências estão vazias, ou todos nelas cancelaram a subscrição ou estão suprimidos. |
| 422 | invalid_parameter | Sem corpo, html ou text ao lado de template, sem subject e sem modelo, mais de 10 audiências ou 8 etiquetas, ou um scheduledAt que não está no futuro ou está a mais de 365 dias. |
| 422 | template_not_found | O modelo não se resolve. As outras recusas de modelo também indicam template.*. |
| 422 | capability_unsupported | A chave está limitada a endereços concretos. As audiências pertencem a todo o workspace. |
| 429 | send_quota_exceeded | O plano não consegue cobrir uma cópia para todos este mês. |
Sem anexos, cc, bcc, tradução nem encriptação. tags aceita até 8, e cada cópia leva também broadcast_id, que o servidor acrescenta.