BoaVoz
Documentação · API

Mensagens

Mande texto, modelo, imagem ou documento para um contato no WhatsApp ou no Instagram, ou para uma conversa do chat do site. O BoaVoz confere tudo o que sabe antes de chamar a Meta e diz o motivo quando não dá.

Permissão da chavemessagesPacoteIntegrações (R$ 49 por mês, por conta). Sem ele, a API responde 403 plan_not_allowed.
RotaO que faz
POST /v1/messagesEnvia texto, modelo, imagem ou documento.
GET /v1/messages/{id}A mensagem, com o status (sent, delivered, read, failed, uncertain).
GET /v1/bots/{id}/templatesOs modelos do número do WhatsApp do chatbot e se dá para enviar cada um pela API.

POST/v1/messages

WhatsApp e Instagram: bot_id e to, o endereço do contato. Chat do site: conversation_id, de uma conversa que o visitante abriu. O type escolhe o resto do corpo.

Texto no WhatsApp
curl -X POST "https://boavoz.com/api/v1/messages" \
  -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",
  "to": "phone:5511999990000",
  "type": "text",
  "text": "Teste da API do BoaVoz"
}'
Resposta · 201
{
  "message_id": "msg_48211",
  "conversation_id": "conv_0e8f5a3c-2b1d-4c6e-9f7a-1d2c3b4a5e6f",
  "status": "sent"
}
Texto assinado por alguém da equipe
curl -X POST "https://boavoz.com/api/v1/messages" \
  -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",
  "to": "phone:5511999990000",
  "type": "text",
  "text": "Seu pedido saiu para entrega.",
  "agent_name": "Ana",
  "sender": "postman"
}'
Resposta · 201
{
  "message_id": "msg_48212",
  "conversation_id": "conv_0e8f5a3c-2b1d-4c6e-9f7a-1d2c3b4a5e6f",
  "status": "sent"
}

agent_name faz a mensagem chegar como de alguém da equipe (“*Ana:* …” no WhatsApp); sender é quem mandou no seu sistema e volta no webhook message.sent.

Texto no chat do site
curl -X POST "https://boavoz.com/api/v1/messages" \
  -H "Authorization: Bearer $BOAVOZ_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 3b9f2c8e-5a1d-4e7b-9c6f-2d8a4e1b7c90" \
  -d '{
  "conversation_id": "conv_5d2a9e1b-3c4f-4b6a-8e7d-2f1a0b9c8d7e",
  "type": "text",
  "text": "Oi! Aqui é o sistema integrado.",
  "agent_name": "Ana"
}'
Resposta · 201
{
  "message_id": "msg_48213",
  "conversation_id": "conv_5d2a9e1b-3c4f-4b6a-8e7d-2f1a0b9c8d7e",
  "status": "sent"
}
Imagem com legenda
curl -X POST "https://boavoz.com/api/v1/messages" \
  -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",
  "to": "phone:5511999990000",
  "type": "image",
  "image": {
    "url": "https://loja.exemplo.com.br/fotos/pedido-1234.jpg"
  },
  "text": "Foto do seu pedido"
}'
Resposta · 201
{
  "message_id": "msg_48214",
  "conversation_id": "conv_0e8f5a3c-2b1d-4c6e-9f7a-1d2c3b4a5e6f",
  "status": "sent"
}
Documento
curl -X POST "https://boavoz.com/api/v1/messages" \
  -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",
  "to": "phone:5511999990000",
  "type": "document",
  "document": {
    "url": "https://loja.exemplo.com.br/boletos/1234.pdf",
    "filename": "Boleto teste.pdf"
  },
  "text": "Segue o seu boleto"
}'
Resposta · 201
{
  "message_id": "msg_48215",
  "conversation_id": "conv_0e8f5a3c-2b1d-4c6e-9f7a-1d2c3b4a5e6f",
  "status": "sent"
}
Modelo aprovado (também fora da janela de 24 horas)
curl -X POST "https://boavoz.com/api/v1/messages" \
  -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",
  "to": "phone:5511999990000",
  "type": "template",
  "template": {
    "name": "pedido_saiu",
    "language": "pt_BR",
    "variables": [
      "Ruan"
    ]
  }
}'
Resposta · 201
{
  "message_id": "msg_48216",
  "conversation_id": "conv_0e8f5a3c-2b1d-4c6e-9f7a-1d2c3b4a5e6f",
  "status": "sent"
}

