Pular para o conteúdo

API v1 do Recado Hub

Para seus sistemas enviarem mensagens pela API oficial do WhatsApp, acompanharem o status, criarem modelos e receberem eventos. Os nomes dos campos são em inglês, para facilitar a troca de outro provedor. A chave de API é criada no painel.

Começo rápido

  1. No painel, conecte um número em Números.
  2. Crie uma chave em Chaves de API. Ela aparece uma vez só: guarde num cofre de senhas ou numa variável de ambiente.
  3. Mande a primeira mensagem:
curl
curl -X POST https://wpp.pivotlab.com.br/api/v1/messages \
  -H "Authorization: Bearer $CHAVE_API" \
  -H "Content-Type: application/json" \
  -d '{
    "numberId": "ID_DO_NUMERO",
    "to": "5511999998888",
    "type": "text",
    "text": "Olá! Seu pedido saiu para entrega.",
    "externalId": "pedido-4521"
  }'

O endereço base é https://wpp.pivotlab.com.br/api/v1. Todas as rotas falam JSON. Texto livre só chega a quem falou com o número nas últimas 24 horas; fora dessa janela, use um modelo aprovado.

Autenticação

Mande a chave em um destes cabeçalhos: Authorization: Bearer <chave> ou x-api-key: <chave>. Uma chave ligada a um número só enxerga esse número; uma chave da conta enxerga todos.

A chave fica só no servidor do seu sistema, nunca no navegador ou no app. Se ela vazar, revogue em Chaves de API e crie outra. O token da Meta nunca sai do Recado Hub: seus sistemas usam só a chave.

Erros

Erros voltam com o código HTTP certo e um JSON com o campo mensagem, escrito para mostrar a uma pessoa:

JSON
{ "mensagem": "O número informado não pertence a esta conta." }
  • 401: chave ausente, errada ou revogada.
  • 400: corpo do pedido com campo faltando ou inválido.
  • 404: o recurso não existe nesta conta.

Números

GET/api/v1/numbers

Lista os números da conta. Com uma chave ligada a um número, devolve só esse número.

curl
curl https://wpp.pivotlab.com.br/api/v1/numbers \
  -H "Authorization: Bearer $CHAVE_API"

GET/api/v1/numbers/:id

Um número, com situação da conexão, qualidade e limite de envio informados pela Meta.

curl
curl https://wpp.pivotlab.com.br/api/v1/numbers/ID_DO_NUMERO \
  -H "x-api-key: $CHAVE_API"

Mensagens

POST/api/v1/messages

Envia uma mensagem de texto, de modelo ou de mídia.

numberIdtextoobrigatório
Id do número que envia.
totextoobrigatório
Telefone de quem recebe, com código do país e DDD, só números.
typetextoobrigatório
Um destes: text, template, image, document, audio ou video.
texttexto
O texto, quando type é text.
templateobjeto
Quando type é template: name, language e, se o modelo tiver variáveis, variables (na ordem) ou components no formato da Meta.
mediaobjeto
Para mídia: link público em HTTPS, mais caption e filename opcionais. Mídia vai sempre por link, nunca em base64.
externalIdtexto
Seu identificador, para achar a mensagem no seu sistema.

curl
curl -X POST https://wpp.pivotlab.com.br/api/v1/messages \
  -H "Authorization: Bearer $CHAVE_API" \
  -H "Content-Type: application/json" \
  -d '{
    "numberId": "ID_DO_NUMERO",
    "to": "5511999998888",
    "type": "template",
    "template": {
      "name": "pedido_enviado",
      "language": "pt_BR",
      "variables": ["Maria", "4521"]
    }
  }'

curl
curl -X POST https://wpp.pivotlab.com.br/api/v1/messages \
  -H "Authorization: Bearer $CHAVE_API" \
  -H "Content-Type: application/json" \
  -d '{
    "numberId": "ID_DO_NUMERO",
    "to": "5511999998888",
    "type": "document",
    "media": {
      "link": "https://seusite.com.br/notas/4521.pdf",
      "filename": "nota-4521.pdf",
      "caption": "Sua nota fiscal"
    }
  }'

GET/api/v1/messages/:id

O status de uma mensagem. O status só anda para frente: uma mensagem lida nunca volta a aparecer como entregue.

curl
curl https://wpp.pivotlab.com.br/api/v1/messages/ID_DA_MENSAGEM \
  -H "Authorization: Bearer $CHAVE_API"

GET/api/v1/messages

Lista as mensagens da conta, das mais novas para as mais antigas, com filtros.

