Como integrar Transferência Humana via Webhook Genérico (HMAC + API REST)

Categoria: Integrações

A plataforma Webhook Genérico (HMAC) é a opção pra integrar a Transferência Humana com qualquer sistema que não tenha adapter dedicado: Crisp, Zendesk, Freshdesk, Intercom, n8n, dashboards internos, etc.

Diferente do Chatwoot (que tem integração bidirecional pronta), no Generic Webhook você implementa as duas pontas:



  • Prototipe.ai → tua plataforma: recebes eventos via webhook HTTP POST assinado com HMAC-SHA256.

  • Tua plataforma → Prototipe.ai: chamas nossa API REST com Bearer token pra pausar a IA, mandar mensagens do operador e devolver controle.

Quando usar Generic Webhook vs Chatwoot



  • Use Chatwoot se você quer uma solução pronta de atendimento humano (UI de tickets, agentes, SLA, relatórios) e não quer escrever código.

  • Use Generic Webhook se você já tem uma plataforma de atendimento, um dashboard interno ou quer orquestrar via n8n/Zapier. Você controla a UX do operador no teu lado.


Visão geral do ciclo



  1. A IA decide transferir → Prototipe.ai pausa a conversa e dispara webhook conversation.handed_off pra teu endpoint.

  2. Tua plataforma recebe o evento, valida o HMAC, abre um ticket/notifica o operador.

  3. Enquanto a IA estiver pausada, cada mensagem nova do usuário dispara message.created no teu endpoint.

  4. O operador humano responde no teu lado → você chama POST /agent_messages na nossa API → a mensagem chega no canal do usuário (WhatsApp/widget).

  5. Quando o operador encerrar, você chama POST /resume → IA volta a responder. Prototipe.ai dispara conversation.resumed de volta pra confirmar.


Etapa 1 — Crie a API Key (autenticação inbound)

Pra tua plataforma chamar nossa API, precisa de uma API Key com permissão handoff.



  1. No Prototipe.ai, abre Configurações do Time → API Keys.

  2. Clica Nova API Key.

  3. Preenche:

    • Nome: Integração HITL — <tua plataforma>

    • Permissões: marca handoff (obrigatório).

    • Acesso a projetos: seleciona o projeto onde a tool 🤝 vai rodar.



  4. Salva e copia o token retornado — ele só aparece uma vez.

⚠️ Importante: API Keys criadas pela UI são team-scoped (têm acesso a vários projetos do time). Por isso, todas as chamadas precisam informar ?slug=<slug-do-projeto> na query string. O slug aparece na URL do projeto.

❌ Não funciona com Generic Webhook:



  • API Keys flow-scoped (atreladas a um automation_flow específico) — bloqueadas pra prevenir escalada de escopo. Use team-scoped ou project-scoped.


Etapa 2 — Cria a tool 🤝 Transf. para Humano


  1. No step do agente, clica Adicionar Nova Ferramenta.

  2. Seleciona o card 🤝 Transf. para Humano e clica Continuar.

  3. No form:

    • Plataforma: Webhook Genérico (HMAC)

    • URL do webhook externo: a URL do teu endpoint que vai receber os eventos. Ex: https://api.minhaplataforma.com/webhooks/prototipai. Precisa ser HTTPS público (bloqueamos IPs privados/loopback automaticamente por SSRF).

    • Segredo HMAC: cola um segredo ou usa o botão gerar pra criar um aleatório. Guarda esse segredo no teu lado — é com ele que você vai verificar a assinatura.

    • Headers extras (JSON, opcional): se teu endpoint exige headers custom (ex: tenant ID, auth secundária), informa como JSON. Ex: {"X-Tenant-Id": "abc-123"}.



  4. Clica Confirmar.


Etapa 3 — Implementa o endpoint que recebe os webhooks

Eventos enviados pelo Prototipe.ai

Todos chegam como POST no endpoint_url que você configurou, com Content-Type: application/json. O body sempre tem o formato:


{

"event": "conversation.handed_off",
"delivered_at": "2026-05-10T22:30:00-03:00",
"data": { ... }
}

Eventos possíveis:



  • conversation.handed_off — IA pausada. data traz conversation, agent_ref (se houver), reason (se houver).

  • conversation.resumed — controle devolvido pra IA. data traz conversation e previous_agent_ref.

  • message.created — usuário mandou mensagem nova com a IA pausada. data traz conversation e message.

Headers em cada POST



  • X-Prototipai-Event: nome do evento (redundante com body.event, útil pra roteamento rápido).

  • X-Prototipai-Delivery: UUID único da entrega. Use pra idempotência — se receber o mesmo UUID duas vezes, ignora a segunda (acontece em retries).

  • X-Prototipai-Signature: assinatura HMAC-SHA256 do body cru usando o segredo configurado.

Validação da assinatura HMAC

Ruby:


expected = OpenSSL::HMAC.hexdigest('sha256', SEGREDO, request.raw_post)

received = request.headers['X-Prototipai-Signature']
unless Rack::Utils.secure_compare(expected, received.to_s)
head :unauthorized and return
end

Node.js:


const crypto = require('crypto');

const expected = crypto
.createHmac('sha256', SEGREDO)
.update(rawBody) // bytes crus, ANTES de JSON.parse
.digest('hex');
const ok = crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(req.headers['x-prototipai-signature'] || '')
);
if (!ok) return res.status(401).end();

Python:


import hmac, hashlib

