Guias
Webhooks
Quando uma cobrança chega a um estado final, enviamos um POST assinado para a URL configurada no painel.
Configuração
- No painel, em Integração → Webhook, informe uma URL HTTPS pública. Uma URL por conta.
- Copie o segredo de assinatura (
whsec_…). Ele pode ser rotacionado a qualquer momento. - Use Enviar teste para receber um evento
ping. - O painel mostra o log de entregas com status, resposta e botão para reenviar.
Eventos
charge.approvedeventocharge.declinedeventocharge.canceledeventocharge.failedeventocharge.expiredeventopingeventoRequisição
POST /sua/url HTTP/1.1
Content-Type: application/json
User-Agent: Integrator-Webhooks/1.0
X-Integrator-Event: charge.approved
X-Integrator-Delivery: a3b1c0de-1111-4f00-9c3a-5e2b1d0f0a99
X-Integrator-Signature: t=1792000842,v1=5f2b0c…{
"id": "a3b1c0de-1111-4f00-9c3a-5e2b1d0f0a99",
"type": "charge.approved",
"created_at": "2026-10-13T18:20:42.120Z",
"data": {
"id": "0b6f0f7e-5a8f-4f1e-9a52-2b7d3c1e9d10",
"object": "charge",
"terminal_id": "6f1c2b8e-2f4a-4c55-9a59-6a1d5b1f0c3e",
"amount": 4590,
"currency": "BRL",
"method": "credit",
"status": "approved",
"description": "Pedido #1234",
"external_reference": "pedido-1234",
"fee": 46,
"acquirer": null,
"nsu": "000123456",
"authorization_code": "A1B2C3",
"card_brand": "VISA",
"acquirer_code": "00",
"failure_reason": null,
"failure_message": null,
"created_at": "2026-10-13T18:20:11.204Z",
"processing_at": "2026-10-13T18:20:11.851Z",
"completed_at": "2026-10-13T18:20:42.017Z",
"expires_at": "2026-10-13T18:22:11.204Z"
}
}O campo id identifica o evento e é o mesmo em todas as tentativas. Use-o para ignorar entregas repetidas.
Validar a assinatura
O cabeçalho X-Integrator-Signature traz t (Unix timestamp em segundos) e v1, que é o HMAC-SHA256 em hexadecimal de t + "." + corpo_bruto usando o seu segredo. Calcule sobre os bytes exatos recebidos, antes de fazer o parse do JSON, e rejeite timestamps com mais de 5 minutos de diferença.
import crypto from "node:crypto";
import express from "express";
const app = express();
app.post("/webhooks/integrator", express.raw({ type: "application/json" }), (req, res) => {
const header = req.get("X-Integrator-Signature") ?? "";
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const expected = crypto
.createHmac("sha256", process.env.INTEGRATOR_WEBHOOK_SECRET)
.update(`${parts.t}.${req.body}`)
.digest("hex");
const valid =
parts.v1?.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
const recent = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
if (!valid || !recent) return res.status(400).send("assinatura inválida");
const event = JSON.parse(req.body.toString("utf8"));
if (event.type === "charge.approved") {
marcarPedidoComoPago(event.data.external_reference, event.data.nsu);
}
res.sendStatus(200);
});Como responder
- Responda
2xxem até 10 segundos. Qualquer outro status, timeout ou erro de conexão conta como falha. - Faça o mínimo na requisição (gravar o evento numa fila, por exemplo) e processe depois.
- Redirecionamentos não são seguidos.
Novas tentativas
Se a entrega falhar, tentamos de novo com espera crescente:
Depois da sétima falha a entrega fica como failed no painel, de onde você pode reenviar. Eventos podem chegar fora de ordem e mais de uma vez: use o status atual da cobrança, ou consulte GET /v1/charges/{id}, como fonte da verdade.
Sem webhook configurado