Menu da documentação

Webhooks

Receba avisos em tempo real quando uma conversa passa para humano, um lead aparece ou um agendamento é pedido.

Com webhooks, a Nevox avisa o seu sistema quando algo acontece. É assim que uma conversa que precisa de humano aparece direto no seu painel administrativo, sem ninguém abrir a Nevox.

Cadastrar um endpoint

Pelo painel

Não precisa de chave de API.

  1. Entre na Nevox e confira, no seletor de empresas, se a empresa aberta é a certa.
  2. Vá em Desenvolvedores → Webhooks → Adicionar endpoint.
  3. Cole a URL do seu sistema que vai receber os avisos (precisa começar com https://).
  4. Escolha o ambiente (Produção para dados reais) e marque os eventos.
  5. Salve e copie o segredo whsec_…: ele aparece uma única vez. Perdeu? Apague o endpoint e cadastre de novo, ou gere outro pela API (rotate-secret, abaixo).

Na mesma tela ficam as últimas entregas de cada endpoint, com Reenviar, e o botão para ligar e desligar. Para mandar um aviso de teste, use POST /v1/webhooks/{id}/test (abaixo).

Pela API

POST /v1/webhooks · escopo webhooks:write

bash
curl https://api.nevox.ai/v1/webhooks \
  -H "Authorization: Bearer $NEVOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://api.suaempresa.com/webhooks/nevox",
    "events": ["conversation.handoff", "conversation.closed", "lead.created"],
    "description": "Fila de atendimento do admin"
  }'
json
{
  "id": "whk_6tq2",
  "url": "https://api.suaempresa.com/webhooks/nevox",
  "events": ["conversation.handoff", "conversation.closed", "lead.created"],
  "status": "enabled",
  "secret": "whsec_...",
  "created_at": "2026-11-03T17:20:00Z"
}

O secret aparece uma única vez. Outros endpoints: GET /v1/webhooks, PATCH /v1/webhooks/{id}, DELETE /v1/webhooks/{id}, POST /v1/webhooks/{id}/rotate-secret, POST /v1/webhooks/{id}/test.

Use "events": ["*"] para receber todos. O endpoint fica no ambiente da chave que o criou: endpoint criado com chave de teste recebe só os eventos de conversas de teste. A URL precisa usar https e não pode apontar para endereço de rede interna.

POST /v1/webhooks/{id}/test envia na hora um evento webhook.test só para aquele endpoint e devolve o resultado.

Formato do evento

http
POST /webhooks/nevox HTTP/1.1
Content-Type: application/json
Nevox-Signature: t=1762183200,v1=9ab3...
Nevox-Event-Id: evt_1z8x
Nevox-Environment: live
json
{
  "id": "evt_1z8x",
  "type": "conversation.handoff",
  "created_at": "2026-11-03T17:20:00Z",
  "data": {
    "conversation": {
      "id": "conv_7mk2p9",
      "agent_id": "agt_8f2k1x",
      "channel": "whatsapp",
      "status": "handoff",
      "summary": "Paciente pede reembolso da consulta de 28/10; diz que não conseguiu entrar na chamada.",
      "reason": "refund_request"
    },
    "contact": {
      "id": "ctc_4hq81",
      "external_id": "paciente_1042",
      "name": "Maria Souza"
    }
  }
}

A assinatura usa o mesmo formato das ações: HMAC-SHA256 de "{t}.{corpo bruto}" com o secret do endpoint.

Eventos

EventoQuando
conversation.createdNova conversa, em qualquer canal
conversation.handoffO agente passou para humano (motivo em reason)
conversation.human_assignedAlguém da equipe respondeu pela primeira vez
conversation.closedConversa encerrada
message.createdNova mensagem (alto volume; use só se precisar espelhar o chat)
contact.created / contact.updatedContato criado ou com dados alterados
lead.created (em breve)Um visitante ou contato foi qualificado como lead
appointment.requested (em breve)O agente coletou um pedido de agendamento
knowledge.ready / knowledge.failed (em breve)Arquivo ou URL terminou de processar
learning.suggestion_created (beta)Nova sugestão de aprendizado
visitor.identifiedUm visitante anônimo foi ligado a um contato
analytics.export_readyUma exportação terminou
form.submittedAlguém enviou um formulário de um site da empresa feito pela Nevox

Valores de reason em conversation.handoff: complaint, refund_request, special_request, forbidden_topic, user_asked_human, low_confidence, action_failed, manual.

Formulários dos sites

Quando a Nevox faz o site da empresa, cada formulário enviado nele (contato, orçamento, cadastro) vira o evento form.submitted e fica na lista GET /v1/form_submissions, no mesmo formato. Assine o evento para receber na hora; use a lista para buscar o histórico ou conferir se perdeu algum.

