Pular para o conteúdo
xFlow Payments

Blog

Pagamento Pix em lote por API

Na API da xFlow não existe um endpoint de lote: cada pagamento é um POST /api/v1/transfers. O lote é uma fila no seu sistema que envia um saque Pix por item, com Idempotency-Key própria, respeita o limite de requisições e confere cada resultado por webhook ou consulta.

Atualizado em · Equipe xFlow Payments · 6 min de leitura

Pagamento Pix em lote é enviar vários Pix de uma vez, por exemplo para pagar fornecedores ou repassar valores a parceiros no fim do dia. Na API da xFlow, a documentação descreve uma transferência por chamada: POST /api/v1/transfers, com um destino e um valor. Não há endpoint que receba uma lista. O lote, então, vive no seu sistema: uma fila de itens, um saque por item, cada um com a sua Idempotency-Key, e uma conferência no fim. Se você ainda não usou o saque pela API, comece pelo guia de API de saque Pix.

Por que montar o lote item por item

Enviar um saque por item parece mais trabalhoso, mas dá ao seu sistema controle sobre cada pagamento:

  • Cada item tem o seu status. Um favorecido com chave errada não trava os outros.
  • O retry é por item. Se a conexão cair no item 37, você repete só o 37, sem risco de pagar os outros de novo.
  • Cada pagamento é rastreável. O saque tem id próprio e cada resposta traz um request_id, que aparece em Integrações > Logs da API.

Antes de rodar o lote

  1. Chave de produção. O saque não funciona com chave de teste: a resposta é 403 test_mode_unsupported.
  2. Saque via API habilitado e conta ativa. Sem isso, as respostas são 403 api_withdrawal_disabled ou 403 account_inactive.
  3. Saldo suficiente. Consulte GET /api/v1/balance e compare o available com a soma do lote. As taxas de saque da conta vêm em GET /api/v1/company, nos campos withdrawalPercent e withdrawalFixedCents.
  4. Dados de cada favorecido. Para saque por chave, você precisa de pix_key e pix_key_type: CPF, CNPJ, EMAIL, PHONE ou EVP (chave aleatória, formato UUID). São os tipos do diretório de chaves do Pix, o DICT. Valide o formato antes de enviar, para não gastar chamada com 400 validation.
  5. Quanto o favorecido recebe. No saque por chave, a taxa sai do próprio amount, e o favorecido recebe o net_amount. No saque por QR Code, o favorecido recebe o valor do QR e a taxa é debitada por cima. Se o fornecedor precisa receber o valor exato da nota, peça o QR Code de cobrança dele ou calcule o amount com a taxa incluída e confira o net_amount na resposta. Nem todo provedor aceita QR: quando não aceita, a resposta é 422 transfer_failed e nada é debitado.

O modelo de dados do lote

Grave o lote antes de enviar qualquer coisa. Uma tabela de itens resolve:

Campo Para que serve
idempotency_key Chave estável do item, como lote-20261025-item-0037
pix_key e pix_key_type Destino do pagamento
valor_centavos O amount enviado à API
transfer_id O id devolvido pela API; vazio enquanto não foi enviado
status O último status conhecido do saque
request_id O identificador da chamada, para os Logs da API e o suporte
erro O código de erro, quando houver

A chave precisa ser a mesma em todas as tentativas do mesmo item. Não use horário nem número aleatório na chave: na repetição, a chave tem de bater com a primeira tentativa. Por que isso importa está em idempotência em API de pagamento.

O envio, item por item

O exemplo abaixo, em Node.js 18 ou superior, envia os itens em sequência. Ele repete falhas de conexão e respostas 5xx com a mesma chave, espera o retry-after no 429 e grava o resultado de cada item.

js
const BASE = "https://app.xflowpayments.com/api/v1";
const AUTH = "Basic " + Buffer.from(
  `${process.env.XFLOW_PUBLIC_KEY}:${process.env.XFLOW_SECRET_KEY}`,
).toString("base64");
const espera = (ms) => new Promise((ok) => setTimeout(ok, ms));

async function enviarItem(item) {
  for (let tentativa = 1; tentativa <= 5; tentativa++) {
    let res;
    try {
      res = await fetch(`${BASE}/transfers`, {
        method: "POST",
        headers: {
          Authorization: AUTH,
          "Content-Type": "application/json",
          "Idempotency-Key": item.idempotencyKey, // a mesma em toda tentativa
        },
        body: JSON.stringify({
          amount: item.valorCentavos,
          pix_key: item.pixKey,
          pix_key_type: item.pixKeyType,
          message: item.mensagem,
        }),
      });
    } catch {
      await espera(2 ** tentativa * 1000); // falha de conexão: tenta de novo
      continue;
    }
    const requestId = res.headers.get("x-request-id");
    if (res.status === 429) {
      await espera(Number(res.headers.get("retry-after") ?? 5) * 1000);
      continue;
    }
    if (res.status >= 500) {
      await espera(2 ** tentativa * 1000);
      continue;
    }
    const corpo = await res.json();
    if (res.ok) return { ok: true, saque: corpo.data, requestId };
    return { ok: false, erro: corpo.error, mensagem: corpo.message, requestId };
  }
  return { ok: false, erro: "sem_resposta" };
}

async function rodarLote(itens) {
  for (const item of itens) {
    if (item.transferId) continue; // já enviado numa execução anterior
    const resultado = await enviarItem(item);
    await gravarResultado(item, resultado); // grava transfer_id, status, request_id ou erro
  }
}