Fora da janela de 24 horas (o contato não escreveu nesse tempo), só sai modelo aprovado. Confira os modelos e o número de variáveis de cada um em GET /v1/bots/{id}/templates.

Texto com botões
curl -X POST "https://boavoz.com/api/v1/messages" \
  -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",
  "to": "phone:5511999990000",
  "type": "text",
  "text": "Quer receber o cardápio do dia?",
  "components": {
    "type": "buttons",
    "buttons": [
      {
        "title": "Sim, quero",
        "id": "quero_cardapio"
      },
      {
        "title": "Agora não",
        "id": "agora_nao"
      }
    ]
  }
}'
Resposta · 201
{
  "message_id": "msg_48217",
  "conversation_id": "conv_0e8f5a3c-2b1d-4c6e-9f7a-1d2c3b4a5e6f",
  "status": "sent"
}

Botões, lista, link e carrossel vão em components, junto de um texto. O formato e como cada um fica em cada canal estão em Mensagens ricas.

Regras e erros dos campos

CampoRegraErro
bot_idObrigatório no WhatsApp e no Instagram: o id com bot_, de um chatbot do escopo da chave.403 forbidden_scope
toObrigatório no WhatsApp e no Instagram: ctc_…, phone:55…, wa:<BSUID>, ig:<id> ou ext:<id>. Contato que não existe no chatbot: 404.400 to
channelwhatsapp ou instagram. Obrigatório com ext:; com os outros formatos, se vier, precisa bater com o do to.400 channel
conversation_idSó no chat do site: conv_…, de uma conversa do site. Não vai junto com to.400 conversation_id
typetext, template, image ou document. No chat do site, não há modelo.400 type
textCom text: obrigatório, até 4.096 caracteres (com components, até 1.024). Com imagem e documento: a legenda, até 1.024. Com modelo: não mande (o texto vem do modelo).400 text
image, document{url, filename?}: url HTTPS pública (até 2.048 caracteres); filename até 200. Imagem: JPEG ou PNG até 5 MB. Documento: PDF, Word, Excel, PowerPoint, TXT ou CSV até 16 MB. No Instagram, PDF e imagem até 8 MB; no site, documento só em PDF. O BoaVoz confere o tipo pelos primeiros bytes.400 image.url, document.url, ….filename
template{name, language?, variables?}: o nome de um modelo aprovado do número, o idioma (padrão pt_BR) e uma variável por {{1}}, {{2}}…, na ordem (até 20, cada uma até 1.024). Só modelos de utilidade e de marketing; o de marketing pede o aceite de novidades.400 template.name, template.language, template.variables
componentsBotões, lista, link ou carrossel, só com type: text (formato).400 components…; 422 prohibited_content
agent_nameOpcional: assina como alguém da equipe (até 80 caracteres).400 agent_name
senderOpcional: quem mandou no seu sistema (até 64: letras, números, espaço, ponto e hífen).400 sender

O que o BoaVoz confere antes de mandar

