Pular para o conteúdo principal

Guia de QR Code Estático

Este guia explica como gerar QR Codes estáticos PIX pela API e por que essa é a forma recomendada de recebê-los.

Visão Geral

O QR Code estático é um código reutilizável: o mesmo BR Code aceita vários pagamentos, de pagadores diferentes, em momentos diferentes. O valor pode ser fixo ou aberto (digitado pelo pagador no app do banco dele). É o formato certo para:

  • Ponto de venda — placa ou adesivo no balcão, totem, mesa de restaurante
  • Cobrança recorrente informal — mensalidade, aluguel, contribuição
  • Doações e gorjetas — valor aberto, pagador decide quanto
  • Link fixo de pagamento — página "pague aqui" que não muda a cada pedido

Se você precisa de uma cobrança única, com valor definido, expiração e conciliação um-para-um com um pedido, use o QR Code Dinâmico.

Gere o QR pela API, não monte o BR Code por conta própria

É tecnicamente possível montar uma string EMV/BR Code estática por conta própria, seguindo a especificação do arranjo PIX, usando apenas a sua chave. O pagamento cai na conta e o crédito é avisado — mas você fica sem o fio que liga o pagamento à cobrança, sem lookup, sem cancelamento e sem as políticas. A seção seguinte explica exatamente o que se perde.

Por que gerar pela API

Um QR estático criado pela API nasce com um identifier seu, validado aqui e registrado no liquidante. Esse identificador é o fio que costura o pagamento do começo ao fim: ele volta no webhook, no extrato e no lookup.

Um BR Code montado por fora não tem esse fio registrado em lugar nenhum. O pagamento cai na conta e os webhooks saem, mas o identificador que chega neles é o que você embutiu no código — se é que embutiu algum — e, fora o dinheiro, nada daquele QR existe do nosso lado.

QR criado pela APIBR Code montado por você
Webhook qrcode.paidRecebido, com o seu identifierRecebido, com o txid que você embutiu — ou com o campo vazio
Webhook pix.in.completedRecebido, com o seu identifierIdem
ExtratoLançamento traz identifierSó traz identificador se você embutiu um txid
Lookup do QRGET .../pix/qr-code/lookup?identifier= funcionaNão existe QR para consultar
CancelamentoDELETE tira o código de circulaçãoNão há o que cancelar pela API
PolíticasRegras da seção qrCode são avaliadas na criaçãoNenhuma regra é avaliada
Colisão de identificadoridentifier validado e registradoO txid é livre e pode colidir com o de uma cobrança sua

O identificador vira uma aposta

O liquidante repassa o txid embutido no BR Code como identifier do pagamento. Se você montou o código com um txid seu, ele aparece nos webhooks e no extrato, e o fio até funciona. Só que esse valor nunca passou pela nossa validação (38 caracteres, charset [A-Za-z0-9._-], prefixo fee- reservado) e não corresponde a QR nenhum aqui dentro. Se ele coincidir com o identifier de um QR dinâmico ativo da mesma conta, o pagamento é atribuído àquela cobrança, que passa a constar como paga.

O caso mais comum, porém, é o BR Code estático sair com *** no campo de txid — o placeholder que a especificação prevê para "sem identificador". Aí o pagamento chega sem identificador nenhum. Os dois webhooks continuam saindo, com identifier vazio, e a única forma de conciliar é por aproximação: valor, horário e pagador.

O que não existe do nosso lado

Como o QR nunca foi criado aqui, não há registro dele. O lookup por identifier não encontra nada e o DELETE não tem o que cancelar — para tirar o código de circulação, resta recolher a placa. As regras da seção qrCode da política também não são avaliadas, porque não houve criação para avaliar. E ninguém confere se a chave embutida pertence à conta ou se o EMV está bem formado: um código torto só falha no app do pagador, e você descobre pelo cliente reclamando.

Não existe endpoint para registrar um BR Code externo

