API do WhatsApp

A API da Genial espelha a WhatsApp Cloud API da Meta. Os caminhos e os formatos de requisição e resposta são os mesmos da documentação oficial. Para os recursos listados abaixo (mensagens, templates, mídia e dados do número), só mudam a URL base e o token. Rotas de registro, inscrição de webhooks e sincronização da coexistência são operadas pela Genial e ficam bloqueadas para a chave do cliente.

URL base

https://app.genialsolucoes.com/v1

Substitui

https://graph.facebook.com/v26.0

Autenticação

Cada número conectado tem uma chave própria, no formato gk_live_…. Envie-a no cabeçalho Authorization. A chave só acessa o número e a conta (WABA) a que pertence.

GET/me

Retorna os identificadores do número ligado à chave.

curl https://app.genialsolucoes.com/v1/me \
  -H "Authorization: Bearer gk_live_SUA_CHAVE"

{
  "phone_number_id": "1234567890",
  "waba_id": "9876543210",
  "display_phone_number": "+55 33 99999-0000",
  "verified_name": "Minha Empresa"
}

Enviar mensagens

POST/{PHONE_NUMBER_ID}/messages

Texto livre só funciona até 24 horas depois da última mensagem do contato. Fora dessa janela, use um template.

curl -X POST https://app.genialsolucoes.com/v1/PHONE_NUMBER_ID/messages \
  -H "Authorization: Bearer gk_live_SUA_CHAVE" \
  -H "Content-Type: application/json" \
  -d '{
    "messaging_product": "whatsapp",
    "to": "5533999990000",
    "type": "text",
    "text": { "body": "Olá! Seu pedido saiu para entrega." }
  }'

Template com variável:

{
  "messaging_product": "whatsapp",
  "to": "5533999990000",
  "type": "template",
  "template": {
    "name": "pedido_enviado",
    "language": { "code": "pt_BR" },
    "components": [
      { "type": "body", "parameters": [{ "type": "text", "text": "Maria" }] }
    ]
  }
}

Imagem, vídeo, áudio, documento, localização, botões e listas seguem o mesmo formato da Meta. Para marcar como lida:

{ "messaging_product": "whatsapp", "status": "read", "message_id": "wamid.HBgM..." }

Templates

GET/{WABA_ID}/message_templates

POST/{WABA_ID}/message_templates

DELETE/{WABA_ID}/message_templates?name=nome_do_template

Mídia

POST/{PHONE_NUMBER_ID}/media

Upload em multipart/form-data (até 4 MB por requisição). Retorna o id da mídia para usar no envio.

GET/media/{MEDIA_ID}

Baixa o arquivo de uma mídia recebida. O id vem no webhook, em messages[].image.id, audio.id e assim por diante.

Webhooks

Todo evento que a Meta envia para o número é repassado por POST para a URL cadastrada, no formato original da Meta. Também vem um bloco genial com os identificadores. Os campos field possíveis incluem messages (mensagens e status de entrega), message_template_status_update, account_update smb_message_echoes (mensagens enviadas pelo app no celular, na coexistência), history (conversas antigas do app, até 180 dias, importadas na conexão) e smb_app_state_sync (contatos salvos no celular).

POST https://seu-sistema.com/webhook
X-Genial-Event: messages
X-Genial-Delivery: 5b0f…
X-Genial-Signature: t=1760980000,v1=9c1d…

{
  "object": "whatsapp_business_account",
  "entry": [{ "id": "WABA_ID", "changes": [{ "field": "messages", "value": { ... } }] }],
  "genial": { "instance_id": "…", "field": "messages", "phone_number_id": "…", "waba_id": "…" }
}

Validar a assinatura

Calcule o HMAC-SHA256 de {t}.{corpo bruto} com o segredo do webhook e compare com v1. Recuse eventos com t com mais de 5 minutos.

import crypto from "node:crypto";

function isValid(rawBody, header, secret) {
  const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
  const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}

Reenvio

Responda com status 2xx em até 10 segundos. Se a resposta for outra ou não vier, o evento é reenviado até 6 vezes, com intervalos crescentes. Use X-Genial-Delivery para ignorar duplicados.

Erros

Erros da Meta são devolvidos sem alteração. Erros da Genial usam o mesmo formato:

{ "error": { "message": "Invalid or missing API key.", "type": "GenialApiException", "code": 401 } }
  • 401: chave ausente ou inválida.
  • 403: a chave não tem acesso a esse número ou recurso, ou o número foi desconectado.