Webhooks
Webhooks — visão geral
Webhooks avisam o seu servidor no instante em que algo acontece — sem você ficar consultando a API. É a forma recomendada de saber que uma cobrança foi paga.
Configuração
Cadastre a URL do seu endpoint no painel, em Desenvolvedor, e escolha os eventos que quer receber. Cada endpoint ganha um secret usado para assinar as entregas.
Eventos disponíveis
charge.paid— a cobrança foi paga.datacontém a cobrança comstatus: "paid"epaid_atpreenchido.charge.expired— a cobrança venceu sem pagamento.data.statusé"expired".
Formato do evento
O corpo é um JSON com id do evento (prefixo evt_), o event, o created_at e data — que é exatamente o objeto charge serializado.
{
"id": "evt_9f8e7d6c5b4a3210fedcba98",
"event": "charge.paid",
"created_at": "2026-07-04T12:05:11.000Z",
"data": {
"id": "clx7a1b2c3d4e5f6g7h8i9j0k",
"object": "charge",
"status": "paid",
"amount": 4990,
"fee": 74,
"net": 4916,
"currency": "BRL",
"description": "Plano Pro — mensal",
"payment_method": "pix",
"customer": { "name": "Ana Souza", "email": "ana@exemplo.com", "document": null },
"pix": {
"txid": "VRD7A1B2C3D4E5F6G7H8I9J0",
"brcode": "00020126360014BR.GOV.BCB.PIX...6304AB1F",
"qrcode_url": "https://api.verdin.com.br/api/v1/charges/clx7a1b2c3d4e5f6g7h8i9j0k/qrcode"
},
"payment_url": "https://api.verdin.com.br/pay/clx7a1b2c3d4e5f6g7h8i9j0k",
"metadata": { "pedido_id": "A-1042" },
"paid_at": "2026-07-04T12:05:11.000Z",
"expires_at": "2026-07-04T13:00:00.000Z",
"created_at": "2026-07-04T12:00:00.000Z"
}
}Recebendo o evento
Responda com um status 2xx assim que receber. Deixe o processamento pesado para depois (fila) — a Verdin espera no máximo 8 segundos por resposta.
import express from "express";
const app = express();
// receba o corpo BRUTO — necessário para verificar a assinatura
app.post("/webhooks/verdin", express.raw({ type: "application/json" }), (req, res) => {
// 1. verifique a assinatura (veja "Verificando a assinatura")
// 2. responda 2xx o mais rápido possível
const evt = JSON.parse(req.body.toString("utf8"));
switch (evt.event) {
case "charge.paid":
// libere o produto para evt.data.id — de forma idempotente
break;
case "charge.expired":
// marque o pedido como expirado, se preciso
break;
}
res.sendStatus(200);
});Entrega e tentativas
Se o seu endpoint não responder 2xx (erro, timeout ou rede indisponível), a Verdin tenta de novo. São 3 tentativas com os intervalos:
- 1ª tentativa: imediata (0s)
- 2ª tentativa: após 5s
- 3ª tentativa: após mais 25s
Cada tentativa tem timeout de 8 segundos.
Trate eventos de forma idempotente
data.id (id da cobrança) — ou o id do evento — como chave de idempotência: se já processou aquele pagamento, responda 2xx e não processe de novo.