Pular para o conteúdo principal

Campanhas

Uma campanha envia um template aprovado para um público: todos os contatos que um filtro seleciona, com as variáveis de cada um preenchidas. Pela API você monta, confere, lança e acompanha a campanha — o envio em si roda num processo à parte, no ritmo que você definir, sem gastar o limite de envio da sua chave.

O caminho de uma campanha

materializing é a montagem da lista de destinatários: o público é congelado no lançamento, e quem entrar na base depois não recebe. Uma campanha pode ainda terminar em canceled ou, quando ninguém do público pode recebê-la, em failed.

1. Criar o rascunho

curl -X POST https://api.joinotify.com/broadcasts \
-H 'Authorization: Bearer sk_live_xxx' \
-H 'Content-Type: application/json' \
-d '{
"name": "Black Friday",
"phoneNumberId": "106540352242922",
"templateName": "promo_black_friday",
"templateLanguage": "pt_BR",
"audienceId": "cm1aud1ence01",
"variables": {
"body": {
"1": { "source": "contact", "key": "first_name", "fallback": "cliente" },
"2": { "source": "static", "value": "BLACK20" }
}
},
"sendWindow": { "days": [1, 2, 3, 4, 5], "start": "09:00", "end": "20:00" },
"timezone": "America/Sao_Paulo"
}'

name é obrigatório; o resto pode vir depois, por PATCH. Nada é conferido ao salvar — a conferência acontece na estimativa, no teste e no lançamento.

Variáveis. Cada marcador do template precisa de uma fonte: contact (um dado do contato, como first_name), attribute (um campo personalizado) ou static (um valor fixo). As duas primeiras exigem um fallback para quem não tem o dado. Um template com cabeçalho de mídia precisa de headerMedia — suba o arquivo em POST /broadcasts/media e use o id.

Público. Um público salvo (audienceId) ou um filtro só desta campanha (audienceFilter), com a mesma árvore de condições. Crie públicos reaproveitáveis em POST /audiences e confira quem eles pegam em POST /audiences/preview.

2. Estimar e testar

POST /broadcasts/{id}/estimate confere a campanha inteira e responde quem ela alcançaria agora:

{
"data": {
"recipients": 4800,
"total": 5230,
"skipped": { "opted_out": 120, "no_consent": 290, "suppressed": 20 },
"cap": 50000,
"firstCampaign": false,
"overCap": 0,
"remainingQuota": 18000,
"portfolioLimit": 10000,
"estimatedCost": { "micros": 297600000, "currency": "brl", "byCountry": [], "unpriced": 0 }
}
}

Depois, mande a campanha para você mesmo com POST /broadcasts/{id}/test e { "to": "+5541987111527" }. O teste sai na hora, fora das métricas, e mostra as variáveis preenchidas com os valores reserva.

Quem recebe um template de marketing

Só contatos com consentimento (opted_in) e fora da lista de supressão. Templates de utilidade não dependem de consentimento, mas continuam pulando números inválidos. Veja Contatos e consentimento.

3. Lançar

curl -X POST https://api.joinotify.com/broadcasts/cm1bro4dc4st01/launch \
-H 'Authorization: Bearer sk_live_xxx' \
-H 'Content-Type: application/json' \
-d '{ "scheduledAt": "2026-11-27T12:00:00-03:00" }'

Sem scheduledAt, a campanha começa na hora. A resposta é 202. O lançamento recusa com 422:

typePor quê
audience_emptyNinguém do público pode receber
broadcast_limit_reachedO público passa do teto de destinatários (limit diz qual)
quota_exceededO plano não tem mais mensagens neste mês

O teto cresce com o histórico. A primeira campanha da conta vai para até 1.000 pessoas; as seguintes, para até dez vezes a maior campanha já concluída — sempre dentro do limite do plano. É o que protege a qualidade de um número novo de um disparo grande demais.

Editar uma campanha agendada a devolve para rascunho: é preciso lançar de novo.

4. Acompanhar

  • GET /broadcasts/{id}/stats — contadores, taxas, cliques por botão, falhas agrupadas, custo por país e a qualidade do número antes e agora.
  • GET /broadcasts/{id}/timeseries — a evolução em faixas de 15 minutos.
  • GET /broadcasts/{id}/recipients e /export — cada destinatário, com o status e o erro.
  • Os eventos broadcast.paused e broadcast.completed no seu webhook.

Pausas automáticas

Uma campanha para sozinha quando continuar prejudicaria o número: qualidade vermelha, template pausado pela Meta, conta restrita, cota do plano esgotada, erros demais seguidos. O motivo fica em pauseReason e chega no evento broadcast.paused — a tabela completa está no catálogo de eventos.

Duas pausas se resolvem sozinhas e trazem resumeAfter: send_window (fora da janela de envio) e throughput (a Meta segurou a vazão; tenta de novo em 15 minutos, 1 hora e 4 horas). As outras esperam alguém corrigir a causa e chamar POST /broadcasts/{id}/resume.

Envios sem resposta

Quando a Meta não responde a um envio, o destinatário fica unknown. Ele não é repetido sozinho: a mensagem pode ter chegado, e repetir mandaria duas vezes. Se você decidir reenviar, POST /broadcasts/{id}/resend-unknown com { "confirm": true } devolve esses destinatários à fila.

Escopo da chave

Uma API key restrita a alguns números só enxerga e cria campanhas nesses números. Os públicos salvos são da conta inteira: /audiences responde 403 para ela.

O público de uma campanha estimada ou lançada por essa chave — salvo ou digitado como filtro — fica restrito aos contatos que têm conversa nos números dela, e o recorte é congelado no lançamento, junto com o público. No envio de teste, um contactId fora desse recorte responde 404. Uma campanha lançada por uma chave sem restrição, ou pelo painel, vai para a base inteira.