ErroQuando
422 outside_messaging_windowO contato não escreveu nas últimas 24 horas: no WhatsApp, só modelo; no Instagram, nada.
422 channel_not_connectedO chatbot não tem o canal conectado, ou o cliente tirou o acesso do BoaVoz (conecte de novo no painel).
422 bot_pausedO chatbot está pausado pelo dono: nada sai pela API até ele ser retomado.
422 bot_without_sizeModelo num chatbot sem tamanho: campanhas, lembretes e modelos só saem de chatbot com tamanho.
422 contact_suppressedO contato pediu para apagar os dados: está na lista de supressão.
422 contact_opted_outO contato pediu para não receber esse tipo de mensagem (SAIR, botão ou o próprio WhatsApp).
422 no_marketing_consentModelo de marketing para quem não aceitou novidades (ou disse que não tem 18 anos).
422 prohibited_contentO portão barrou o texto, um título ou o modelo pelas regras da Meta (o texto nunca é editado).
422 channel_rejectedA Meta recusou; details.channel_code traz o código dela.
403 feature_suspendedO canal está suspenso, ou o negócio ainda não fez o aceite do canal.
  • Deu certo: 201 com "status": "sent". Sem resposta da Meta no prazo: 202 com "status": "uncertain"; o status da Meta concilia depois (webhook message.status ou message.failed). O BoaVoz nunca reenvia sozinho: para tentar de novo, use a mesma Idempotency-Key.
  • Erro temporário da Meta: 503 ou 429 com Retry-After.
Repetir com a mesma Idempotency-Key
curl -X POST "https://boavoz.com/api/v1/messages" \
  -H "Authorization: Bearer $BOAVOZ_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: pedido-1234-saiu" \
  -d '{
  "bot_id": "bot_7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "to": "phone:5511999990000",
  "type": "text",
  "text": "Mensagem idempotente"
}'
Resposta · 201
{
  "message_id": "msg_48218",
  "conversation_id": "conv_0e8f5a3c-2b1d-4c6e-9f7a-1d2c3b4a5e6f",
  "status": "sent"
}
Repetir com a mesma Idempotency-Key (em até 24 horas) devolve o mesmo message_id, com o cabeçalho Idempotent-Replayed: true, e só uma mensagem chega ao contato.

GET/v1/messages/{id}

A mensagem no mesmo formato do histórico. status: received (do contato), pending, sent, delivered, read, failed (com error.code) ou uncertain. source: contact, ai, human (equipe), api, campaign ou phone_app (o app do celular, na coexistência).

Ler uma mensagem (com o status)
curl "https://boavoz.com/api/v1/messages/msg_48211" \
  -H "Authorization: Bearer $BOAVOZ_API_KEY"
Resposta · 200
{
  "id": "msg_48211",
  "conversation_id": "conv_0e8f5a3c-2b1d-4c6e-9f7a-1d2c3b4a5e6f",
  "direction": "outbound",
  "source": "api",
  "text": "Teste da API do BoaVoz",
  "media": null,
  "agent": {
    "type": "api",
    "display_name": "Integração",
    "sender": null
  },
  "status": "read",
  "error": null,
  "deleted": false,
  "edited": false,
  "reply_to": null,
  "reactions": {
    "contact": null,
    "agent": null
  },
  "created_at": "2026-10-19T15:02:11.481Z"
}

GET/v1/bots/{id}/templates

Os modelos do número do WhatsApp do chatbot, com a categoria, o status da Meta, o texto e o número de variáveis. sendable diz se dá para mandar pela API (aprovado, de utilidade ou de marketing e num formato que o BoaVoz envia); quando não dá, unsupported_reason diz por quê. Sem WhatsApp conectado: 422 channel_not_connected.

Os modelos do número
curl "https://boavoz.com/api/v1/bots/bot_7c9e6679-7425-40de-944b-e07fc1f90ae7/templates" \
  -H "Authorization: Bearer $BOAVOZ_API_KEY"
Resposta · 200
{
  "data": [
    {
      "name": "pedido_saiu",
      "language": "pt_BR",
      "category": "utility",
      "status": "approved",
      "rejected_reason": null,
      "body": "Oi, {{1}}! O seu pedido saiu para entrega.",
      "variables": 1,
      "sendable": true,
      "unsupported_reason": null
    },
    {
      "name": "promo_sexta",
      "language": "pt_BR",
      "category": "marketing",
      "status": "pending",
      "rejected_reason": null,
      "body": "{{1}}, a sexta chegou com 10% de desconto no cardápio!",
      "variables": 1,
      "sendable": false,
      "unsupported_reason": null
    }
  ]
}

Próximos passos