Webhooks entregam notificações em tempo real quando recursos mudam. Em vez de fazer polling na API, registre uma URL e receba requisições POST sempre que eventos ocorrerem. A API Dinie segue a especificação Standard Webhooks.
Registre uma URL para receber eventos de webhook. Você pode se inscrever em eventos específicos ou receber todos.
const endpoint = await dinie.webhooks.endpoints.create({
url: "https://yourapp.example.com/webhooks/dinie",
events: ["customer.active", "credit_offer.available", "loan.*"],
description: "Production webhook",
});
// Armazene endpoint.secret com segurança -- ele é exibido apenas uma vez.
console.log(endpoint.secret); // "whsec_xxxxx..."O endpoint.secret retornado é necessário para verificar assinaturas -- armazene-o imediatamente no seu gerenciador de secrets.
Warning: O
secreté retornado apenas na criação e rotação. Armazene-o imediatamente no seu gerenciador de secrets. Você precisa dele para verificar assinaturas de webhook.
| Operação | Método | Endpoint |
|---|---|---|
| Listar todos | GET | /v3/webhooks/endpoints |
| Obter um | GET | /v3/webhooks/endpoints/{id} |
| Atualizar | PATCH | /v3/webhooks/endpoints/{id} |
| Excluir | DELETE | /v3/webhooks/endpoints/{id} |
Você pode desabilitar um endpoint sem excluí-lo definindo status: "disabled" via PATCH.
Cada entrega é uma requisição POST com headers de autenticação (webhook-id, webhook-timestamp, webhook-signature). Use o SDK para verificar e parsear o evento:
import express from "express";
import Dinie from "dinie";
const app = express();
const dinie = new Dinie({
clientId: process.env.DINIE_CLIENT_ID,
clientSecret: process.env.DINIE_CLIENT_SECRET,
webhookSecret: process.env.DINIE_WEBHOOK_SECRET,
});
app.post("/webhooks/dinie", express.raw({ type: "application/json" }), (req, res) => {
const event = dinie.webhooks.unwrap(req.body.toString(), req.headers);
switch (event.type) {
case "customer.active":
console.log(`Cliente ${event.data.id} aprovado`);
break;
case "loan.active":
console.log(`Empréstimo ${event.data.id} desembolsado`);
break;
}
res.sendStatus(200);
});O webhook_secret é configurado no construtor do client. O método unwrap usa esse secret para verificar a assinatura HMAC-SHA256, validar o timestamp (proteção contra replay de 5 minutos) e retornar o evento tipado. Se a verificação falhar, uma exceção é lançada -- retorne 400 nesse caso.
Se você não estiver usando os SDKs, implemente a verificação manualmente seguindo a especificação Standard Webhooks:
- Construa o conteúdo assinado:
{webhook-id}.{webhook-timestamp}.{raw_body} - Decodifique o secret: remova o prefixo
whsec_e decodifique o restante em base64 - Calcule o HMAC-SHA256 do conteúdo assinado usando o secret decodificado
- Codifique o resultado em base64 e compare com cada assinatura no header
webhook-signature - Rejeite se o
webhook-timestampfor mais antigo que 5 minutos (proteção contra replay)
Consulte a especificação Standard Webhooks para detalhes completos sobre os headers e formato de assinatura.
Seu endpoint de webhook deve responder em até 15 segundos. Apenas o código de status HTTP importa -- o corpo da resposta é ignorado.
| Status | Comportamento | Descrição |
|---|---|---|
| 2xx (200-299) | Sucesso | Entrega confirmada. Mais comuns: 200 OK ou 204 No Content. |
| 3xx (300-399) | Falha + retentativa | Redirecionamentos NÃO são seguidos. Atualize a URL do endpoint via API. |
| 410 Gone | Endpoint desativado | Dinie desativa permanentemente o endpoint e para de enviar eventos. Use para cancelar a inscrição sem chamar a API. |
| 429 Too Many Requests | Throttle | Dinie reduz a taxa de envio e reagenda com backoff. |
| 4xx (outros) | Falha + retentativa | Qualquer outro erro de cliente dispara retentativas. |
| 502 Bad Gateway | Throttle | Dinie reduz a taxa de envio antes de retentar. |
| 504 Gateway Timeout | Throttle | Mesmo comportamento do 502. |
| 5xx (outros) | Falha + retentativa | Qualquer outro erro de servidor dispara retentativas. |
| Timeout (sem resposta em 15s) | Falha + retentativa | Conexão encerrada, entrega tratada como falha. |
Entregas com falha são reenviadas com exponential backoff. Jitter aleatório é adicionado a cada intervalo para evitar thundering herd.
| Tentativa | Intervalo |
|---|---|
| 1 | Imediato |
| 2 | 5 segundos |
| 3 | 5 minutos |
| 4 | 30 minutos |
| 5 | 2 horas |
| 6 | 5 horas |
| 7 | 10 horas |
| 8 | 14 horas |
| 9 | 20 horas |
| 10 | 24 horas |
Após 10 tentativas com falha (~3 dias), o evento é marcado como failed. Eventos com falha podem ser reenviados manualmente. Falhas repetidas em múltiplos eventos desabilitarão automaticamente o endpoint de webhook.
Info: Use o header
webhook-idcomo chave de idempotência para deduplicar entregas do seu lado. O mesmo evento pode ser entregue mais de uma vez durante as retentativas.
Rotacione o secret de assinatura do seu webhook sem downtime:
curl -X POST https://sandbox.api.dinie.com.br/v3/webhooks/endpoints/we_550e8400.../secret/rotate \
-H "Authorization: Bearer dinie_at_..." \
-H "Content-Type: application/json" \
-d '{ "expire_current_in": 3600 }'Durante o período de transição (expire_current_in, padrão 1 hora, máximo 24 horas), ambos os secrets -- antigo e novo -- estão ativos. O header webhook-signature contém assinaturas para ambos os secrets, então seu código de verificação vai corresponder a qualquer um deles.
- Chame
POST /v3/webhooks/endpoints/{id}/secret/rotatecom um período de transição - Atualize sua aplicação com o novo secret da resposta
- Faça o deploy -- durante o período de transição, ambos os secrets funcionam
- Após o período de transição, apenas o novo secret é usado
- Sempre verifique assinaturas -- nunca confie nos payloads de webhook sem verificação
- Responda rapidamente -- retorne um status
200em até 15 segundos. Processe o evento de forma assíncrona se necessário. - Implemente idempotência -- use o
webhook-idpara deduplicar. Seu handler deve processar o mesmo evento duas vezes com segurança. - Use HTTPS -- URLs de webhook devem usar HTTPS. Endpoints HTTP são rejeitados.
- Registre as entregas -- armazene o
webhook-ide owebhook-timestamppara debugging e trilhas de auditoria.
Todos os payloads seguem o mesmo envelope:
{
"type": "resource.action",
"timestamp": 1709546400,
"data": { ... }
}- Eventos específicos:
["customer.active", "loan.created"] - Todos de um recurso:
["loan.*"] - Todos os eventos: omita o campo
eventsou passe um array vazio
| Recurso | Evento | Descrição |
|---|---|---|
| Cliente | customer.created | Novo cliente cadastrado |
customer.under_review | KYC enviado, análise iniciada | |
customer.kyc_updated | Status de requisito KYC alterado | |
customer.active | Totalmente verificado, elegível para crédito | |
customer.denied | Permanentemente bloqueado | |
| Oferta de Crédito | credit_offer.available | Nova oferta criada para o cliente |
credit_offer.expired | Oferta ultrapassou a data de validade | |
| Empréstimo | loan.created | Empréstimo criado com CCB gerada sincronamente, signing_url disponível |
loan.signature_received | Assinatura individual recebida | |
loan.processing | Todas assinaturas coletadas, desembolso em andamento | |
loan.active | Fundos desembolsados, pagamento iniciado | |
loan.payment_received | Parcela recebida | |
loan.finished | Empréstimo totalmente quitado | |
loan.cancelled | Empréstimo cancelado | |
loan.error | Erro no processamento ou desembolso |