Começo rápido
- No painel, conecte um número em Números.
- Crie uma chave em Chaves de API. Ela aparece uma vez só: guarde num cofre de senhas ou numa variável de ambiente.
- Mande a primeira mensagem:
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:
{ "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 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 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,audioouvideo. texttexto- O texto, quando
typeétext. templateobjeto- Quando
typeétemplate:name,languagee, se o modelo tiver variáveis,variables(na ordem) oucomponentsno formato da Meta. mediaobjeto- Para mídia:
linkpúblico em HTTPS, maiscaptionefilenameopcionais. Mídia vai sempre por link, nunca em base64. externalIdtexto- Seu identificador, para achar a mensagem no seu sistema.
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 -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 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 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 "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órioUTILITY,MARKETINGouAUTHENTICATION.componentslistaobrigatório- Cabeçalho, corpo, rodapé e botões, no formato da Meta.
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 -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 -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.
{
"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
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").Links de conexão
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 -X POST https://wpp.pivotlab.com.br/api/v1/connect-links \
-H "Authorization: Bearer $CHAVE_API"