A API não tem como "adotar" um código gerado por fora. O POST /v1/accounts/{accountId}/pix/out/qr-code/decode decodifica um BR Code, mas serve para pagar um QR de terceiro, não para registrar um QR seu de recebimento. Se o QR já está impresso e circulando montado por fora, o caminho é gerar um novo pela API e substituir.

Estático ou dinâmico?

EstáticoDinâmico
RotaPOST .../pix/qr-code/staticPOST .../pix/qr-code/dynamic
ValorOpcional (omitir = QR aberto)Obrigatório, maior que zero
ExpiraçãoNão expiraexpirationDate (default do liquidante: 1 dia)
PagamentosVários, no mesmo códigoUm
Restringir o pagadorNãoallowedPayerTaxNumber
Webhook de expiraçãoNão se aplicaqrcode.expired
Uso típicoBalcão, doação, mensalidadeCheckout, fatura, pedido

Passo 1: Criar o QR Code Estático

Request

curl -X POST "https://tenant.api.corpx.com/v1/accounts/{accountId}/pix/qr-code/static" \
-H "Authorization: Bearer {token}" \
-H "X-Tenant-Id: {tenant_id}" \
-H "Content-Type: application/json" \
-d '{
"pixKey": "your-pix-key-here",
"value": 49.90,
"identifier": "loja-centro-caixa-01"
}'

Parâmetros do Body

CampoTipoObrigatórioDescrição
pixKeystringSimChave PIX da conta recebedora (deve estar cadastrada)
valuenumberNãoValor fixo em BRL. Omitido ou 0 cria um QR aberto, em que o pagador digita o valor
identifierstringRecomendadoSeu identificador do QR (máximo 38 caracteres, charset [A-Za-z0-9._-], não pode começar com fee-). Se omitido, a API gera um UUID sem hifens
messagestringNãoMensagem de até 140 caracteres. Ver o aviso abaixo
message não chega ao BR Code estático

O campo é aceito e validado, mas hoje não é repassado ao liquidante na criação do QR estático — ele não aparece para o pagador nem volta preenchido no message/description da resposta. Se você precisa de texto visível na cobrança, use o QR dinâmico, que repassa a mensagem.

Escolha um identifier que signifique algo

O identificador é a sua chave de conciliação, e ele volta em todo pagamento daquele QR. Em QR estático ele identifica o ponto de cobrança, não a venda: loja-centro-caixa-01, mesa-14, doacao-site. Assim, quando três pagamentos chegam, você sabe de qual placa cada um veio.

Resposta de Sucesso (201 Created)

{
"txid": "loja-centro-caixa-01",
"emv": "00020126580014br.gov.bcb.pix0136d2e1c0a4-8f5b-4c7e-9a1d-6b3f2e8c7a5052040000530398654049.905802BR5912SUA EMPRESA6004Lins62070503***6304A1B2",
"type": "static",
"status": "ACTIVE",
"value": 49.90,
"identifier": "loja-centro-caixa-01",
"accountId": "{accountId}",
"pixKey": "8026ff12-cb4a-4d19-9638-08bb15d450e2",
"allowChange": false,
"createdAt": "2026-02-10T12:00:00Z"
}
Sem envelope data

A resposta é um objeto plano (top-level). Não há envelope data nem os campos statusCode/title/message. O código copia-e-cola está em emv e a chave em pixKey.

CampoDescrição
emvCódigo PIX Copia e Cola (string EMV). Renderize como QR
identifierO identificador que você enviou (ou o gerado pela API)
txidEcoa o identifier — em QR estático os dois são o mesmo valor
typeSempre static neste endpoint
statusACTIVE na criação
valueValor fixo, ou 0 quando o QR é aberto
allowChangetrue quando o pagador pode alterar o valor

Note que não há expiresAt: o QR estático não expira. Ele vale até você cancelar.

Reenvio não é deduplicado

