ProFeedback
Webhooks

Webhook de detrator e eventos assinados

O ProFeedback avisa o seu sistema por POST HTTPS a cada evento escolhido. Cadastre os endpoints no painel, em Integrações, ou por POST /v1/webhooks.

Eventos

EventoQuando
response.createdToda nota registrada (API, link de pesquisa ou painel).
response.detractorNota de 0 a 6, junto com o response.created. Use para tratar o cliente insatisfeito.
customer.createdCliente novo (API, importação CSV, painel ou nota com external_id novo).

Corpo

JSON com id do evento, event, created_at, sandbox (true quando veio de chave pf_test_) e data:

{"created_at": "2026-10-23T10:00:00-03:00", "data": {"attributes": {"atendente": "Ana"}, "category": "detractor", "comment": "Demorou para responder.", "created_at": "2026-10-23T10:00:00-03:00", "customer": {"external_id": "crm-001", "id": "8b6f5c1e-2f0a-4d43-9a57-3f1c9a0b7e21", "name": "Maria Souza"}, "id": "0e7d3a52-6c1b-4f7e-8d2a-91b5c4e6f3a0", "score": 4, "source": "link"}, "event": "response.detractor", "id": "5a1c9e47-3b2d-4f60-a8e1-7c2f4d9b6e15", "sandbox": false}

Cabeçalhos

X-ProFeedback-Signaturesha256=<hex>: HMAC-SHA256 do carimbo de tempo mais o corpo.
X-ProFeedback-TimestampInstante do envio em segundos Unix.
X-ProFeedback-EventNome do evento.
X-ProFeedback-DeliveryId da entrega (use para descartar repetição).

Validar a assinatura

O segredo do endpoint aparece uma vez, ao criar ou rotacionar. A assinatura cobre "<timestamp>." + corpo_bruto: calcule sobre o corpo exatamente como chegou (sem reserializar o JSON), compare em tempo constante e recuse carimbos com mais de 5 minutos.

Python

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)

Node.js

const crypto = require("crypto");

function assinaturaConfere(secret, corpoBruto, timestamp, assinatura) {
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
  const esperado = "sha256=" + crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.`)
    .update(corpoBruto) // Buffer do corpo cru, ex.: express.raw({ type: "application/json" })
    .digest("hex");
  const a = Buffer.from(esperado);
  const b = Buffer.from(assinatura || "");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Exemplo conferível

Com o segredo whsec_exemplo_nao_use_em_producao, X-ProFeedback-Timestamp: 1792764000 e o corpo acima (exatamente esses bytes), a assinatura é:

X-ProFeedback-Signature: sha256=33f33fd0e544da4546a177aa1d767bedfa8b5ff05ca03c6bc299068ebf8697c2

Entrega e retentativas

  • Responda 2xx em até 10 segundos; processe o evento depois, em fila.
  • Sem 2xx, até 5 tentativas com espera crescente (a partir de 60 segundos, teto de 1 hora).
  • A mesma entrega pode chegar mais de uma vez: use o X-ProFeedback-Delivery ou o id do evento para não processar duas vezes.
  • Destinos internos (IP privado, localhost) são recusados.
  • Entregas e reenvio: painel, em Integrações, ou GET /v1/webhooks/deliveries e POST /v1/webhooks/deliveries/{id}/resend. Teste com POST /v1/webhooks/{id}/test e troque o segredo com POST /v1/webhooks/{id}/rotate-secret.