Processing your request, please wait...

    Referência da API — Webhooks

    Payload de /webhooks/deposit_v2 e do callback de saída (response_url)

    Quem chama quem

    Os endpoints /webhooks/deposit_v2 e /webhooks/deposit documentados na especificação Swagger são chamados pelo provedor de pagamento (stack DePix) para a nossa plataforma — eles não são chamados pelo seu sistema, e estão documentados aqui apenas como referência do formato de payload.

    O que você efetivamente implementa é o inverso: um endpoint receptor. Quando você cria uma cobrança via POST /integrated-payment com webhook: 1 e response_url, é a nossa plataforma que faz um POST na sua response_url assim que o pagamento é confirmado — usando exatamente o mesmo formato de payload documentado abaixo para /webhooks/deposit_v2. Você não chama esses endpoints; você recebe chamadas neles ("outgoing merchant callback").

    A metadata opcional enviada em details na criação da cobrança é decodificada e mesclada de volta no campo details_transaction desse callback.

    ⚠️ No seu próprio endpoint receptor (response_url), e em qualquer chamada que você fizer a outros endpoints desta API, sempre envie o header Accept: application/json. Sem ele, uma falha de validação de parâmetro obrigatório retorna um redirect HTML 302 em vez de um erro JSON — reproduzido ao vivo em /integrated-payment. Veja a página de API — Pagamentos para detalhes.

    POST/webhooks/deposit_v2

    Formato atual e recomendado para novas integrações — é também o formato usado no callback de saída (response_url) descrito acima. Autenticado com Authorization: Basic <TOKEN>, onde <TOKEN> é o valor configurado em TOKEN_API_WEBHOOK_CALLBACK_EULEN. Cabeçalhos incorretos recebem 401.

    Limite de taxa efetivo: 60 requisições por minuto (verificado ao vivo via headers X-RateLimit-* em 19/07/2026). A rota em si declara um limite de 100/min, mas existe um limitador global de 60/min por IP aplicado a todas as rotas /api/*, que é mais restrito e é atingido primeiro. Versões anteriores desta documentação diziam 100/min — isso estava incorreto.

    Campos do payload

    CampoTipoDescrição
    qrIdstringIdentificador da cobrança/QRCode.
    bankTxIdstring, nullableID da transação bancária, quando presente. Sempre null em callbacks de saída para o integrador.
    blockchainTxIDstring, nullableID da transação on-chain/Liquid, quando presente.
    customerMessagestring, nullableMensagem do pagador.
    payerNamestring, nullableNome do pagador.
    payerTaxNumberstring, nullableCPF/CNPJ do pagador (somente dígitos).
    payerEUIDstringPresente apenas quando o pagador é conhecido no cadastro de pagadores DePix.
    expirationstring (date-time ISO-8601)Timestamp de expiração.
    pixKeystring, nullableChave PIX usada na cobrança.
    statusstringStatus do pagamento (ex.: depix_sent, paid).
    valueInCentsintegerValor em centavos.
    details_transactionobjectPresente quando há metadata associada à cobrança. Contém os dados originalmente enviados em details na criação do QRCode, decodificados e mesclados aqui.

    Exemplo de payload recebido

    {
      "qrId": "0196e8c8cc1071d9ad572ec4c10b8abb",
      "bankTxId": "fitbank_...",
      "blockchainTxID": "5532bac5c84b06e724f8266566b439011578858d2a03211ea1a261fc3add8f99",
      "customerMessage": null,
      "payerName": "Jane Doe",
      "payerTaxNumber": "12345678901",
      "expiration": "2025-05-19T11:03:15-03:00",
      "pixKey": "pay@example.com",
      "status": "depix_sent",
      "valueInCents": 200
    }

    Exemplo com details_transaction (callback de saída)

    {
      "qrId": "0196e8c8cc1071d9ad572ec4c10b8aab",
      "bankTxId": null,
      "blockchainTxID": "5532bac5c84b06e724f8266566b4390115788588da03211ea1a261fc3add8f99",
      "customerMessage": null,
      "payerName": "Jane Doe",
      "payerTaxNumber": "12345678901",
      "payerEUID": "EU014794399658321",
      "expiration": "2025-05-19T11:03:15-03:00",
      "pixKey": "pay@example.com",
      "status": "depix_sent",
      "valueInCents": 1210,
      "details_transaction": {
        "list_id_payments": [1, 2, 3, 5],
        "list_amount": [100, 253, 21.5, 145.68],
        "host": [333, 333, 333, 333],
        "condo": [2, 2, 2, 2]
      }
    }

    Exemplo de resposta de sucesso (200)

    Resposta real, capturada em produção para um qrId não correspondente a nenhuma cobrança pendente, num domínio sem servidores espelho configurados (19/07/2026):

    {
      "async": false,
      "response": {
        "status": [],
        "thirdPart": []
      },
      "forward": []
    }

    response traz o resultado do matching/liquidação do depósito contra uma cobrança pendente. forward traz os resultados do encaminhamento do webhook para servidores espelho — só é não-vazio nos domínios rodolforomao/blackrocket.

    Respostas

    StatusSignificado
    200Confirmado. O processamento interno pode continuar de forma assíncrona.
    401Não autorizado — cabeçalho Authorization ausente ou inválido. {"success": false, "message": "Unauthorized.", "response": null}.
    429Limite efetivo de 60 requisições/minuto excedido (veja o aviso acima). {"message": "Too Many Attempts."}.

    POST/webhooks/deposit (legado)

    Formato legado, mantido para compatibilidade — use /webhooks/deposit_v2 para novas integrações. Mesma autenticação (Authorization: Basic <TOKEN>). Também documentado apenas como referência: quem chama este endpoint é o provedor de pagamento, não o seu sistema.

    Limite de taxa efetivo: 60 requisições por minuto — mesma ressalva do /webhooks/deposit_v2 acima: a rota declara 100/min, mas o limitador global de 60/min por IP em todas as rotas /api/* é mais restrito e é atingido primeiro (verificado ao vivo em 19/07/2026).

    Campos do payload

    CampoTipoDescrição
    qrIdstringIdentificador da cobrança/QRCode.
    statusstringStatus do pagamento (ex.: depix_sent, paid).
    valueInCentsintegerValor em centavos.

    Exemplo de payload recebido

    {
      "qrId": "0196e8c8cc1071d9ad572ec4c10b8abb",
      "status": "depix_sent",
      "valueInCents": 200
    }

    Exemplo de resposta de sucesso (200)

    {
      "success": true,
      "message": "Webhook received and processing."
    }

    O processamento interno (matching do depósito, encaminhamento para servidores espelho) acontece de forma assíncrona após o envio dessa resposta.

    Respostas

    StatusSignificado
    200Confirmado. O processamento interno pode continuar de forma assíncrona.
    401Não autorizado — cabeçalho Authorization ausente ou inválido. {"success": false, "message": "Unauthorized."}.
    429Limite efetivo de 60 requisições/minuto excedido (veja o aviso acima). {"message": "Too Many Attempts."}.

    Implementando seu endpoint receptor (response_url)

    O endpoint que você expõe como response_url precisa: (1) validar o cabeçalho Authorization: Basic <TOKEN> antes de processar qualquer coisa, comparando-o com o token combinado com a plataforma; (2) ler o payload no formato v2 documentado acima; (3) responder rapidamente com um status 2xx para confirmar o recebimento — evite processamento pesado de forma síncrona dentro do próprio handler; (4) tratar o evento de forma idempotente, verificando se o qrId já foi processado antes de aplicar qualquer efeito colateral (como liberar um pedido).

    Nunca pule a validação do cabeçalho Authorization. Um endpoint receptor que aceita qualquer chamada sem autenticação pode ser explorado para simular pagamentos falsos.

    Exemplo em PHP (puro, sem framework)

    <?php
    // receptor-webhook.php — endpoint que VOCÊ hospeda para receber o callback
    // (a plataforma faz o POST aqui quando você usa webhook:1 + response_url em /integrated-payment)
    
    $expectedToken = getenv('MEU_TOKEN_WEBHOOK'); // valor combinado com a plataforma
    $authHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
    
    if (!hash_equals("Basic {$expectedToken}", $authHeader)) {
        http_response_code(401);
        header('Content-Type: application/json');
        echo json_encode(['success' => false, 'message' => 'Unauthorized']);
        exit;
    }
    
    $payload = json_decode(file_get_contents('php://input'), true);
    
    // Payload no formato v2 (mesmo shape de /webhooks/deposit_v2):
    // qrId, bankTxId, blockchainTxID, customerMessage, payerName, payerTaxNumber,
    // payerEUID?, expiration, pixKey, status, valueInCents, details_transaction?
    
    if (in_array($payload['status'] ?? null, ['depix_sent', 'paid'], true)) {
        // Marque o pedido correspondente a $payload['qrId'] como pago.
        // Se você enviou metadata em `details` na criação da cobrança, ela
        // volta aqui em $payload['details_transaction'].
    }
    
    // Responda 2xx rapidamente para confirmar o recebimento.
    http_response_code(200);
    header('Content-Type: application/json');
    echo json_encode(['success' => true]);

    Exemplo em Node.js (Express)

    // receptor-webhook.js — endpoint que VOCÊ hospeda para receber o callback
    // (a plataforma faz o POST aqui quando você usa webhook:1 + response_url em /integrated-payment)
    const express = require('express');
    const app = express();
    app.use(express.json());
    
    const EXPECTED_TOKEN = process.env.MEU_TOKEN_WEBHOOK; // valor combinado com a plataforma
    
    app.post('/webhook/pix-pago', (req, res) => {
      const authHeader = req.headers['authorization'] || '';
    
      if (authHeader !== `Basic ${EXPECTED_TOKEN}`) {
        return res.status(401).json({ success: false, message: 'Unauthorized' });
      }
    
      const payload = req.body;
      // Payload no formato v2 (mesmo shape de /webhooks/deposit_v2):
      // qrId, bankTxId, blockchainTxID, customerMessage, payerName, payerTaxNumber,
      // payerEUID?, expiration, pixKey, status, valueInCents, details_transaction?
    
      if (payload.status === 'depix_sent' || payload.status === 'paid') {
        // Marque o pedido correspondente a payload.qrId como pago.
        // Se você enviou metadata em `details` na criação da cobrança, ela
        // volta aqui em payload.details_transaction.
      }
    
      // Responda 2xx rapidamente para confirmar o recebimento.
      res.status(200).json({ success: true });
    });
    
    app.listen(3000);

    Próximos passos