A API não deduplica a criação por identifier — não há tratamento de Idempotency-Key neste endpoint. Antes de recriar um QR que talvez já exista, consulte pelo lookup (Passo 4). Recriar com um identificador já usado depende da resposta do liquidante e pode falhar com conflito.

Passo 2: Exibir o QR Code

Gere a imagem a partir da string emv com qualquer biblioteca de QR code (qrcode.js, python-qrcode, etc.) e ofereça também o copia e cola:

<input type="text" value="00020126580014br.gov.bcb.pix..." readonly />
<button onclick="navigator.clipboard.writeText(this.previousElementSibling.value)">
Copiar
</button>

Como o QR estático não expira, a imagem pode ser impressa, colada numa placa ou publicada num site — ela continua válida enquanto o QR não for cancelado.

Passo 3: Receber os Pagamentos por Webhook

Cada pagamento recebido naquele QR dispara dois eventos, e os dois trazem o seu identifier:

  • qrcode.paid — "o QR que eu criei foi pago" (conciliação da cobrança)
  • pix.in.completed — "minha conta foi creditada" (ledger, saldo, extrato)

Assine os dois ou só o que fizer sentido para o seu fluxo; eles descrevem o mesmo dinheiro sob ângulos diferentes.

qrcode.paid

{
"id": "qrcode-paid-E303062942026021014300000005ABCD",
"type": "qrcode.paid",
"occurredAt": "2026-02-10T14:30:12.000000000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-yourcompany",
"accountId": "{accountId}",
"data": {
"qrcodeId": "loja-centro-caixa-01",
"identifier": "loja-centro-caixa-01",
"accountId": "{accountId}",
"tenantId": "tenant-yourcompany",
"type": "static",
"amount": 49.90,
"endToEnd": "E303062942026021014300000005ABCD",
"transactionId": "b7c1e0f2-3a4d-4e5f-8a9b-0c1d2e3f4a5b",
"status": "SUCCESS",
"receivedAt": "2026-02-10T14:30:12.000000000Z",
"payer": {
"name": "João da Silva",
"document": "12345678901",
"bankIspb": "30306294",
"branch": "0020",
"account": "004912314"
},
"payee": {
"name": "SUA EMPRESA LTDA",
"document": "12345678000190"
}
}
}

Em QR estático, qrcodeId e identifier carregam o mesmo valor: o identificador que você escolheu na criação. O que distingue um pagamento do outro é o endToEnd.

Cada pagamento é um evento independente

O mesmo QR pago cinco vezes gera cinco qrcode.paid, cada um com seu endToEnd. Não trate o primeiro evento como "cobrança quitada" e pare de escutar — o código continua ativo. Use o endToEnd como chave de deduplicação das suas próprias baixas.

Não existem qrcode.expired nem qrcode.cancelled para QR estático: ele não expira, e o cancelamento é uma ação sua, síncrona, cuja confirmação é a resposta HTTP do DELETE.

Passo 4: Conciliar

O identifier do QR aparece nos dois lugares onde você procura dinheiro:

No extrato, cada lançamento traz o campo identifier:

curl -X GET "https://tenant.api.corpx.com/v1/accounts/{accountId}/statement?from=2026-02-10&to=2026-02-10" \
-H "Authorization: Bearer {token}" \
-H "X-Tenant-Id: {tenant_id}"

No lookup de pagamento, você busca direto pelo identificador:

curl -X GET "https://tenant.api.corpx.com/v1/accounts/{accountId}/pix/payments/lookup?identifier=loja-centro-caixa-01" \
-H "Authorization: Bearer {token}" \
-H "X-Tenant-Id: {tenant_id}"

Consultar o estado do QR

curl -X GET "https://tenant.api.corpx.com/v1/accounts/{accountId}/pix/qr-code/lookup?identifier=loja-centro-caixa-01" \
-H "Authorization: Bearer {token}" \
-H "X-Tenant-Id: {tenant_id}"

