Skip to main content
O seu endpoint de webhook é público na internet. Qualquer pessoa que descubra a URL pode enviar um POST fingindo ser a Ephra — e, se o seu sistema liberar o pedido só porque chegou um JSON com "status": "paid", essa pessoa acabou de comprar de graça. Esta página descreve o que a Ephra envia hoje, o que ela não envia, e como fechar a porta com o que existe.

O que chega no seu endpoint

Uma entrega real da Ephra é um POST com estes headers:
Não existe header de assinatura hoje. A entrega não carrega nem Click-Signature, nem X-Ephra-Signature, nem qualquer HMAC. O campo signatureSecretMasked, que aparece quando você cadastra um webhook, existe no cadastro mas não assina as entregas.Se você escrever um validador de HMAC agora, ele vai rejeitar todas as notificações legítimas. Pior: um validador que “passa quando o header está ausente” não valida nada e dá uma falsa sensação de segurança.
User-Agent: Ephra Finance identifica o remetente, mas é um header comum — qualquer cliente HTTP consegue forjá-lo. Ele serve para filtrar ruído em log, não para autenticar.

O signatureSecret não é um segredo protegido

Além de não assinar nada, ele é legível por qualquer chave de API da sua empresa. GET /v1/webhook, a rota transacional legada, devolve o valor inteiro em texto puro a cada listagem — dos mesmos webhooks que GET /v1/webhooks mostra mascarados. As duas rotas leem as mesmas linhas; só o contrato de resposta muda.
Dois cortes nossos nesse corpo, para encurtar a página: a lista real trazia os três webhooks contados em totalCount, e o […] esconde o miolo do segredo, que tem 128 caracteres. O signatureSecretMasked: "••••••••146b" que /v1/webhooks devolve para esse mesmo webhook são os 4 últimos caracteres desse valor. Duas conclusões para o seu desenho de segurança:
  • A máscara é conveniência de tela, não proteção. Ela evita que o segredo apareça inteiro num print do painel; ela não impede ninguém de lê-lo.
  • Não use o signatureSecret como senha compartilhada — nem como token no caminho da URL, nem num header que o seu endpoint confira. Quem tem uma chave de API da sua empresa já o conhece. Gere o seu próprio segredo para a URL, como no passo a seguir, e guarde-o só do seu lado.

Como proteger o endpoint agora

Três camadas, em ordem de esforço.
1

Use uma URL que ninguém adivinha

Cadastre o webhook num caminho com um segredo longo e aleatório dentro:
Rejeite com 404 qualquer requisição em outro caminho. É o mesmo princípio de um link de convite: o segredo está na URL. Trate essa URL como senha — fora do log de acesso, fora do controle de versão, fora do chat da equipe.
2

Restrinja a origem, se puder

Se o seu ERP fica atrás de um WAF ou de um balanceador, libere o caminho do webhook apenas para as faixas de saída da Ephra e recuse o resto. Peça as faixas atuais a suporte@ephra.io.
3

Confirme pela API antes de liberar

Esta é a camada que realmente decide. Não confie no corpo do POST: use-o apenas como gatilho e pergunte à API qual é o estado de verdade.
Libere o pedido só quando a própria API disser "status": "paid", e só quando o valor bater com o que você cobrou. Um POST forjado não sobrevive a essa conferência, porque o atacante não tem a sua chave de API.
Quando a assinatura HMAC entrar no ar, esta página passa a descrever o header e o algoritmo, e a conferência pela API continua sendo a recomendação para valores altos. Nenhuma das três camadas acima deixa de valer.

Idempotência

A mesma entrega pode chegar mais de uma vez: uma retentativa depois de um timeout do seu lado, um reenvio manual em POST /v1/webhook-deliveries/{deliveryId}/retry, ou uma resposta sua que a Ephra não conseguiu ler. Cada evento traz um id único (um UUID). Guarde-o e descarte duplicatas antes de processar. Sem isso, um cliente recebe dois acessos, ou o seu financeiro contabiliza a mesma venda duas vezes.
Grave o id na mesma transação de banco em que você libera o pedido. Gravar depois abre uma janela em que duas entregas simultâneas passam pelas duas verificações antes de qualquer uma inserir.

Retentativas

Se o seu endpoint não responder 2xx, a Ephra reenvia automaticamente. Uma entrega criada e nunca enviada (órfã) é recuperada depois de 2 minutos pelo mesmo mecanismo — nenhuma fica para trás dentro da janela.
Sessenta segundos de timeout não é um convite para processar durante sessenta segundos. Responda 200 assim que gravar o evento e faça o trabalho pesado numa fila. Um endpoint lento vira um endpoint com retentativas, e retentativas viram duplicatas se a idempotência estiver fraca.
Acompanhe o que falhou em GET /v1/webhook-deliveries: o campo retries diz em que tentativa a entrega está, e status: "error" marca as que ainda vão voltar.

Checklist

  • HTTPS sempre — a Ephra testa a URL no cadastro, mas não impede HTTP.
  • Segredo longo e aleatório no caminho da URL; 404 em qualquer outro.
  • Descartar scope: "test" antes de liberar qualquer coisa.
  • Idempotência pelo id do evento, gravado na mesma transação de banco.
  • Confirmar status e valor em GET /v1/transactions/{transactionId} antes de liberar.
  • Responder 2xx rápido; processar em segundo plano.
  • Não validar o payload com schema rígido — campos novos não devem derrubar o seu endpoint.
  • Não construir validação de assinatura enquanto não houver header para validar.
  • Não usar o signatureSecret da Ephra como segredo do seu endpoint — ele sai em texto puro em GET /v1/webhook.