Toda entrega de webhook é um HTTP POST com corpo JSON e três headers relevantes.
| Header | Valor | Descrição |
|---|
Content-Type | application/json | Tipo do corpo |
x-kiwify-digital-signature | Base64url (sem padding) | Assinatura EdDSA-Ed25519 |
x-kiwify-timestamp | Unix ms (ex: 1705423200000) | Timestamp usado na mensagem assinada |
Exemplo:
Obter a chave pública
Use GET /v1/webhooks-keys para listar chaves públicas. Use a chave com is_active: true.
Cache a chave por até 24 horas e atualize periodicamente.
Processo de verificação
Passo 1: Validar timestamp
Rejeite entregas com timestamp fora de uma janela de 5 minutos do horário atual (proteção contra replay).
Passo 2: Reconstruir a mensagem assinada
Formato PoP (mesmo padrão da autenticação da API, com diferença no uri):
| Componente | Descrição |
|---|
url_path | Somente o path da URL registrada (ex: /webhooks/kiwibank), não a URL completa |
POST | Método HTTP (sempre POST) |
raw_body | Corpo JSON exatamente como recebido — não re-serialize |
timestamp | Valor do header x-kiwify-timestamp |
Passo 3: Verificar a assinatura
- Calcule SHA-256 dos bytes UTF-8 da mensagem
- Decodifique
x-kiwify-digital-signature de base64url (sem padding)
- Verifique com EdDSA-Ed25519 usando a chave pública ativa
A assinatura de webhook usa Ed25519 prehashed (SHA-256 da mensagem antes da verificação). Isso difere ligeiramente de alguns exemplos genéricos de Ed25519 que assinam a mensagem diretamente.
Exemplo (Python)
Erros comuns
- Usar a URL completa em vez de apenas o path na mensagem assinada
- Re-serializar o JSON (espaços/ordem de chaves alteram a assinatura)
- Verificar Ed25519 diretamente sobre a mensagem sem SHA-256 prévio