A resposta é o mesmo objeto plano da criação, acrescido dos campos de pagamento quando há algum: paidAmount, endToEndId, paidAt, payer.

O lookup mostra um pagamento, não o histórico

Num QR reutilizável, o lookup reflete o último pagamento conhecido pelo liquidante, e o status passa a PAID depois do primeiro. Ele não lista os pagamentos anteriores. Para o histórico completo, use os webhooks qrcode.paid ou o extrato filtrado por período.

Status possíveis: ACTIVE, AWAITING-PAYMENT, PAID, CANCELLED, EXPIRED, UNKNOWN — sempre em MAIÚSCULAS.

Passo 5: Cancelar o QR Code

Um QR estático vale até ser cancelado. Cancele quando a placa sai de circulação, o caixa é desativado ou a campanha termina:

curl -X DELETE "https://tenant.api.corpx.com/v1/accounts/{accountId}/pix/qr-code?identifier=loja-centro-caixa-01" \
-H "Authorization: Bearer {token}" \
-H "X-Tenant-Id: {tenant_id}"

Resposta (200 OK):

{
"identifier": "loja-centro-caixa-01",
"status": "CANCELLED"
}

Depois do cancelamento, o BR Code impresso deixa de ser cobrável — quem escanear recebe erro no app do banco.

Políticas

A criação de QR estático passa pela seção qrCode da política do tenant ou da conta. Uma violação devolve 422 com errorCode: policy_denied e a lista de violations:

RegraruleEfeito
Criação de estáticoqrCode.canCreateStaticfalse recusa a criação
Valor mínimoqrCode.minAmountRecusa QR com valor abaixo do mínimo
Valor máximoqrCode.maxAmountRecusa QR com valor acima do máximo

As regras de valor não se aplicam a QR aberto: sem value na criação, não há montante para comparar, e as duas são puladas. Se o seu controle de valor precisa valer sempre, não use QR aberto. Detalhes em Políticas e Regras.

Escopo Necessário

OperaçãoEscopo
Criar QR estáticoqrcode.manage
Consultar e cancelarqrcode.manage ou read

Uma credencial só com qrcode.manage cria e acompanha QR codes sem enxergar saldo e extrato da conta. Ver o Guia de Autenticação.

Boas Práticas

  1. Gere sempre pela API — um BR Code montado por fora custa a conciliação, o lookup e o cancelamento
  2. Dê nome ao ponto de cobrança — o identifier do QR estático identifica a placa/caixa/campanha, não a venda
  3. Deduplique pelo endToEnd — é o que distingue um pagamento do outro no mesmo QR
  4. Não confie só no lookup — ele mostra o último pagamento; o histórico está nos webhooks e no extrato
  5. Cancele o que saiu de circulação — QR estático não expira sozinho
  6. Prefira valor fixo quando houver limite de política — QR aberto pula as regras de minAmount/maxAmount

Erros Comuns

Os erros vêm no formato { "errorCode": "...", "message": "..." }:

errorCodeHTTPCausaSolução
missing_field400pixKey ausenteEnvie uma chave cadastrada na conta
invalid_identifier400identifier acima de 38 caracteres, fora do charset [A-Za-z0-9._-] ou começando com fee-Ajuste o identificador
invalid_field400message acima de 140 caracteresEncurte a mensagem
invalid_payload400JSON malformadoCorrija o corpo da requisição
missing_param400Lookup ou cancelamento sem identifier na queryInforme ?identifier= (alias: txid)
not_found404QR não encontrado no lookupVerifique o identifier e o accountId
policy_denied422Uma regra da seção qrCode recusou a criaçãoVeja violations no corpo — Políticas e Regras
partner_unavailable503Liquidante indisponívelRepita a chamada

Exemplo:

{
"errorCode": "missing_field",
"message": "pixKey is required"
}

Lista completa em Erros.

Próximos Passos