BoaVoz
Documentação · API

Campanhas e aceite

Mande campanhas de novidades e lotes de lembretes pelo WhatsApp, acompanhe os totais e registre o aceite de novidades que a pessoa deu no seu sistema. Os envios saem pelo mesmo motor do painel, com as mesmas conferências.

Permissão da chavecampaignsPacoteIntegrações (R$ 49 por mês, por conta). Sem ele, a API responde 403 plan_not_allowed.
RotaO que faz
POST /v1/campaignsCampanha de novidades ou lote de lembretes, com modelo aprovado.
GET /v1/campaigns/{id}O status e os totais.
POST /v1/contacts/{contact}/consentRegistra o aceite (ou a retirada) de novidades no WhatsApp.

As regras das campanhas (aceite, SAIR, 18+, horário e custo) estão em Campanhas e consentimento.

POST/v1/campaigns

kind marketing: um modelo de marketing para um público (etiquetas, contatos ou ids externos); só recebe quem aceitou novidades (e, com bebida ou remédio, quem confirmou 18 anos). kind utility_reminder: um modelo de utilidade com uma linha por envio, cada uma com o contato, a hora e as variáveis dela. Quem pediu para sair é pulado; o envio confere tudo de novo, como no painel. Idempotency-Key obrigatória: repetir não cria outra campanha.

Campanha de novidades para uma etiqueta
curl -X POST "https://boavoz.com/api/v1/campaigns" \
  -H "Authorization: Bearer $BOAVOZ_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 3b9f2c8e-5a1d-4e7b-9c6f-2d8a4e1b7c90" \
  -d '{
  "bot_id": "bot_7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "kind": "marketing",
  "name": "Teste Postman",
  "template": {
    "name": "promo_sexta",
    "language": "pt_BR",
    "variables": [
      {
        "type": "first_name",
        "fallback": "cliente"
      }
    ]
  },
  "audience": {
    "tags": [
      "vip"
    ]
  }
}'
Resposta · 201
{
  "campaign_id": "cmp_2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d",
  "status": "sending",
  "estimated_contacts": 312,
  "estimated_cost_cents": 1934,
  "estimated_days": 1,
  "excluded": {
    "no_consent": 41,
    "suppressed": 3
  },
  "quiet_hours": false
}
Lembretes, um por linha
curl -X POST "https://boavoz.com/api/v1/campaigns" \
  -H "Authorization: Bearer $BOAVOZ_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 3b9f2c8e-5a1d-4e7b-9c6f-2d8a4e1b7c90" \
  -d '{
  "bot_id": "bot_7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "kind": "utility_reminder",
  "name": "Lembrete teste",
  "template": {
    "name": "lembrete_consulta",
    "language": "pt_BR"
  },
  "sends": [
    {
      "contact": "phone:5511999990000",
      "send_at": "2026-10-20T18:00:00-03:00",
      "variables": [
        "Ruan"
      ]
    }
  ]
}'
Resposta · 201
{
  "campaign_id": "cmp_2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d",
  "status": "scheduled",
  "estimated_contacts": 1,
  "estimated_cost_cents": 5,
  "excluded": {},
  "off_hours": 0
}
CampoRegraErro
Idempotency-KeyCabeçalho obrigatório (uma chave por campanha).400 Idempotency-Key
bot_idObrigatório; o chatbot precisa ter tamanho e WhatsApp conectado.403 forbidden_scope; 422 bot_without_size, channel_not_connected, bot_paused
channelSó whatsapp (o padrão): o Instagram não tem mensagem de marketing.400 channel
kindmarketing ou utility_reminder. O modelo precisa ser da categoria certa.400 kind; 422 template_category_mismatch
nameOpcional: o nome no painel (até 120 caracteres).400 name
template{name, language?, variables?}, de um modelo aprovado do número. Na campanha, cada variável é um texto ou {"type": "first_name", "fallback": "cliente"} (o primeiro nome do contato), até 20.400 template.name, template.variables; 422 prohibited_content
audienceSó no marketing: tags (até 20), contacts (até 10.000 endereços) e external_ids (até 1.000). Pelo menos um. Ninguém apto: 422 empty_audience, com quem ficou de fora em details.excluded.400 audience…
send_atSó no marketing, opcional: agenda o envio (ISO 8601 com o fuso, até 60 dias à frente).400 send_at
regulatedSó no marketing, opcional: true quando a campanha fala de bebida alcoólica ou remédio, e ela só vai para quem confirmou 18 anos. O BoaVoz também marca sozinho quando o modelo fala disso.400 regulated
sendsSó nos lembretes: até 2.000 linhas {contact, send_at, variables}. send_at com o fuso, não no passado e até 90 dias à frente; variables, textos de até 200 caracteres sem quebra de linha, tantos quanto o modelo pede.400 sends[i].…