Enviar em sequência é o jeito mais simples de respeitar o limite de requisições, que é por conta. Se precisar de paralelismo, limite o número de envios simultâneos e acompanhe os cabeçalhos x-ratelimit-remaining e x-ratelimit-reset, presentes em toda resposta autenticada.

Acompanhamento: status e webhooks

Cada saque tem um destes status: requested, processing, completed ou rejected. Se o saque automático estiver desligado na conta, cada item nasce com pending_approval: true e espera aprovação antes de seguir.

Assine os webhooks withdrawal.processing, withdrawal.completed e withdrawal.failed. A documentação não detalha o data desses eventos, então use o webhook como gatilho e confirme o estado com GET /api/v1/transfers/{id}, usando o transfer_id que você gravou. Para conferir o lote inteiro de uma vez, liste com GET /api/v1/transfers?from=2026-10-25&to=2026-10-25 e siga o cursor starting_after enquanto has_more for true.

Conferência no fim do lote

Resultado do item O que fazer
201 e depois completed Pago. Dê baixa no título do fornecedor.
201 com pending_approval: true Aguarda aprovação. Não reenvie.
rejected ou withdrawal.failed Não pago. Leia o failure_reason, corrija e envie como pagamento novo, com chave nova.
400 validation Dados inválidos. Corrija e envie com chave nova, porque a mesma chave com outro corpo dá 409.
409 idempotency_conflict A chave foi usada com outro corpo. Revise a geração de chaves no seu código.
422 transfer_failed Recusado, por exemplo por saldo insuficiente ou QR não aceito. Confira o saldo e a mensagem.
Sem resposta Repita com a mesma chave. Se o saque já existir, a API devolve o mesmo.

A lista completa de códigos está em erros da API Pix. Para fechar o lote com o extrato, use o mesmo raciocínio da conciliação de pagamentos: cada item ligado ao seu transfer_id e ao request_id da chamada.

Controles que valem para todo lote

  • Aprovação interna antes do envio. Quem monta o lote não deveria ser quem libera. A API envia o que o seu sistema mandar, então a alçada fica do seu lado.
  • Allowlist de IP. Limite as chamadas ao IP do servidor que roda o lote. Fora da lista, a API responde 401.
  • Segredo fora do código. O sk_live_ fica num cofre ou numa variável de ambiente, nunca no repositório.
  • Primeiro lote pequeno. Como não há teste de saque no sandbox, valide a fila com a API simulada nos seus testes e rode o primeiro lote real com poucos itens.

A referência dos campos do saque está na documentação da API.

Perguntas frequentes

A API da xFlow tem um endpoint de pagamento em lote?

Não. A documentação descreve um saque por chamada, em POST /api/v1/transfers, com um destino e um valor. Para pagar vários favorecidos, o seu sistema monta uma fila e envia um saque por item.

Como evitar pagar o mesmo fornecedor duas vezes se o lote parar no meio?

Dê a cada item uma Idempotency-Key estável, como o id do lote mais o número do item, e grave o id do saque devolvido. Ao rodar de novo, pule os itens com id gravado e repita os outros com a mesma chave: a API devolve o mesmo saque em vez de debitar o saldo outra vez.

O fornecedor recebe o valor cheio?

Depende do destino. No saque por chave Pix, a taxa sai do próprio amount, e o favorecido recebe o net_amount. No saque por QR Code, o favorecido recebe o valor do QR e a taxa é debitada por cima do seu saldo.

Dá para testar o lote no ambiente de teste?

Não. Com chave de teste, POST /api/v1/transfers responde 403 test_mode_unsupported. Teste a lógica da fila com a API simulada nos seus testes automatizados e comece a produção com um lote pequeno.

Continue lendo

BlogAPI de saque PixNa API da xFlow, o saque Pix (Pix out) é um POST /api/v1/transfers com amount em centavos e a chave do destinatário (pix_key e pix_key_type) ou um qr_code copia e cola. O saque nasce com status requested, pode exigir aprovação (pending_approval) e é acompanhado pelos webhooks withdrawal.* ou por GET /api/v1/transfers/{id}. Não funciona com chave de teste.BlogIdempotência em API de pagamento: retry sem duplicarIdempotência é a garantia de que repetir a mesma requisição não repete o efeito. Na API da xFlow, você envia o header Idempotency-Key ao criar cobranças e saques: num retry com a mesma chave, a API devolve a mesma cobrança ou o mesmo saque, sem gerar outro Pix nem debitar o saldo de novo.BlogErros da API PixNa API Pix da xFlow, todo erro volta em JSON com error (o código), message e request_id. O status HTTP diz a família do problema: 400 corpo inválido, 401 credencial, 403 conta ou modo, 404 recurso inexistente, 409 idempotência, 422 recusa de negócio, 429 limite e 502 falha no provedor. O request_id localiza a chamada em Integrações > Logs da API.BlogConciliação de pagamentos com várias adquirentesConciliar pagamentos é conferir se cada valor recebido bate com um pedido e com o extrato. Com várias adquirentes, cada uma devolve dados de um jeito. Na xFlow, toda cobrança volta no mesmo formato, com End-to-end ID, dados de quem pagou, a referência do seu pedido, valor, líquido e taxa.GlossárioO que é DICTDICT é o Diretório de Identificadores de Contas Transacionais, o serviço do Pix que guarda as chaves Pix e devolve os dados da conta de quem vai receber. É ele que permite pagar com CPF, CNPJ, telefone, e-mail ou chave aleatória, sem digitar agência e conta.