json
{
  "id": "evt_9c1e0b7a4f2d4e8a9b3c5d6e7f8a9b0c",
  "type": "form.submitted",
  "created_at": "2026-10-28T14:05:12Z",
  "data": {
    "form_submission": {
      "id": "frm_3f9c2a7e0b1d4c5e8f6a9b0c1d2e3f40",
      "site": { "id": "6a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d", "name": "Site TAZZ", "domain": "tazz.app" },
      "form": "Cadastro de motorista",
      "name": "Maria Souza",
      "email": "maria@exemplo.com",
      "phone": "(11) 98765-4321",
      "phone_e164": "+5511987654321",
      "message": "Quero dirigir aos fins de semana.",
      "fields": {
        "Cidade": "São Paulo",
        "Página": "/motoristas",
        "Idioma": "pt-BR",
        "Veio de": "https://www.google.com/",
        "utm_source": "instagram",
        "utm_medium": "bio",
        "utm_campaign": "motoristas-outubro"
      },
      "page": "/motoristas",
      "referrer": "https://www.google.com/",
      "language": "pt-BR",
      "utm": { "source": "instagram", "medium": "bio", "campaign": "motoristas-outubro", "term": null, "content": null },
      "created_at": "2026-10-28T14:05:12Z"
    }
  }
}
  • phone vem como a pessoa escreveu; phone_e164 só quando dá para ter certeza do país (o número começa com + ou tem o tamanho de um número do país do site). Senão, null.
  • fields traz todos os outros campos do formulário, com o rótulo do site, mais página, idioma, origem e utm_*. page, referrer, language e utm repetem esses valores para você não precisar ler fields.
  • Só formulários do ambiente live. O evento sai uma vez por envio; o id do formulário (frm_…) serve para não gravar duas vezes.

Passo a passo para receber os formulários

  1. Cadastre um endpoint em Desenvolvedores → Webhooks com o evento form.submitted, no ambiente Produção (como cadastrar). Guarde o segredo whsec_… no seu sistema.
  2. No seu sistema, confira a assinatura de cada aviso (como conferir), grave o formulário pelo id (frm_…) e responda 2xx.
  3. Envie um formulário de verdade no site: ele deve aparecer no seu sistema em segundos, e a entrega aparece em Últimas entregas no painel.
  4. Para trazer os formulários enviados antes disso, use Gerar chave em Desenvolvedores → Chaves de API com uma chave de Produção só com o escopo forms:read e use a lista abaixo.

O segredo e a chave dão acesso a nome, telefone e email de quem preencheu: guarde no servidor, nunca no navegador nem em mensagem aberta. Vazou? Troque o segredo (apague e recadastre o endpoint, ou rotate-secret) e revogue a chave.

Buscar o histórico

bash
curl "https://api.nevox.ai/v1/form_submissions?form=Cadastro%20de%20motorista&created_after=2026-10-01T00:00:00Z&limit=50"   -H "Authorization: Bearer $NEVOX_SECRET_KEY"
json
{
  "data": [ { "id": "frm_3f9c2a7e0b1d4c5e8f6a9b0c1d2e3f40", "form": "Cadastro de motorista", "name": "Maria Souza", "...": "..." } ],
  "has_more": true,
  "next_cursor": "MTIz"
}

Escopo forms:read. Mais novos primeiro, até 100 por página; para a próxima, mande cursor com o next_cursor. Filtros: site_id (o id do site), form (o nome exato do formulário) e created_after (data ISO 8601).

Entrega e novas tentativas

  • Responda com qualquer 2xx em até 10 segundos. Processe o evento depois, numa fila.
  • Sem 2xx, a Nevox tenta de novo em 1 min, 5 min, 30 min, 2 h, 6 h, 12 h e 24 h.
  • Depois de 3 dias falhando, o endpoint é desativado e o dono da conta recebe um email.
  • A ordem de entrega não é garantida. Use created_at e busque o estado atual pela API se precisar.
  • O mesmo evento pode chegar mais de uma vez. Use o id do evento para ignorar repetidos.

As últimas entregas aparecem em Painel → Desenvolvedores → Webhooks, com botão para reenviar. O histórico fica guardado por 30 dias.

O summary da conversa pode vir null no evento: o resumo é gerado logo depois e fica em GET /v1/conversations/{id}.

Exemplo: fila de atendimento no seu admin

js
// app/api/webhooks/nevox/route.ts (Next.js)
import { assinaturaValida } from "@/lib/nevox";

export async function POST(req: Request) {
  const raw = await req.text();
  const ok = assinaturaValida(
    raw,
    req.headers.get("nevox-signature") ?? "",
    process.env.NEVOX_WEBHOOK_SECRET!
  );
  if (!ok) return new Response("assinatura inválida", { status: 400 });

  const evento = JSON.parse(raw);

  if (evento.type === "conversation.handoff") {
    await db.ticket.upsert({
      where: { nevoxConversationId: evento.data.conversation.id },
      create: {
        nevoxConversationId: evento.data.conversation.id,
        pacienteId: evento.data.contact.external_id,
        resumo: evento.data.conversation.summary,
        motivo: evento.data.conversation.reason,
      },
      update: {},
    });
  }

  return new Response("ok");
}