Tema
Webhooks
Em vez de você consultar o Edusati para saber se algo mudou, o Edusati chama o seu servidor quando acontece: uma resposta foi finalizada, um convite foi aberto, uma meta foi atingida.
Resposta finalizada
│
▼
Edusati ──── POST assinado ────► https://seu-servidor.com/edusati
└─ você responde 2xxConfigurar um endpoint
No painel, em Integrações › Webhooks:
- Nova assinatura
- Informe a URL — precisa ser
https://e acessível pela internet - Escolha os eventos que interessam (ou
*para todos) - Salve e revele o segredo — é com ele que você valida a assinatura
- Clique em Testar: dispara um evento
pinge você vê o resultado no log
O log de entregas fica na mesma tela: status, código HTTP de resposta, tentativas e o payload enviado.
O formato
Todo POST tem o mesmo envelope. Só type e data mudam:
json
{
"id": "8f4c1e2a-…",
"type": "response.created",
"version": 1,
"occurredAt": "2026-07-28T13:45:12.001Z",
"companyId": "c0ffee00-…",
"data": {
"formId": "f1a2b3c4-…",
"formName": "Pesquisa de satisfação — Julho",
"formSlug": "satisfacao-julho",
"responseId": "r1a2b3c4-…",
"inviteId": null,
"completedAt": "2026-07-28T13:45:11.980Z",
"respondent": { "name": "Ana Prado", "email": "ana@exemplo.com", "phone": null, "externalId": "MAT-4471" },
"answers": { "q_nps": 9, "q_comentario": "Atendimento muito rápido" }
}
}| Campo | O que é |
|---|---|
id | Id do evento. É por ele que você deduplica |
type | Um dos tipos do catálogo |
version | Versão do payload — ver compatibilidade |
occurredAt | Quando o fato aconteceu (ISO 8601 UTC) |
companyId | Empresa de origem |
data | Conteúdo específico do tipo |
As respostas viajam dentro do evento de propósito: é o que dispensa uma segunda chamada só para descobrir o que a pessoa respondeu.
As três regras
1. Valide a assinatura
Sua URL é pública; qualquer um pode fazer POST nela. A assinatura é o que prova que a requisição veio de nós. Detalhes e código pronto em Assinatura.
Nunca confie no conteúdo de um POST cuja assinatura você não verificou.
2. Deduplique pelo id
A entrega é pelo menos uma vez. Uma falha de rede entre o seu 200 e o nosso registro faz a mesma entrega ser tentada de novo — com o mesmo id de evento.
js
if (await jaProcessei(evento.id)) return res.sendStatus(200);
await processar(evento);
await marcarProcessado(evento.id);Guarde os ids por pelo menos alguns dias. Se o seu processamento já é idempotente por natureza (um UPSERT pela chave do registro), isso basta e você não precisa da tabela de ids.
X-Edusati-Delivery não serve para deduplicar
Esse header identifica a tentativa, e muda a cada retry. O id do corpo é o que se repete — é ele que você guarda.
3. Responda rápido
Responda 2xx assim que receber e processe depois, fora do ciclo da requisição. O timeout é de 10 segundos; passar disso conta como falha e agenda retry, mesmo que você tenha processado tudo direitinho.
js
app.post('/edusati', async (req, res) => {
if (!assinaturaValida(req)) return res.sendStatus(401);
await fila.enfileirar(req.body); // processamento pesado sai daqui
res.sendStatus(200); // confirma antes de trabalhar
});O que acontece quando falha
Resumo — o detalhe está em Entrega e falhas:
- Qualquer 2xx confirma o recebimento
- Qualquer outra resposta, timeout, erro de DNS ou de TLS agenda retry com backoff (30s, 1min, 2min… até 1h), num total de 5 tentativas
- 410 Gone desativa o endpoint imediatamente — use quando ele acabou de vez
- 20 falhas consecutivas desativam o endpoint automaticamente
- Redirecionamentos não são seguidos: mudou de endereço, edite o endpoint
Compatibilidade
Vamos acrescentar campos em data ao longo do tempo, e isso não bumpa a version. Seu consumidor precisa tolerar campos que não conhece — não valide o payload com um schema estrito que rejeite propriedades extras.
Mudança de semântica de um campo existente, essa sim, sobe a version. Você não faz deploy junto com a gente, então esse é o contrato: aditivo é silencioso, quebra é versionada.
Da mesma forma, tipos novos de evento podem aparecer. Se você assinou *, ignore com elegância o que não reconhece:
js
const handler = handlers[evento.type];
if (!handler) return res.sendStatus(200); // 200, não 400 — não é erro delePrivacidade
O payload de response.created inclui as respostas e os dados do respondente — ou seja, dados pessoais saem do Edusati e entram na sua infraestrutura. Trate-os com o mesmo cuidado que trataria dados coletados por você: TLS, acesso restrito, prazo de descarte.
Do nosso lado, o log de entregas (com o payload) tem prazo curto de retenção.
Próximos passos
- Assinatura — validar em Node, Python e PHP
- Catálogo de eventos — os 10 tipos e o
datade cada um - Entrega e falhas — retry, desativação, reenvio manual
- Receptor de exemplo — servidor Express pronto para copiar