Webhooks
Webhooks avisam o seu sistema em tempo real quando algo acontece, por exemplo, quando um pagamento entra. Assim você não precisa ficar consultando a API em loop.
Entrega garantida
Como funciona
Cadastre uma URL em Painel → API → Webhooks. Quando um evento ocorre, enviamos um POST com o corpo do evento para essa URL. Responda com 2xx em poucos segundos para confirmar o recebimento, qualquer outra resposta (ou timeout) agenda uma nova tentativa.
Eventos disponíveis
| Parâmetro | Tipo | Descrição |
|---|---|---|
pixQrCode.paid | evento | Uma cobrança PIX foi paga. |
pixQrCode.failed | evento | Uma cobrança PIX expirou ou falhou (não será paga). |
boleto.paid | evento | Um boleto foi pago (pela linha digitável ou pelo Pix do título). |
billing.paid | evento | Um checkout / link de pagamento foi pago. |
withdraw.done | evento | Um saque foi concluído. |
withdraw.failed | evento | Um saque falhou e foi estornado integralmente. |
withdraw.refunded | evento | O recebedor devolveu (estornou) um saque Copia e Cola já concluído, o valor voltou ao seu saldo. |
Formato do evento
O corpo é sempre um JSON com um id estável (o mesmo em todas as retentativas do evento), use-o para deduplicar. Em eventos *.paid/withdraw.done, data.status vem PAID com paidAt, payer (pagador real) e endToEndId preenchidos; em eventos *.failed, data.status vem FAILED/CANCELED com failureReason.
{
"id": "evt_9f8a2c1e",
"event": "pixQrCode.paid",
"devMode": false,
"createdAt": "2026-07-01T12:03:11Z",
"data": {
"id": "chg_9f8a2c1e",
"kind": "pixQrCode",
"status": "PAID",
"amount": 4990,
"netAmount": 4960,
"paidAt": "2026-07-01T12:03:11Z",
"payer": {
"name": "Maria Souza",
"document": "12345678900",
"documentMasked": "***.456.789-**",
"bank": "NU PAGAMENTOS"
},
"endToEndId": "E18935924202607011203apiv1x9f8a2",
"failureReason": null
}
}billing.paid, checkouts e cobranças de uso único
No evento billing.paid o data traz também singleUse (true quando a cobrança foi criada via /v1/billing/single-use/create) e externalReference, use-o para conciliar o pagamento com a fatura no seu sistema. netAmount, paidAt, payer e endToEndId vêm preenchidos na confirmação.
{
"id": "evt_1b2c3d4e",
"event": "billing.paid",
"devMode": false,
"createdAt": "2026-07-22T15:05:20Z",
"data": {
"id": "chg_ab12cd34",
"kind": "billing",
"status": "PAID",
"singleUse": true,
"externalReference": "fatura_123456",
"amount": 5000,
"netAmount": 4950,
"paidAt": "2026-07-22T15:05:20Z",
"payer": {
"name": "João Silva",
"document": "12345678900",
"documentMasked": "***.456.789-**",
"bank": "NU PAGAMENTOS"
},
"endToEndId": "E18935924202607221505apiv1x1a2b3",
"metadata": {
"clienteId": "123"
}
}
}O objeto data.payer, quem de fato pagou
Em pixQrCode.paid e billing.paid, o payer traz o pagador REAL confirmado pelo banco emissor na liquidação do PIX, não o pagador declarado na criação da cobrança. Use-o para conciliar, identificar clientes e prevenir fraude.
| Parâmetro | Tipo | Descrição |
|---|---|---|
payer.name | string | Nome completo do pagador real, confirmado pelo banco emissor. |
payer.document | string | CPF/CNPJ completo do pagador (somente dígitos). Trate com cuidado, dado sensível (LGPD). |
payer.documentMasked | string | Versão pronta para exibição: CPF mascarado (***.456.789-**) ou CNPJ formatado. |
payer.bank | string | Instituição financeira de origem do pagamento (ex.: NU PAGAMENTOS). |
payer pode vir null
payer vem null, os demais campos (endToEndId, paidAt, valores) continuam preenchidos e o endToEndId permite rastrear a origem junto ao Banco Central.Cabeçalhos de cada entrega
| Parâmetro | Tipo | Descrição |
|---|---|---|
X-Lynx-Event | header | Nome do evento (ex.: pixQrCode.paid). |
X-Lynx-Event-Id | header | ID estável do evento (= id no corpo). Deduplique por ele. |
X-Lynx-Delivery | header | ID único desta entrega (muda a cada reenvio). |
X-Lynx-Attempt | header | Número da tentativa (1, 2, 3…). |
X-Lynx-Timestamp | header | Unix timestamp da assinatura desta tentativa. |
X-Lynx-Webhook-Secret | header | Seu secret (whsec_…) para uma checagem rápida de igualdade. |
X-Lynx-Signature | header | Assinatura HMAC: t=<timestamp>,v1=<hmac>. |
Segurança em 2 camadas
1) Enviamos o seu secret (whsec_…) no header X-Lynx-Webhook-Secret, para uma checagem rápida de igualdade. 2) E assinamos `${timestamp}.${corpo}` com HMAC SHA-256 no header X-Lynx-Signature. Sempre valide a assinatura e rejeite eventos com timestamp muito antigo (proteção contra replay).
import crypto from "crypto";
const TOLERANCE = 5 * 60; // 5 min, rejeita replays
export function verify(rawBody, signatureHeader, secret) {
const parts = Object.fromEntries(signatureHeader.split(",").map((p) => p.split("=")));
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > TOLERANCE) return false; // replay
const expected = crypto
.createHmac("sha256", secret)
.update(`${parts.t}.${rawBody}`)
.digest("hex");
return crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
}import hmac, hashlib, time
TOLERANCE = 5 * 60 # 5 min, rejeita replays
def verify(raw_body: bytes, signature_header: str, secret: str) -> bool:
parts = dict(p.split("=") for p in signature_header.split(","))
if abs(time.time() - int(parts["t"])) > TOLERANCE:
return False # replay
expected = hmac.new(secret.encode(), f"{parts['t']}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(parts["v1"], expected)Retentativas e idempotência
Se sua URL não responder 2xx, re-entregamos o mesmo evento com backoff crescente. Como um evento pode chegar mais de uma vez, trate a entrega de forma idempotente.
| Parâmetro | Tipo | Descrição |
|---|---|---|
Agenda de retry | backoff | 10s → 30s → 2min → 5min → 15min → 30min → 1h → 6h (8 tentativas, ~12h). |
Idempotência | obrigatório | Deduplique pelo id / X-Lynx-Event-Id. Ignore um evento já processado. |
Responda rápido | importante | Devolva 2xx em poucos segundos e processe de forma assíncrona. |
Reenvio manual | painel | Em Painel → API → Logs → Entregas, reenvie qualquer entrega com 1 clique. |
Valide sempre | segurança | Cheque a assinatura HMAC (e o timestamp) antes de creditar qualquer coisa. |