expected = hmac.new(SEGREDO.encode(), raw_body, hashlib.sha256).hexdigest()
received = request.headers.get('X-Prototipai-Signature', '')
if not hmac.compare_digest(expected, received):
abort(401)

⚠️ Cuidados:



  • Use o body cru (raw bytes) na assinatura, não o JSON re-serializado.

  • Use comparação constant-time (secure_compare / timingSafeEqual / hmac.compare_digest) pra evitar timing attacks.

  • Responde rápido (< 10s). Demora além disso conta como timeout.

Retry / response codes



  • 2xx → entrega marcada como sucesso.

  • 4xx (exceto 429) → não retentamos. Assumimos bug permanente no teu lado.

  • 5xx ou 429 → retentamos com backoff exponencial via Sidekiq.


Etapa 4 — Implementa a chamada à nossa API REST

Todos os endpoints exigem:



  • Authorization: Bearer <TOKEN> (a API Key da Etapa 1)

  • ?slug=<slug-do-projeto> na query string

POST /api/v1/conversations/:id/handoff — pausa a IA

POST /api/v1/conversations/:id/handoff?slug=<slug>

Authorization: Bearer <TOKEN>
Content-Type: application/json

{
"agent_ref": "maria@empresa.com",
"reason": "cliente irritado, escalando"
}

Ambos os parâmetros (agent_ref, reason) são opcionais. Útil quando você quer disparar o handoff do teu lado (ex: regra de negócio), sem depender da IA decidir.

POST /api/v1/conversations/:id/agent_messages — operador responde

POST /api/v1/conversations/:id/agent_messages?slug=<slug>

Authorization: Bearer <TOKEN>
Content-Type: application/json

{
"content": "Oi, sou a Maria! Como posso ajudar?",
"agent_ref": "maria@empresa.com"
}

content obrigatório (máx 8.000 caracteres). A conversa precisa estar em handoff (chame /handoff antes, ou a IA chamou a tool). A mensagem entra no canal original do usuário automaticamente.

POST /api/v1/conversations/:id/resume — devolve pra IA

POST /api/v1/conversations/:id/resume?slug=<slug>

Authorization: Bearer <TOKEN>

Sem body. A IA volta no mesmo estado em que estava antes do handoff (preservamos o lookahead_state automaticamente).

GET /api/v1/conversations/:id/poll — alternativa ao webhook

GET /api/v1/conversations/:id/poll?slug=<slug>&since=2026-05-05T12:00:00Z

Authorization: Bearer <TOKEN>

Retorna até 100 mensagens criadas após since (ISO8601). Suporta ETag/If-None-Match — se nada mudou desde a última chamada, retorna 304 Not Modified sem pesar o banco. Use polling se receber webhooks for inviável (ex: ambiente sem URL pública).


Exemplo completo (curl, fluxo do operador)

TOKEN='cole-a-key-aqui'

CONV=123
SLUG='meu-projeto-slug'
BASE='https://app.prototipeai.com'

# 1) Pausa a IA (opcional — a tool da IA já faz isso automaticamente)
curl -X POST "$BASE/api/v1/conversations/$CONV/handoff?slug=$SLUG" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{"agent_ref":"maria@x.com","reason":"teste"}'

# 2) Operador responde
curl -X POST "$BASE/api/v1/conversations/$CONV/agent_messages?slug=$SLUG" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{"content":"Oi, sou a Maria!","agent_ref":"maria@x.com"}'

# 3) Devolve pra IA
curl -X POST "$BASE/api/v1/conversations/$CONV/resume?slug=$SLUG" -H "Authorization: Bearer $TOKEN"


Polling vs Webhook — qual usar?



  • Webhook (recomendado): tempo real, baixo custo. Requer endpoint público HTTPS.

  • Polling: simples de implementar, funciona em qualquer ambiente. Latência depende do intervalo (recomenda-se 5-10s). Cuidado com rate limit.

  • Híbrido: webhook como primário, polling como fallback se você suspeitar de eventos perdidos.


Erros comuns



  • 401 Token de autenticação não fornecido → faltou o header Authorization: Bearer ….

  • 401 Token inválido → key errada, expirada ou revogada.

  • 401 Projeto não é válido → faltou o ?slug= na query string (team-scoped key precisa).

  • 403 API key não possui permissão handoff → recria a key marcando a permissão handoff.

  • 403 API key flow-scoped não pode usar handoff → use uma key team-scoped (com slug) ou project-scoped.

  • 409 Conversa já está em handoff → tentou pausar uma conversa já pausada. Trate como idempotente no teu lado.

  • 409 Conversa não está em handoff → tentou mandar mensagem ou resume em conv ainda controlada pela IA.

  • 404 Conversa não encontrada → o :id não existe ou não pertence ao projeto do slug.

  • 400 content é obrigatório / content excede 8000 caracteres → ajusta o payload.


Segurança e boas práticas



  • Nunca exponha a API Key no frontend. Use no backend, com proxy se precisar.

  • Rotaciona o segredo HMAC periodicamente (ou se houver suspeita de vazamento). Edita a tool no Prototipe.ai pra trocar.

  • Idempotência: armazene os X-Prototipai-Delivery recentes (24h) e ignore duplicatas. Retries acontecem em falhas transitórias.

  • Endpoint público: o Prototipe.ai bloqueia URLs apontando pra redes privadas (RFC1918, loopback, link-local) pra prevenir SSRF.

  • Timeouts: tenta responder em < 5s. Limite interno é 10s.

  • HTTPS obrigatório em produção.

‹ Voltar para todos os itens