É comum confundir estes quatro números porque todos soam como "quanto posso enviar", mas respondem perguntas diferentes e são verificados em pontos diferentes do fluxo. Esta página separa cada um deles.
Planilha (CSV) de destinatários
Na nova transmissão, a aba Planilha (CSV) aceita tanto o formato posicional (o destinatário na primeira coluna, seguido de uma coluna por variável do template) quanto uma planilha crua com linha de cabeçalho. Quando há cabeçalho, abrimos o mapeamento de colunas: você indica qual coluna é o destinatário e liga cada variável do template à coluna certa, sem precisar reordenar a planilha. Sem cabeçalho, o formato posicional continua valendo como antes.
Público a partir dos contatos
A aba Contatos dispara para os contatos já cadastrados do número selecionado, sem exportar nem colar nada. O público é a base do número, com um filtro opcional por campo personalizado(campo preenchido, ou igual a um valor exato). Etapa do funil não entra aqui: ela é o estado do contato, não um segmento de disparo.
Cada variável do template é preenchida por uma fonte à sua escolha: o nome do contato, um campo personalizado, ou um valor fixo igual para todos. Um contato sem o nome ou sem o campo usado numa variável fica de fora do disparo, e a tela mostra quantos ficaram e por quê antes de você enviar. Os limites de destinatários por transmissão e o envio em ondas valem igual às outras abas.
Limite de destinatários por transmissão
Cada transmissão tem um teto de destinatários próprio, verificado ao adicionar destinatários e de novo ao enviar/agendar. Ele é o menor entre três candidatos:
- o teto do plano da conta;
- a capacidade REAL do número na Meta (o messaging limit atual dele, quando conhecido);
- um teto operacional absoluto de 2.000 destinatários, que vale para todos os planos, inclusive o Enterprise.
Teto por plano (antes de qualquer ajuste de qualidade):
| Plano | Destinatários por transmissão |
|---|---|
| Free | Transmissões não disponíveis |
| Starter | 1.000 |
| Pro | 2.000 |
| Enterprise | 2.000 |
O plano Free não inclui transmissões: o recurso está nos planos pagos (Starter em diante). No Free você envia templates avulsos e responde conversas 1:1, mas não dispara em massa.
Se a qualidade do número estiver YELLOW na Meta, o teto do plano é reduzido a 50% (amortecimento conservador nosso, não um número definido pela Meta). Qualidade RED bloqueia a transmissão inteiramente, e um número com restrição ativa na Meta (status não-operacional) também bloqueia, independente de qualidade ou plano.
A capacidade real do número na Meta entra no cálculo quando conhecida (sincronizada periodicamente, não em tempo real). Tiers reconhecidos:
| Tier da Meta | Capacidade |
|---|---|
TIER_50 | 50 |
TIER_250 | 250 |
TIER_1K | 1.000 |
TIER_2K | 2.000 |
TIER_10K | 10.000 |
TIER_100K | 100.000 |
TIER_UNLIMITED | 2.000 |
Um tier ausente (número nunca sincronizado) não participa do cálculo. A política segue só por plano/qualidade. Um tier presente mas fora desta lista (formato novo/desconhecido da Meta) bloqueia de forma conservadora, para nunca liberar uma capacidade que não foi validada.
Nos planos pagos, uma transmissão maior que a capacidade da Meta não precisa ser recusada: ela pode ser enviada em ondas ao longo dos dias (veja Envio em ondas). Nesse modo, a capacidade da Meta deixa de limitar o total da transmissão e passa a dimensionar o tamanho de cada onda; o teto do plano e o teto operacional continuam valendo para o total.
Códigos de erro estáveis desta verificação:
| Status | Código | Quando |
|---|---|---|
| 422 | recipient_limit_exceeded | O total de destinatários pretendido excede o teto aplicável. |
| 422 | quality_restricted | Qualidade do número RED na Meta. |
| 422 | meta_restricted | Número com restrição ativa/status não-operacional na Meta. |
| 409 | whatsapp_disconnected | Conexão inativa ou token inválido. |
| 503 | capacity_unavailable | Erro de infraestrutura ao verificar a capacidade. Tente novamente; nunca é tratado como "sem limite". |
Quota mensal de templates
Separado do limite acima: cada plano tem um teto de templates (mensagens iniciadas pelo negócio) por mês, verificado em cada envio de template (avulso ou de transmissão). Responder uma conversa dentro da janela de 24h não consome essa quota. É um gate independente, que existe mesmo que a transmissão esteja dentro do teto de destinatários.
| Plano | Templates por mês |
|---|---|
| Free | 500 |
| Starter | 10.000 |
| Pro | 100.000 |
| Enterprise | sem teto |
No worker de transmissão, a quota do lote é reservada atomicamente antes do envio; o que não for usado é devolvido em best-effort. Se essa devolução falhar por algum motivo, a diferença se corrige sozinha na virada do período. O sistema nunca deixa uma conta enviar acima do contratado por causa disso.
Rate limit HTTP
Independente dos dois limites acima: a API REST pública aceita 120 requisições a cada 10 segundos por conta (qualquer rota /v1, não só envio). Passar disso responde 429 com code: "rate_limited" e os headers X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset e Retry-After.
Messaging limit da Meta
O messaging limit é um conceito da própria Meta, não do BotoZap: um teto de destinatários únicos de conversas iniciadas pelo negócio (business-initiated) numa janela móvel de 24 horas. No modelo atual da Meta, esse limite é calculado por portfólio e compartilhado entre os números dele, não é um saldo independente por número.
Duas confusões comuns, ambas erradas:
- Não é um saldo que vai diminuindo em tempo real. É um teto reavaliado pela janela móvel, sincronizado periodicamente pela BotoZap (não em tempo real).
- A Meta não defineum "máximo de destinatários por transmissão". Os tetos por transmissão descritos acima são regra operacional da BotoZap, ancorada nesse messaging limit como um dos três candidatos do mínimo, não uma tradução 1:1 dele.
Envio em ondas (planos pagos)
Uma transmissão maior que o messaging limit atual do número não precisa ser recusada. Nos planos pagos, ela pode ser enviada em ondas diárias automáticas: a plataforma fatia os destinatários em lotes que cabem no tier do número e libera um lote por dia, respeitando a janela móvel de 24 horas da Meta. Esse é o mesmo ritmo que faz a Meta subir o tier do número ao longo do tempo.
O tamanho de cada onda deriva do tier atual do número, com uma folga conservadora: cada onda usa cerca de metade do tier. Um número que nunca teve o tier sincronizado assume o piso de 250 e envia cerca de 125 destinatários por onda até o primeiro sync. Entre o início de uma onda e o da seguinte há um intervalo de 24 horas e 30 minutos, para que duas ondas nunca caiam dentro da mesma janela de 24 horas.
O teto de destinatários do plano continua valendo para o total da transmissão, não por onda. O envio em ondas afrouxa apenas o limite da Meta, distribuindo o total ao longo dos dias; o teto do plano e o teto operacional absoluto seguem limitando quantos destinatários uma transmissão pode ter no total.
Cada número aquece uma transmissão por vez dentro do mesmo portfólio. Se outra transmissão em ondas já estiver aquecendo naquele portfólio, a próxima espera a vez, para não dividir a mesma janela de 24 horas entre duas.
O que o envio em ondas não é. Ele é um ritmo conservador que respeita o limite do seu número, não uma garantia de prazo. O messaging limit é do portfólio inteiro e compartilhado: respostas 1:1, envios avulsos e outras transmissões do mesmo portfólio consomem o mesmo tier. Por isso a plataforma dimensiona cada onda com folga e mostra uma estimativa de prazo antes do disparo, mas o tempo real de conclusão depende de todo o tráfego do portfólio.
Throughput da Meta (mensagens/segundo)
A Meta também expõe um indicador de ritmo de processamento (mensagens por segundo) por número. A BotoZap guarda esse valor apenas como informativo: ele não é o messaging limit, não define tamanho de transmissão e não altera o tamanho de lote nem a concorrência do worker de envio.
Semântica de execução
Disparar uma transmissão é assíncrono: o POST .../send responde 202 Accepted assim que a transmissão muda para sending, e o envio de fato acontece em segundo plano. Interromper com PATCH { status: "stopped" } impede novos destinatários de serem reivindicados pelo worker a partir dali, mas uma requisição de envio já em voo no momento da parada pode completar.
Em uma falha rara de infraestrutura entre o envio à Meta e a gravação do resultado, uma duplicata é possível. O disparo é at-least-once, igual ao resto da plataforma (veja Confiabilidade e entrega). A BotoZap não vende garantia de exactly-once sobre a Cloud API porque a Cloud API em si não oferece isso.
Cada destinatário enviado gera uma mensagem em GET /api/v1/messages com source: "broadcast" (distinta de um envio avulso, source: "api"). Essa mensagem já nasce com conversation_id preenchido: o disparo cria a conversa do contato no momento do envio, e ela aparece de imediato nas telas de Conversas e Inbox. Antes desta mudança o campo vinha null até a primeira resposta do contato.
A conversa criada assim nasce com a janela de 24h fechada: a mensagem chegou ao aparelho, mas a pessoa ainda não respondeu, e texto livre continua recusado pela Meta. Quando o contato responde, a resposta cai nessa mesma conversa e abre a janela.
Status por destinatário
Os contadores agregados da transmissão (counters.delivered, counters.read e counters.responded) têm um detalhamento por destinatário em GET /api/v1/broadcasts/:id/recipients. Cada destinatário carrega, além do status de envio, os carimbos de entrega:
| Campo | Significado |
|---|---|
sent_at | Momento em que a mensagem foi aceita pela Meta (o wamid foi emitido). |
delivered_at | Quando a Meta reportou a entrega no aparelho do destinatário. null até o evento chegar. |
read_at | Quando a Meta reportou a leitura. Uma leitura implica entrega, então delivered_at também é preenchido mesmo que o evento de entrega não tenha chegado antes. |
responded_at | Quando o destinatário respondeu à transmissão. A resposta é atribuída quando ela cita o envio (resposta citada / botão) ou quando chega até 7 dias após o envio daquele destinatário. Uma resposta marca todos os disparos elegíveis daquele contato, não só o último. null enquanto não respondeu. |
status_error | O errors[] cru do evento de status da Meta quando há falha de entrega depois do aceite (a mensagem saiu mas não chegou); null quando não houve. |
Esses carimbos são atualizados pelos eventos de status do WhatsApp e podem levar alguns instantes para chegar. Se você recebe os webhooks da BotoZap, os mesmos eventos whatsapp.message.delivered / .read / .failed das mensagens de transmissão também são encaminhados ao seu endpoint.
Fluxo completo
Da criação ao envio, com o exemplo de cada etapa:
Criar uma transmissão (rascunho)
Cria uma transmissão em `draft`, sem destinatários ainda — adicione via `POST .../recipients`.
curl https://botozap.com.br/api/v1/broadcasts \
-X POST \
-H "X-API-Key: $BOTOZAP_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Campanha de lançamento",
"phone_number_id": "178899001122334",
"template_name": "lancamento_produto",
"template_language": "pt_BR"
}'Resposta 201
{
"data": {
"id": "5b0a9d0e-6e1a-4f3b-8c2d-1a9f6e0b7c11",
"name": "Campanha de lançamento",
"status": "draft",
"source": "api",
"template_name": "lancamento_produto",
"template_language": "pt_BR",
"template_id": null,
"waba_connection_id": "5b0a9d0e-6e1a-4f3b-8c2d-1a9f6e0b7c11",
"phone_number_id": "5b0a9d0e-6e1a-4f3b-8c2d-1a9f6e0b7c11",
"scheduled_at": null,
"started_at": null,
"stopped_at": null,
"completed_at": null,
"counters": {
"total": 0,
"sent": 0,
"failed": 0,
"delivered": 0,
"read": 0,
"responded": 0
},
"created_at": "2026-07-14T12:00:00.000Z",
"updated_at": "2026-07-14T12:00:00.000Z"
}
}Adicionar destinatários
Só é possível adicionar destinatários enquanto a transmissão está em `draft`. A adição é tudo-ou-nada contra o teto de destinatários do plano.
curl https://botozap.com.br/api/v1/broadcasts/5b0a9d0e-6e1a-4f3b-8c2d-1a9f6e0b7c11/recipients \
-X POST \
-H "X-API-Key: $BOTOZAP_KEY" \
-H "Content-Type: application/json" \
-d '{
"recipients": [
{
"to_recipient": "+5511999990001"
},
{
"to_recipient": "+5511999990002"
}
]
}'Resposta 200
{
"data": {
"added": 2,
"duplicates": 0,
"errors": []
}
}Disparar o envio
Dispara o envio assíncrono: a transmissão vira `sending` imediatamente e o processamento continua em segundo plano.
curl https://botozap.com.br/api/v1/broadcasts/5b0a9d0e-6e1a-4f3b-8c2d-1a9f6e0b7c11/send \
-X POST \
-H "X-API-Key: $BOTOZAP_KEY"Resposta 202
{
"data": {
"id": "5b0a9d0e-6e1a-4f3b-8c2d-1a9f6e0b7c11",
"name": "Campanha de lançamento",
"status": "sending",
"source": "api",
"template_name": "lancamento_produto",
"template_language": "pt_BR",
"template_id": null,
"waba_connection_id": "5b0a9d0e-6e1a-4f3b-8c2d-1a9f6e0b7c11",
"phone_number_id": "5b0a9d0e-6e1a-4f3b-8c2d-1a9f6e0b7c11",
"scheduled_at": null,
"started_at": "2026-07-14T12:00:00.000Z",
"stopped_at": null,
"completed_at": null,
"counters": {
"total": 2,
"sent": 0,
"failed": 0,
"delivered": 0,
"read": 0,
"responded": 0
},
"created_at": "2026-07-14T12:00:00.000Z",
"updated_at": "2026-07-14T12:00:00.000Z"
}
}O disparo é at-least-once: numa falha rara de infraestrutura no meio do envio, o sweep retoma — uma duplicata rara é possível (a Cloud API não tem idempotency key nativa no envio).
Consultar progresso
Consulta os contadores de progresso de uma transmissão (`counters.sent/failed/delivered/read/responded`).
curl https://botozap.com.br/api/v1/broadcasts/5b0a9d0e-6e1a-4f3b-8c2d-1a9f6e0b7c11 \
-H "X-API-Key: $BOTOZAP_KEY"Resposta 200
{
"data": {
"id": "5b0a9d0e-6e1a-4f3b-8c2d-1a9f6e0b7c11",
"name": "Campanha de lançamento",
"status": "sending",
"source": "api",
"template_name": "lancamento_produto",
"template_language": "pt_BR",
"template_id": null,
"waba_connection_id": "5b0a9d0e-6e1a-4f3b-8c2d-1a9f6e0b7c11",
"phone_number_id": "5b0a9d0e-6e1a-4f3b-8c2d-1a9f6e0b7c11",
"scheduled_at": null,
"started_at": "2026-07-14T12:00:00.000Z",
"stopped_at": null,
"completed_at": null,
"counters": {
"total": 2,
"sent": 0,
"failed": 0,
"delivered": 0,
"read": 0,
"responded": 0
},
"created_at": "2026-07-14T12:00:00.000Z",
"updated_at": "2026-07-14T12:00:00.000Z"
}
}Listar destinatários
Lista os destinatários de uma transmissão, paginado por cursor. Além do status de envio, cada destinatário traz os carimbos de entrega (`sent_at`/`delivered_at`/`read_at`) atualizados pelos eventos de status do WhatsApp.
curl https://botozap.com.br/api/v1/broadcasts/5b0a9d0e-6e1a-4f3b-8c2d-1a9f6e0b7c11/recipients?limit=2 \
-H "X-API-Key: $BOTOZAP_KEY"Resposta 200
{
"data": [],
"paging": {
"cursors": {
"before": "MjAyNi0wNy0xNFQxMjowMDowMC4wMDBa",
"after": "MjAyNi0wNy0xNFQxMjowMDowMC4wMDBa"
},
"next": "MjAyNi0wNy0xNFQxMjowMDowMC4wMDBa",
"previous": "MjAyNi0wNy0xNFQxMjowMDowMC4wMDBa"
}
}Interromper um envio em andamento
Para interromper uma transmissão `sending`, use `PATCH { status: "stopped" }`. `POST /cancel` só funciona a partir de `draft`/`scheduled` (cancela um agendamento) — não interrompe um envio já em curso.
curl https://botozap.com.br/api/v1/broadcasts/5b0a9d0e-6e1a-4f3b-8c2d-1a9f6e0b7c11 \
-X PATCH \
-H "X-API-Key: $BOTOZAP_KEY" \
-H "Content-Type: application/json" \
-d '{
"status": "stopped"
}'Resposta 200
{
"data": {
"id": "5b0a9d0e-6e1a-4f3b-8c2d-1a9f6e0b7c11",
"name": "Campanha de lançamento",
"status": "stopped",
"source": "api",
"template_name": "lancamento_produto",
"template_language": "pt_BR",
"template_id": null,
"waba_connection_id": "5b0a9d0e-6e1a-4f3b-8c2d-1a9f6e0b7c11",
"phone_number_id": "5b0a9d0e-6e1a-4f3b-8c2d-1a9f6e0b7c11",
"scheduled_at": null,
"started_at": "2026-07-14T12:00:00.000Z",
"stopped_at": "2026-07-14T12:00:00.000Z",
"completed_at": null,
"counters": {
"total": 2,
"sent": 0,
"failed": 0,
"delivered": 0,
"read": 0,
"responded": 0
},
"created_at": "2026-07-14T12:00:00.000Z",
"updated_at": "2026-07-14T12:00:00.000Z"
}
}Limpar destinatários
Remove todos os destinatários de uma transmissão em `draft`. Idempotente: limpar um draft já vazio também responde 204.
curl https://botozap.com.br/api/v1/broadcasts/5b0a9d0e-6e1a-4f3b-8c2d-1a9f6e0b7c11/recipients \
-X DELETE \
-H "X-API-Key: $BOTOZAP_KEY"Resposta 204