Webhook de detrator: como agir em minutos quando a nota é ruim
Um webhook de detrator é um aviso HTTP que o serviço de NPS envia ao seu sistema no instante em que um cliente dá nota de 0 a 6. Ele carrega quem respondeu, a nota, o comentário e os atributos do atendimento, assinado para o seu sistema confiar na origem. Com ele, a nota ruim vira um caso aberto com dono e prazo em minutos, em vez de uma linha num relatório no fim do mês.
Este artigo mostra o formato real do evento response.detractor do ProFeedback, como validar a assinatura em poucas linhas de Python e o que fazer com o alerta depois que ele chega.
O que é um webhook de detrator?
NPS (Net Promoter Score) é a métrica de satisfação que nasce de uma única pergunta: de 0 a 10, quanto você recomendaria a empresa a um amigo ou colega? Quem responde 9 ou 10 é promotor, 7 ou 8 é neutro e de 0 a 6 é detrator. O NPS é o percentual de promotores menos o percentual de detratores, e vai de -100 a +100.
Como a escala vai de 0 a 10, a nota de 0 a 6 classifica o cliente como detrator. Um webhook é um POST HTTPS que o serviço faz para um endereço que você cadastrou, assim que o evento acontece. O webhook de detrator junta as duas coisas: no momento em que uma resposta entra nessa faixa, o seu sistema recebe o aviso, sem precisar consultar nada.
A diferença em relação a um painel é o tempo. O painel mostra o detrator quando alguém olha; o webhook avisa quando ele aparece. Para recuperar um cliente insatisfeito, as primeiras horas valem mais do que a semana seguinte.
Como é o payload do evento response.detractor?
No ProFeedback, toda nota dispara response.created e, se for de 0 a 6, também response.detractor. O corpo é JSON com o id do evento, o nome do evento, a data, a marca de sandbox e os dados da resposta:
{
"id": "5a1c9e47-3b2d-4f60-a8e1-7c2f4d9b6e15",
"event": "response.detractor",
"created_at": "2026-10-23T10:00:00-03:00",
"sandbox": false,
"data": {
"id": "0e7d3a52-6c1b-4f7e-8d2a-91b5c4e6f3a0",
"score": 4,
"category": "detractor",
"comment": "Demorou para responder.",
"source": "link",
"attributes": {"atendente": "Ana"},
"customer": {
"id": "8b6f5c1e-2f0a-4d43-9a57-3f1c9a0b7e21",
"external_id": "crm-001",
"name": "Maria Souza"
},
"created_at": "2026-10-23T10:00:00-03:00"
}
}Quatro cabeçalhos acompanham a entrega:
X-ProFeedback-Signature | sha256=<hex>, HMAC-SHA256 do carimbo de tempo mais o corpo. |
X-ProFeedback-Timestamp | Instante do envio em segundos Unix. |
X-ProFeedback-Event | Nome do evento, para rotear sem abrir o JSON. |
X-ProFeedback-Delivery | Id da entrega, para descartar repetição. |
O external_id dentro de customer é o identificador do cliente no seu sistema: é por ele que o alerta encontra o cadastro certo no CRM ou no atendimento.
Como validar a assinatura do webhook em Python?
A assinatura cobre a string "<timestamp>." + corpo bruto, com o segredo do endpoint (mostrado uma vez, ao criar ou rotacionar). Calcule sobre os bytes exatamente como chegaram, sem reserializar o JSON, compare em tempo constante e recuse carimbos antigos:
import hashlib
import hmac
import time
def assinatura_confere(secret: str, corpo_bruto: bytes, timestamp: str, assinatura: str) -> bool:
if abs(time.time() - int(timestamp)) > 300:
return False
esperado = "sha256=" + hmac.new(
secret.encode(), f"{timestamp}.".encode() + corpo_bruto, hashlib.sha256
).hexdigest()
return hmac.compare_digest(esperado, assinatura)Num handler web, leia request.body (ou o equivalente cru do seu framework), valide, responda 2xx em até 10 segundos e processe o evento numa fila. Sem 2xx, a entrega é repetida com espera crescente; guarde o X-ProFeedback-Delivery para não abrir dois casos da mesma resposta. O exemplo conferível, com segredo e assinatura calculados, está na documentação de webhooks.
O que fazer com o alerta de detrator?
Receber o evento é a parte fácil. O que muda o resultado é a rotina que ele dispara. Um roteiro em quatro passos:
- Abrir um caso. O handler cria um ticket, uma tarefa ou uma conversa no sistema que a equipe já usa, com a nota, o comentário, os atributos (atendente, unidade) e o link para o cadastro do cliente.
- Atribuir. O caso vai para uma pessoa, não para uma fila genérica. Os atributos ajudam: a unidade define o gestor, a fila define o supervisor.
- Dar prazo. Um prazo curto para o primeiro contato, de preferência no mesmo dia útil. O caso em atraso escala para o próximo nível.
- Registrar. Causa, ação e resultado ficam anotados no caso. No fim da semana, os casos agrupados por causa viram a lista de melhorias.
O que não fazer: responder com mensagem automática genérica ("sentimos muito, estamos melhorando") e encerrar. O cliente que deu nota 3 e recebeu um texto pronto fica mais detrator do que estava.
Sobre o passo seguinte, como conduzir o contato e o que perguntar, veja Pesquisa de satisfação após o atendimento.
Como fica um handler mínimo?
Um esboço em Python, com qualquer framework web, do que o endpoint faz entre receber e responder:
def receber_webhook(request):
if not assinatura_confere(
SEGREDO, request.body, request.headers["X-ProFeedback-Timestamp"],
request.headers["X-ProFeedback-Signature"],
):
return resposta(401)
if ja_processado(request.headers["X-ProFeedback-Delivery"]):
return resposta(200)
if request.headers["X-ProFeedback-Event"] == "response.detractor":
enfileirar(abrir_caso_de_recuperacao, json.loads(request.body))
return resposta(200)A função abrir_caso_de_recuperacao roda fora da requisição, cria o caso no seu sistema a partir do external_id e atribui o dono. O resto é rotina de atendimento.
Como saber se o alerta está funcionando?
Três medidas simples, todas calculáveis a partir dos casos abertos:
- Tempo até o primeiro contato, do evento ao registro do contato humano.
- Percentual de detratores contatados no prazo.
- Nota seguinte do mesmo cliente, quando ele volta a responder depois da janela.
O NPS do período, por segmento, continua sendo a medida de fundo; a conta e as faixas estão em Como calcular o NPS. O webhook não melhora o NPS por si: ele dá tempo para a equipe melhorar.
Perguntas frequentes
O webhook de detrator substitui o response.created?
Não. Toda nota dispara response.created; a nota de 0 a 6 dispara também response.detractor. Assine só o segundo se quiser apenas os alertas.
O que acontece se o meu endpoint estiver fora do ar?
A entrega é repetida com espera crescente por algumas tentativas. Use o id da entrega para não processar a mesma resposta duas vezes quando ela chegar.
Notas do sandbox também disparam o webhook?
Sim, com o campo sandbox igual a true no corpo. Use isso para testar o handler sem misturar com produção.