curl
curl https://wpp.pivotlab.com.br/api/v1/messages \
  -H "Authorization: Bearer $CHAVE_API"

Modelos

GET/api/v1/templates?numberId=

Os modelos de mensagem de um número, com o status da análise da Meta.

curl
curl "https://wpp.pivotlab.com.br/api/v1/templates?numberId=ID_DO_NUMERO" \
  -H "Authorization: Bearer $CHAVE_API"

POST/api/v1/templates

Cria um modelo e manda para a análise da Meta. Os componentes seguem o formato da Meta. A Meta decide a categoria final: um pedido de UTILITY pode voltar como MARKETING.

numberIdtextoobrigatório
Id do número dono do modelo.
nametextoobrigatório
Só letras minúsculas, números e sublinhado.
languagetextoobrigatório
Código de idioma da Meta, como pt_BR.
categorytextoobrigatório
UTILITY, MARKETING ou AUTHENTICATION.
componentslistaobrigatório
Cabeçalho, corpo, rodapé e botões, no formato da Meta.
curl
curl -X POST https://wpp.pivotlab.com.br/api/v1/templates \
  -H "Authorization: Bearer $CHAVE_API" \
  -H "Content-Type: application/json" \
  -d '{
    "numberId": "ID_DO_NUMERO",
    "name": "pedido_enviado",
    "language": "pt_BR",
    "category": "UTILITY",
    "components": [
      {
        "type": "BODY",
        "text": "Oi, {{1}}! Seu pedido {{2}} saiu para entrega.",
        "example": { "body_text": [["Maria", "4521"]] }
      }
    ]
  }'

DELETE/api/v1/templates/:id

Apaga um modelo.

curl
curl -X DELETE https://wpp.pivotlab.com.br/api/v1/templates/ID_DO_MODELO \
  -H "Authorization: Bearer $CHAVE_API"

Webhooks

GET/POST/PATCH/DELETE/api/v1/webhooks

Cadastre, edite e apague os endereços que recebem os eventos, com os mesmos campos do painel: url, eventos e, para um número só, numeroId. O segredo de assinatura aparece uma vez só, na resposta da criação.

curl
curl -X POST https://wpp.pivotlab.com.br/api/v1/webhooks \
  -H "Authorization: Bearer $CHAVE_API" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://seusistema.com.br/recado/eventos",
    "eventos": ["mensagem.recebida", "mensagem.status"]
  }'

Eventos

mensagem.recebidaevento
Alguém mandou mensagem para um número da conta.
mensagem.statusevento
Uma mensagem enviada mudou de status (enviada, entregue, lida ou falhou).
modelo.statusevento
A Meta mudou o status de um modelo, como aprovado, recusado ou pausado.
numero.atualizadoevento
Mudou algo no número, como qualidade, limite de envio ou nome verificado.
numero.desconectadoevento
O número foi desconectado, pelo painel ou por aviso da Meta.

Entrega

Cada evento chega num POST com JSON e o cabeçalho X-Recado-Assinatura: sha256=<HMAC do corpo com o segredo>. O campo dados traz os detalhes do evento.

Corpo da entrega
{
  "evento": "mensagem.status",
  "conta": "ID_DA_CONTA",
  "numeroId": "ID_DO_NUMERO",
  "criadoEm": "2026-11-14T13:52:00.000Z",
  "dados": {}
}

Responda com um status 2xx logo que receber e processe depois. Se a entrega falhar, o Recado Hub tenta de novo em 30 s, 2 min, 10 min, 1 h e 6 h. Use o criadoEm e o conteúdo de dados para ignorar repetições.

Conferir a assinatura

Node.js
import crypto from "node:crypto";

// corpoCru: o corpo exatamente como chegou, antes de qualquer JSON.parse.
export function assinaturaValida(corpoCru, cabecalho, segredo) {
  const esperada =
    "sha256=" + crypto.createHmac("sha256", segredo).update(corpoCru).digest("hex");
  const a = Buffer.from(esperada);
  const b = Buffer.from(cabecalho ?? "");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

// Express: app.post("/recado/eventos", express.raw({ type: "application/json" }), ...)
// e compare com req.get("X-Recado-Assinatura").

POST/api/v1/connect-links

Cria um link de uso único para mandar a um cliente. Ele abre o link, entra com o Facebook da empresa dele na janela da Meta e conecta o número sozinho, sem acesso ao painel.

curl
curl -X POST https://wpp.pivotlab.com.br/api/v1/connect-links \
  -H "Authorization: Bearer $CHAVE_API"