Sign inGet Started

Enviando e-mail

POST /v1/email/messages envia um e-mail. Forneça um remetente, destinatários e conteúdo em um payload JSON. A API retorna 202 Accepted com um ID de mensagem e então entrega o e-mail de forma assíncrona. Consulte a referência da API para os schemas completos.

Um envio mínimo

O menor payload válido é um from, pelo menos um destinatário to, um subject e um corpo (html, text, ou ambos). O endereço from precisa estar em um domínio que você verificou neste espaço de trabalho, ou no domínio de onboarding.
const msg = await bird.email.send({
  from: { email: "onboarding@messagebird.dev", name: "Bird" },
  to: ["delivered@messagebird.dev"],
  subject: "Hello from Bird",
  html: "<p>My first Bird email.</p>",
});
console.log(msg.id, msg.status); // "em_…", "accepted"
Use o host regional (https://us1.platform.bird.com ou https://eu1.platform.bird.com) com uma chave bk_{region}_... correspondente.
O exemplo de envio usa delivered@messagebird.dev, um endereço sandbox que sempre aceita e-mails. A API rejeita domínios de placeholder com um 422: example.com, example.net, example.org, example.edu, test.com, e qualquer coisa sob os TLDs reservados .test, .example, .invalid ou .localhost. Um envio para esses domínios só pode resultar em bounce, o que prejudica sua reputação de remetente.

Enviando antes de verificar um domínio

Durante o onboarding, você pode enviar a partir do nosso domínio compartilhado de onboarding, onboarding@messagebird.dev. Esses envios ignoram a verificação de domínio, mas alcançam apenas membros verificados do seu próprio espaço de trabalho e endereços sandbox, com um limite diário de destinatários. O quickstart contém as regras e os limites exatos.

Montando o payload

Destinatários

to, cc e bcc aceitam até 50 endereços cada, e to exige pelo menos um. Cada entrada é uma string de e-mail simples, uma string de mailbox RFC 5322 (Jane <jane@acme.com>), ou um objeto com um display name opcional.
Destinatários na lista de supressão do espaço de trabalho não causam falha na solicitação. Ela ainda retorna um 202, e cada destinatário suprimido aparece nos endpoints de leitura como status: rejected com o motivo recipient_suppressed, inclusive quando isso se aplica a todos os destinatários do envio.

Conteúdo

subject é obrigatório para envios inline, com até 998 caracteres. Forneça html, text, ou ambos, cada um com até 524.288 caracteres. Envie ambos sempre que possível: um cliente que não consegue renderizar HTML recorre à parte de texto.
Para personalizar conteúdo inline, coloque tokens {{ variable }} no assunto ou corpo e passe os valores deles em parameters, até 16 KB serializados. Um único conjunto de valores se aplica a todos os destinatários do envio, e um token sem chave correspondente é renderizado vazio. Para conteúdo que você reutiliza, envie um template.
Inclua parameters, mesmo como um objeto vazio ({}), para processar o assunto e o corpo como Liquid. Omita-o para enviar tokens como {{ animal }} exatamente como escritos. Cada nome de parâmetro é uma palavra única, como first_name; nomes com pontos e o nome reservado bird são rejeitados. Sintaxe Liquid inválida e tags ou filtros não suportados retornam 422.
Valores inseridos em HTML são escapados para que não alterem a marcação ao redor. Para um link completo ou URL de imagem, use {{ link }} sem url_encode. Para um valor dentro de uma query de URL, codifique esse valor explicitamente, por exemplo https://example.com/search?q={{ query | url_encode }}.

Reply-to e headers personalizados

reply_to aceita de 1 a 25 endereços, nos mesmos formatos que os destinatários. Toda resposta de destinatário vai para todos eles, então um ou dois é o comum.
headers é um objeto string-para-string para seus próprios headers, por exemplo {"X-Campaign": "spring-2026"}, limitado a 25 headers com valores de até 998 caracteres. Três tipos de header retornam como um 422:
  • Headers de endereçamento e plataforma. Defina o endereçamento da mensagem pelos campos dedicados (from, to, cc, bcc, reply_to, subject). Esses nomes, e os headers que geramos para você (Content-Type, Content-Transfer-Encoding, DKIM-Signature, Received, Return-Path), não podem ser definidos aqui.
  • List-Unsubscribe e List-Unsubscribe-Post em um envio marketing. Nós definimos um header de cancelamento de inscrição com um clique em conformidade nesses casos. Em um envio transactional, mantemos os seus exatamente como você definiu.
  • Qualquer valor com retorno de carro ou quebra de linha.

Rastreamento

track_opens e track_clicks têm como padrão true. Defina qualquer um como false para pular a injeção de pixel de abertura ou a reescrita de links neste envio. Rastreamento e métricas explica o que cada um altera na mensagem.

Categoria e pool de IPs

category classifica o conteúdo e define a política de supressão: marketing bloqueia a entrega em qualquer motivo de supressão e em qualquer opt-out, e transactional entrega mesmo com uma supressão por reclamação ou um opt-out apenas de marketing (um registrado para todas as mensagens também bloqueia). O padrão é a categoria do template em um envio com template e marketing nos demais casos, então defina transactional explicitamente para recibos, redefinições de senha e outros e-mails operacionais. Categorias explica a escolha. E-mails enviados via SMTP usam a categoria da configuração SMTP da chave.
ip_pool_id seleciona o pool de envio: um ID de pool (ipp_...), ou ipp_shared para rotear explicitamente pelo pool compartilhado. Omita-o para usar o pool padrão da sua organização. Um pool desconhecido, ou sem IPs dedicados disponíveis para envio, é rejeitado com um 422.

Referência de campos

CampoTipoObrigatórioLimites e observações
fromaddresssimDeve estar em um domínio verificado, ou no domínio de onboarding
toaddress[]sim1 a 50
cc, bccaddress[]nãoAté 50 cada
subjectstringenvios inlineAté 998 caracteres; omita em envios com template
html, textstringpelo menos umAté 524.288 caracteres cada; omita em envios com template
reply_toaddress[]não1 a 25; respostas vão para todos os endereços listados
headersobject (string → string)nãoAté 25; nomes reservados são rejeitados (veja headers personalizados)
parametersobjectnãoValores para {{ tokens }} em conteúdo inline; até 16 KB serializados; compartilhados entre destinatários
tags{name, value}[]nãoAté 20; nome ≤ 32 chars, valor ≤ 64 chars; apenas [A-Za-z0-9_-]; nomes únicos por envio
metadataobjectnãoJSON arbitrário, até 2 KB serializado
track_opensbooleannãoPadrão true
track_clicksbooleannãoPadrão true
categorystringnãomarketing ou transactional; padrão é o do template em envio com template, caso contrário marketing
ip_pool_idstringnãoipp_... ou ipp_shared; omita para usar o pool padrão da sua organização
templateobjectnãoEnvia um template publicado por id ou slug, com parameters para suas variáveis e um language opcional
attachmentsobject[]nãoAté 20; veja anexos
scheduled_atRFC 3339 timestampnãoAgende conteúdo inline ou um template; veja envio agendado

Enviando com um template

Em vez de conteúdo inline, envie um template publicado: defina template como um objeto nomeando-o por id (emt_...) ou por slug, exatamente um dos dois, com os valores das variáveis em template.parameters. Omita subject, html e text, porque o template já os contém.
const msg = await bird.email.send({
  from: { email: "onboarding@messagebird.dev", name: "Bird" },
  to: ["delivered@messagebird.dev"],
  category: "transactional",
  template: {
    slug: "welcome-email",
    parameters: { first_name: "Jane" },
  },
});
console.log(msg.id, msg.status);
O conteúdo de um template é Liquid, então além da substituição simples de {{ variable }} ele pode usar filtros, condicionais {% if %} e loops {% for %}. Personalizando com variáveis lista os poucos construtos que uma publicação rejeita. template.parameters é onde você coloca os valores para os parâmetros do próprio template, indexados por nome. Omita um e o envio é rejeitado com um 422 nomeando-o. Todo o restante do envio se comporta como no modo inline, incluindo destinatários, tags, metadata, rastreamento e anexos. O que é específico de um envio com template:
  • Inline ou com template, nunca ambos. Enviar template junto com subject, html ou text é rejeitado com um 422. A API também rejeita valores de variáveis no campo de nível superior parameters; em um envio com template eles pertencem a template.parameters.
  • bird é o único nome reservado. Um caminho de placeholder que começa com bird. nomeia dados nossos, como o link de cancelamento de inscrição ou o registro de contato do destinatário, então uma chave template.parameters não pode se chamar bird. Todas as outras chaves são suas, e cada uma é uma palavra simples: {"order_number": "A-1043"} preenche {{ order_number }}.
  • Um template pode ser enviado agora ou depois. Adicione scheduled_at para agendar o envio. Nós fixamos a versão publicada, o idioma selecionado e os valores dos parâmetros no momento da aceitação. Se você excluir o template antes do horário de envio, a mensagem é rejeitada com generation_failure.
  • Um envio usa a versão publicada do template. Rascunhos nunca são enviados. Um template desconhecido é rejeitado com um 404, e um template sem versão publicada com um 422.
  • language escolhe um dos idiomas do template. Omita-o para enviar o padrão do template. Solicite um que o template não possui, e a configuração on_missing_language dele decide se a correspondência mais próxima é enviada ou se o envio é rejeitado. Um template que define language_source_required rejeita um envio que não especifica nenhum idioma.
  • A categoria do template é um padrão, e a sua a substitui. Omita category e o envio herda a do template, então um template transacional não precisa repeti-la em cada chamada.
Templates de e-mail cobre criação, publicação e os construtos que um template pode conter.

Tags vs metadata

Ambos associam seus próprios dados a um envio, e diferem em como você os consulta depois:
  • tags são pares {name, value} estruturados: até 20 por envio, nome com até 32 caracteres, valor com até 64, apenas letras ASCII, dígitos, underscore e hífen, e nomes únicos dentro do envio. Tags são dimensões de filtro, então você pode filtrar a lista de mensagens por tag e segmentar analytics e resumos do dashboard por tag. Use-as para rótulos de baixa cardinalidade como campaign, experiment_variant ou source.
  • metadata é um objeto JSON arbitrário, até 2 KB serializado. Nós o armazenamos, retornamos em leituras API e o incluímos em cada evento de webhook, então ele é ideal para contexto que você quer de volta: IDs internos, chaves estrangeiras, payloads estruturados.
Cada evento de webhook inclui ambos junto com os IDs de correlação (email_id, recipient_id), então você pode conciliar com seus próprios registros sem uma segunda consulta. Nomes de tag e chaves de metadata de nível superior que começam com __bird são rejeitados. Você não precisa codificar dispositivo, geografia, provedor de caixa de correio, tipo de bounce ou domínio do destinatário em nenhum dos campos, porque capturamos cada um desses como uma dimensão de analytics.
Exemplo de código
{
  "tags": [{ "name": "campaign", "value": "onboarding" }],
  "metadata": { "user_id": "usr_12345", "order_id": "ord_98765" }
}

Anexos

attachments aceita até 20 arquivos por mensagem, como bytes codificados em base64 inline. Rejeitamos um envio cujo tamanho estimado da mensagem gerada exceda 20 MB, medido após a codificação base64, então mantenha o conteúdo bruto dos anexos em até 15 MB para margem. Anexos contém o contrato de campos, imagens inline, os tipos de arquivo bloqueados e como baixar um anexo de volta.

O que um 202 significa

Um envio bem-sucedido retorna 202 Accepted com um ID de mensagem prefixado por em_ e status: accepted:
Exemplo de código
{
  "id": "em_01ky7ma8y2es1s2akzk53tmjn0",
  "status": "accepted",
  "category": "marketing",
  "from": { "email": "hello@yourdomain.com" },
  "to": [{ "email": "delivered@messagebird.dev" }],
  "subject": "Hello from Bird",
  "accepted_count": 1,
  "processed_count": 0,
  "delivered_count": 0,
  "deferred_count": 0,
  "bounced_count": 0,
  "complained_count": 0,
  "rejected_count": 0,
  "open_count": 0,
  "click_count": 0,
  "track_opens": true,
  "track_clicks": true,
  "created_at": "2026-07-23T13:58:20.866Z"
}
O 202 significa que aceitamos o envio de forma durável. Falhas que você pode corrigir voltam na própria requisição como 422: um domínio de remetente não verificado ou um campo que não passa na validação. Os resultados por destinatário (entregue, devolvido, adiado, reclamado) chegam depois por meio de webhooks e dos endpoints de leitura de mensagens.
Duas coisas decorrem disso:
  • Leituras retornam o estado sem o corpo. GET /v1/email/messages/{message_id} retorna o estado da mensagem e do destinatário, nunca o corpo html ou text. Quando o armazenamento de conteúdo está ativado para o espaço de trabalho, os corpos armazenados ficam disponíveis por até 30 dias a partir de GET /v1/email/messages/{message_id}/content.
  • Uma leitura pode demorar um instante após o envio. Um 404 nos endpoints de leitura logo após um 202 significa que a mensagem ainda não está visível; tente novamente em um momento.

Tentando novamente com segurança

Envie um header Idempotency-Key com um valor único por envio lógico. Se uma requisição foi bem-sucedida mas você não viu a resposta, reenvie-a com a mesma chave. A API retorna o resultado original em vez de enviar um segundo e-mail e inclui um header Idempotency-Replay. Idempotência contém o formato da chave e a retenção.

Envio em lote

Para reduzir requisições API, POST /v1/email/batches aceita até 100 mensagens independentes e as valida como uma unidade. Chamar o endpoint de envio individual em um loop também é suportado. Um item de lote usa o payload desta página, incluindo scheduled_at, então um lote pode misturar mensagens imediatas e agendadas.

Faturamento

Os envios de e-mail são medidos por destinatário contra a cota mensal do seu plano, então uma mensagem para três destinatários consome três envios. Faturamento e uso cobre o modelo de medição e a leitura de uso em tempo real.

Próximos passos