LYNX UPBANK
DocsFundamentosWebhooks
1 min de leitura

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

Cada evento é enviado em background (nunca trava a operação) e re-tentado automaticamente com backoff exponencial até 8 tentativas ao longo de ~12h caso seu endpoint fique fora do ar. Você também pode reenviar manualmente qualquer entrega.

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âmetroTipoDescrição
pixQrCode.paideventoUma cobrança PIX foi paga.
pixQrCode.failedeventoUma cobrança PIX expirou ou falhou (não será paga).
boleto.paideventoUm boleto foi pago (pela linha digitável ou pelo Pix do título).
billing.paideventoUm checkout / link de pagamento foi pago.
withdraw.doneeventoUm saque foi concluído.
withdraw.failedeventoUm saque falhou e foi estornado integralmente.
withdraw.refundedeventoO 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.

json
{
  "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.

json
{
  "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âmetroTipoDescrição
payer.namestringNome completo do pagador real, confirmado pelo banco emissor.
payer.documentstringCPF/CNPJ completo do pagador (somente dígitos). Trate com cuidado, dado sensível (LGPD).
payer.documentMaskedstringVersão pronta para exibição: CPF mascarado (***.456.789-**) ou CNPJ formatado.
payer.bankstringInstituição financeira de origem do pagamento (ex.: NU PAGAMENTOS).

payer pode vir null

Em casos raros o banco emissor não informa o pagador na liquidação (ex.: algumas transferências entre contas de pagamento). Nesses casos 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âmetroTipoDescrição
X-Lynx-EventheaderNome do evento (ex.: pixQrCode.paid).
X-Lynx-Event-IdheaderID estável do evento (= id no corpo). Deduplique por ele.
X-Lynx-DeliveryheaderID único desta entrega (muda a cada reenvio).
X-Lynx-AttemptheaderNúmero da tentativa (1, 2, 3…).
X-Lynx-TimestampheaderUnix timestamp da assinatura desta tentativa.
X-Lynx-Webhook-SecretheaderSeu secret (whsec_…) para uma checagem rápida de igualdade.
X-Lynx-SignatureheaderAssinatura 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).

javascript
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));
}
python
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âmetroTipoDescrição
Agenda de retrybackoff10s → 30s → 2min → 5min → 15min → 30min → 1h → 6h (8 tentativas, ~12h).
IdempotênciaobrigatórioDeduplique pelo id / X-Lynx-Event-Id. Ignore um evento já processado.
Responda rápidoimportanteDevolva 2xx em poucos segundos e processe de forma assíncrona.
Reenvio manualpainelEm Painel → API → Logs → Entregas, reenvie qualquer entrega com 1 clique.
Valide sempresegurançaCheque a assinatura HMAC (e o timestamp) antes de creditar qualquer coisa.
app.lynx · Webhooks & Entregas
Painel de entregas de webhook da LYNX com status e reenvio
Cada entrega é registrada com status, tentativas e resposta, com reenvio em 1 clique quando algo falha.