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.
É 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 API | BR Code montado por você | |
|---|---|---|
Webhook qrcode.paid | Recebido, com o seu identifier | Recebido, com o txid que você embutiu — ou com o campo vazio |
Webhook pix.in.completed | Recebido, com o seu identifier | Idem |
| Extrato | Lançamento traz identifier | Só traz identificador se você embutiu um txid |
| Lookup do QR | GET .../pix/qr-code/lookup?identifier= funciona | Não existe QR para consultar |
| Cancelamento | DELETE tira o código de circulação | Não há o que cancelar pela API |
| Políticas | Regras da seção qrCode são avaliadas na criação | Nenhuma regra é avaliada |
| Colisão de identificador | identifier validado e registrado | O 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.
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ático | Dinâmico | |
|---|---|---|
| Rota | POST .../pix/qr-code/static | POST .../pix/qr-code/dynamic |
| Valor | Opcional (omitir = QR aberto) | Obrigatório, maior que zero |
| Expiração | Não expira | expirationDate (default do liquidante: 1 dia) |
| Pagamentos | Vários, no mesmo código | Um |
| Restringir o pagador | Não | allowedPayerTaxNumber |
| Webhook de expiração | Não se aplica | qrcode.expired |
| Uso típico | Balcão, doação, mensalidade | Checkout, 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
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
pixKey | string | Sim | Chave PIX da conta recebedora (deve estar cadastrada) |
value | number | Não | Valor fixo em BRL. Omitido ou 0 cria um QR aberto, em que o pagador digita o valor |
identifier | string | Recomendado | Seu 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 |
message | string | Não | Mensagem de até 140 caracteres. Ver o aviso abaixo |
message não chega ao BR Code estáticoO 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.
identifier que signifique algoO 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"
}
dataA 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.
| Campo | Descrição |
|---|---|
emv | Código PIX Copia e Cola (string EMV). Renderize como QR |
identifier | O identificador que você enviou (ou o gerado pela API) |
txid | Ecoa o identifier — em QR estático os dois são o mesmo valor |
type | Sempre static neste endpoint |
status | ACTIVE na criação |
value | Valor fixo, ou 0 quando o QR é aberto |
allowChange | true 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.
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.
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.
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:
| Regra | rule | Efeito |
|---|---|---|
| Criação de estático | qrCode.canCreateStatic | false recusa a criação |
| Valor mínimo | qrCode.minAmount | Recusa QR com valor abaixo do mínimo |
| Valor máximo | qrCode.maxAmount | Recusa 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ção | Escopo |
|---|---|
| Criar QR estático | qrcode.manage |
| Consultar e cancelar | qrcode.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
- Gere sempre pela API — um BR Code montado por fora custa a conciliação, o lookup e o cancelamento
- Dê nome ao ponto de cobrança — o
identifierdo QR estático identifica a placa/caixa/campanha, não a venda - Deduplique pelo
endToEnd— é o que distingue um pagamento do outro no mesmo QR - Não confie só no lookup — ele mostra o último pagamento; o histórico está nos webhooks e no extrato
- Cancele o que saiu de circulação — QR estático não expira sozinho
- 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": "..." }:
errorCode | HTTP | Causa | Solução |
|---|---|---|---|
missing_field | 400 | pixKey ausente | Envie uma chave cadastrada na conta |
invalid_identifier | 400 | identifier acima de 38 caracteres, fora do charset [A-Za-z0-9._-] ou começando com fee- | Ajuste o identificador |
invalid_field | 400 | message acima de 140 caracteres | Encurte a mensagem |
invalid_payload | 400 | JSON malformado | Corrija o corpo da requisição |
missing_param | 400 | Lookup ou cancelamento sem identifier na query | Informe ?identifier= (alias: txid) |
not_found | 404 | QR não encontrado no lookup | Verifique o identifier e o accountId |
policy_denied | 422 | Uma regra da seção qrCode recusou a criação | Veja violations no corpo — Políticas e Regras |
partner_unavailable | 503 | Liquidante indisponível | Repita a chamada |
Exemplo:
{
"errorCode": "missing_field",
"message": "pixKey is required"
}
Lista completa em Erros.
Próximos Passos
- Guia de QR Code Dinâmico - Cobranças únicas com valor e expiração
- Guia de Devolução - Devolver um pagamento recebido
- Webhooks - Configurar as notificações