Nevoa Manager

Documentação para desenvolvedores

Public API v1

Receba uma cópia das conversas via webhook e manipule conversas, contatos, tags e setores pela API REST.

Visão geral

Webhook de saída

A Névoa faz POST no seu endpoint com mensagens e eventos de ciclo de vida, assinados com HMAC.

API REST

Endpoints em /api/public/v1 autenticados com Authorization: Bearer <apiToken>.

Escopo por número

Vincule integrações META_WHATSAPP em linkedIntegrationIds. Todo payload de conversa inclui o objeto channel.

Quickstart

  1. No painel Integrações, crie uma integração PUBLIC_API.
  2. Gere e copie o apiToken e o webhookSecret.
  3. Informe o webhookUrl (HTTPS) e marque os eventos desejados.
  4. Selecione os números WhatsApp vinculados — ou deixe vazio para todos.
  5. Use o Bearer token nas chamadas REST (ex.: POST /messages com phone) e verifique o HMAC nos webhooks.

Base URL

https://web-production-b9668.up.railway.app/api/public/v1

Versão atual: v1. Mudanças incompatíveis geram nova versão (v2). A v1 permanece estável. O envelope do webhook inclui apiVersion: "v1".

Autenticação

Todas as rotas em /api/public/v1/** exigem o token da integração PUBLIC_API ACTIVE.

Header

Authorization: Bearer <apiToken>

Exemplo

curl -s \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  "https://web-production-b9668.up.railway.app/api/public/v1/channels"

Rotação de token

  1. Gere um novo token no painel da integração.
  2. Atualize o cliente para o novo token.
  3. Salve a integração — o token antigo deixa de funcionar imediatamente.
  4. O webhookSecret (HMAC) é independente e pode ser rotacionado da mesma forma.
StatusQuando
401Header ausente, formato inválido, token inexistente ou integração INACTIVE
404Recurso fora do escopo linkedIntegrationIds (não vaza existência)
409Conflito de negócio (ex.: TEMPLATE_REQUIRED ao enviar free-form fora da janela 24h)
429Rate limit da Public API excedido (padrão 120 req/min por integração)

Canais e escopo

config.linkedIntegrationIds lista as integrações META_WHATSAPP vinculadas. Lista vazia ou ausente = todos os números do tenant (inclusive futuros).

Objeto channel

JSON

{
  "integrationId": "wa-uuid",
  "name": "WhatsApp Comercial",
  "provider": "META_WHATSAPP",
  "phoneNumberId": "123",
  "displayPhoneNumber": "+5511999999999"
}
  • Em listagens, use o filtro channelIntegrationId para restringir a um número (deve pertencer ao escopo).
  • Rotas com :id de conversa/contato fora do vínculo retornam 404 (não vazam existência).
  • Todo payload de mensagem/conversa (API e webhook) inclui o objeto channel.

Canais

Ações — clique para expandir

Endpoints REST

Conversas

Ações — clique para expandir

Mensagens / Templates

Envie free-form por telefone/contato (última conversa) ou inicie com template aprovado.

Ações — clique para expandir

Contatos

Ações — clique para expandir

Catálogos

Ações — clique para expandir

Webhook deliveries

Ações — clique para expandir

Webhooks

A Névoa entrega eventos no seu webhookUrl com envelope versionado e assinatura HMAC.

Envelope e headers

Envelope

{
  "id": "uuid-da-entrega",
  "event": "message.received",
  "apiVersion": "v1",
  "occurredAt": "2026-07-19T12:00:00.000Z",
  "data": {}
}
  • Content-Typeapplication/json
  • X-Nevoa-EventTipo do evento
  • X-Nevoa-Delivery-IdID da entrega (idempotência)
  • X-Nevoa-TimestampUnix epoch (segundos)
  • X-Nevoa-Signaturesha256=<hmac-sha256 hex de `${timestamp}.${corpoBruto}`>
  • User-AgentNevoa-PublicAPI/1.0

Política de entrega

  • Timeout: 10s
  • Retries: 5 tentativas, backoff exponencial (5s, 10s, 20s, 40s, 80s)
  • Sucesso: HTTP 2xx
  • Idempotência: use X-Nevoa-Delivery-Id / id do envelope
  • Integração INACTIVE ou isWebhookEnabled=false → entrega DISABLED
  • HMAC inclui timestamp; rejeite skew > 300s (anti-replay)

Verificação HMAC

Node.js

const crypto = require('crypto');

function verify(rawBody, secret, signatureHeader, timestampHeader) {
  const ts = Number(timestampHeader);
  if (!Number.isFinite(ts) || Math.abs(Date.now() / 1000 - ts) > 300) {
    return false;
  }
  const expected =
    'sha256=' +
    crypto
      .createHmac('sha256', secret)
      .update(`${ts}.${rawBody}`, 'utf8')
      .digest('hex');
  const a = Buffer.from(expected);
  const b = Buffer.from(signatureHeader || '');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Python

import hmac, hashlib, time

def verify(raw_body: bytes, secret: str, signature_header: str, timestamp_header: str) -> bool:
    try:
        ts = int(timestamp_header)
    except (TypeError, ValueError):
        return False
    if abs(int(time.time()) - ts) > 300:
        return False
    payload = f"{ts}.".encode() + raw_body
    digest = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
    expected = f"sha256={digest}"
    return hmac.compare_digest(expected, signature_header or "")

Catálogo de eventos

Eventos — clique para expandir

Erros

400 Requisição inválida

Body ou query string fora do contrato: campo obrigatório ausente, enum inválido, filtro não suportado (ex.: assigned=me).

Exemplo

{ "statusCode": 400, "message": ["type must be one of the following values: TEXT, IMAGE, VIDEO, AUDIO, DOCUMENT"], "error": "Bad Request" }

401 Não autorizado

Bearer ausente, inválido ou integração PUBLIC_API inativa.

Exemplo

{ "statusCode": 401, "message": "Token de API inválido ou integração inativa" }

404 Não encontrado (escopo)

Conversa, contato ou canal fora de linkedIntegrationIds. A API responde 404 para não vazar existência.

Exemplo

{ "statusCode": 404, "message": "Conversa não encontrada" }

409 TEMPLATE_REQUIRED

Envio free-form fora da janela de 24h do WhatsApp. Use template.

Exemplo

{
  "code": "TEMPLATE_REQUIRED",
  "message": "Janela de 24h indisponível. É necessário aguardar resposta do cliente ou usar template."
}

429 Rate limit excedido

Limite de 120 requisições por minuto por integração. Aguarde e tente novamente com backoff.

Exemplo

{ "statusCode": 429, "message": "Limite de requisições da Public API excedido. Tente novamente em instantes." }

Changelog

v1 · 2026-07-19

  • Provider PUBLIC_API com apiToken, webhookSecret, webhookUrl, subscribedEvents, isWebhookEnabled, linkedIntegrationIds
  • Webhook de saída (outbox + HMAC + retries) para 8 tipos de evento
  • Objeto channel em payloads de mensagem/conversa
  • API REST /api/public/v1 (conversas, mensagens, templates, contatos, catálogos, channels, webhook-deliveries)
  • POST /messages — free-form por phone/contactId (conversa mais recente no escopo)