Na resposta, excluded conta quem ficou de fora, por motivo: no_consent (sem aceite de novidades), suppressed (pediu para sair), minor e no_age (18+), country, no_phone e not_contact. estimated_cost_cents é a estimativa da tarifa da Meta.

Lembrete para quem nunca conversou com o chatbot
Exige a declaração de consentimento para lembretes do cliente (no painel, aba Campanhas do cliente). Sem ela: 400 com details.field utility_consent e as linhas.

GET/v1/campaigns/{id}

status: scheduled, sending, paused (com pause_reason), finished ou canceled. Os totais contam enviadas, entregues, lidas, respondidas, falhas, puladas por motivo e descadastros.

O status e os totais
curl "https://boavoz.com/api/v1/campaigns/cmp_2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d" \
  -H "Authorization: Bearer $BOAVOZ_API_KEY"
Resposta · 200
{
  "id": "cmp_2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d",
  "bot_id": "bot_7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "kind": "marketing",
  "name": "Teste Postman",
  "status": "finished",
  "pause_reason": null,
  "template": {
    "name": "promo_sexta",
    "language": "pt_BR",
    "category": "marketing"
  },
  "scheduled_at": null,
  "created_at": "2026-10-19T15:20:00.000Z",
  "finished_at": "2026-10-19T15:31:12.000Z",
  "estimated_contacts": 312,
  "estimated_cost_cents": 1934,
  "totals": {
    "sent": 312,
    "delivered": 305,
    "read": 211,
    "replied": 27,
    "failed": 7,
    "pending": 0,
    "uncertain": 0,
    "skipped_no_consent": 41,
    "skipped_no_age": 0,
    "skipped_suppressed": 3,
    "opted_out": 2
  }
}

POST/v1/contacts/{contact}/consent

Grava o aceite de novidades no WhatsApp com a prova (onde, o texto e quando), e cria o contato se ele ainda não existe. Quem pediu para sair continua fora (opted_out: true): só um novo sim dele no chat tira do descadastro. granted: false retira o aceite e emite o webhook contact.opted_out.

Registrar o aceite de novidades
curl -X POST "https://boavoz.com/api/v1/contacts/phone:5511999990000/consent" \
  -H "Authorization: Bearer $BOAVOZ_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 3b9f2c8e-5a1d-4e7b-9c6f-2d8a4e1b7c90" \
  -d '{
  "bot_id": "bot_7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "channel": "whatsapp",
  "granted": true,
  "source": "Teste pelo Postman",
  "text": "Aceito receber novidades e promoções por WhatsApp"
}'
Resposta · 201
{
  "consent_id": "cns_7a8b9c0d-1e2f-4a3b-8c4d-5e6f7a8b9c0d",
  "contact_id": "ctc_9a1b2c3d-4e5f-4a6b-8c7d-0e1f2a3b4c5d",
  "opted_out": false
}
CampoRegraErro
bot_idObrigatório; o chatbot precisa ter WhatsApp conectado.403 forbidden_scope; 422 channel_not_connected
channelSó whatsapp (o padrão). Contato do Instagram: 400.400 channel, contact
grantedtrue (aceitou) ou false (retirou).400 granted
sourceObrigatório: onde a pessoa aceitou ou retirou (3 a 200 caracteres, ex.: cadastro no site).400 source
textObrigatório: o texto que ela aceitou (10 a 1.000 caracteres).400 text
collected_atOpcional: quando foi (ISO 8601, no passado). Padrão: agora.400 collected_at

Contato que pediu para apagar os dados: 422 contact_suppressed (só um novo sim dado por ele no chat vale).