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
- A IA decide transferir → Prototipe.ai pausa a conversa e dispara webhook
conversation.handed_offpra teu endpoint. - Tua plataforma recebe o evento, valida o HMAC, abre um ticket/notifica o operador.
- Enquanto a IA estiver pausada, cada mensagem nova do usuário dispara
message.createdno teu endpoint. - O operador humano responde no teu lado → você chama
POST /agent_messagesna nossa API → a mensagem chega no canal do usuário (WhatsApp/widget). - Quando o operador encerrar, você chama
POST /resume→ IA volta a responder. Prototipe.ai disparaconversation.resumedde 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.
- No Prototipe.ai, abre Configurações do Time → API Keys.
- Clica Nova API Key.
- Preenche:
- Nome:
Integração HITL — <tua plataforma> - Permissões: marca
handoff(obrigatório). - Acesso a projetos: seleciona o projeto onde a tool 🤝 vai rodar.
- Nome:
- 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
- No step do agente, clica Adicionar Nova Ferramenta.
- Seleciona o card 🤝 Transf. para Humano e clica Continuar.
- 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"}.
- Plataforma:
- 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.datatrazconversation,agent_ref(se houver),reason(se houver).conversation.resumed— controle devolvido pra IA.datatrazconversationeprevious_agent_ref.message.created— usuário mandou mensagem nova com a IA pausada.datatrazconversationemessage.
Headers em cada POST
X-Prototipai-Event: nome do evento (redundante combody.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.5xxou429→ 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 headerAuthorization: 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ãohandoff.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:idnão existe ou não pertence ao projeto doslug.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-Deliveryrecentes (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.