Skip to main content

Dynamic QR Code Guide

This guide explains how to generate PIX charges using dynamic QR Codes.

Overview

The Dynamic QR Code allows you to create unique charges with a defined amount, expiration date, and payer information. It is ideal for:

  • E-commerce - Order payments
  • Billing - Invoices and bills
  • Services - Payment for rendered services

For a reusable code that accepts multiple payments and never expires — a sign on the counter, a donation, a monthly fee — see the Static QR Code Guide.

Integration Flow

┌─────────────┐      ┌─────────────┐      ┌─────────────┐
│ Create │ │ Customer │ │ Webhook │
│ Charge │ ───► │ Pays QR │ ───► │ Received │
└─────────────┘ └─────────────┘ └─────────────┘
│ │
▼ ▼
Returns EMV Confirms
and payload Payment

Step 1: Create a Charge (Dynamic QR Code)

Request

curl -X POST "https://tenant.api.corpx.com/v1/accounts/{accountId}/pix/qr-code/dynamic" \
-H "Authorization: Bearer {token}" \
-H "X-Tenant-Id: {tenant_id}" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-12345" \
-d '{
"pixKey": "your-pix-key-here",
"value": 150.75,
"expirationDate": "2026-02-10T15:30:00Z",
"identifier": "order-12345",
"message": "Payment for order #12345"
}'

Body Parameters

FieldTypeRequiredDescription
pixKeystringYesPIX key of the receiving account (must be registered)
valuenumberYesCharge amount in BRL, greater than zero. Max 2 decimal places (e.g., 150.75)
expirationDatedatetimeRecommendedExpiration date/time (RFC3339, e.g., 2026-02-10T15:30:00Z). Without it the settlement bank default applies (MT: 1 day)
identifierstringRecommendedUnique charge identifier (max 38 characters, charset [A-Za-z0-9._-]). If omitted, the API generates a UUID without dashes
messagestringNoMessage displayed to the payer (max 140 characters)
allowedPayerTaxNumberstringNoSpecific CPF/CNPJ authorized to pay

Important Headers

HeaderDescription
Idempotency-KeyUnique identifier to prevent duplicate charges

Success Response (201 Created)

{
"txid": "88a38a908a92441a98dac228ee1d9507",
"emv": "00020101021226790014br.gov.bcb.pix2557brcode.starkinfra.com/v2/88a38a908a92441a98dac228ee1d95075204000053039865802BR5912iDez Digital6004Lins62070503***63040BFF",
"type": "dynamic",
"status": "ACTIVE",
"value": 150.75,
"message": "Payment for order #12345",
"description": "Payment for order #12345",
"identifier": "order-12345",
"accountId": "{accountId}",
"pixKey": "8026ff12-cb4a-4d19-9638-08bb15d450e2",
"allowChange": false,
"createdAt": "2026-02-10T12:00:00Z",
"expiresAt": "2026-02-10T15:30:00Z"
}
No data envelope

The response is a flat object (top-level fields). There is no data envelope and no statusCode/title/message fields. The copy-and-paste code is in emv (not data.payload) and the key is in pixKey (not data.chave).

FieldDescription
emvPIX Copy and Paste code (EMV string). Render it as the QR.
txidInternal transaction ID
identifierUnique charge ID (use for lookups)
statusCharge status (ACTIVE = active)
pixKeyPIX key used for the charge
valueCharge amount in BRL
expiresAtExpiration date/time (RFC3339)

Step 2: Display the QR Code

Using PIX Copy and Paste

Display the emv field for the customer to copy:

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

You can generate a QR code image from the emv string using any QR code library (e.g., qrcode.js, python-qrcode).

Step 3: Check Charge Status

Poll the status of a charge by its identifier:

QR-code-only credential

The qrcode.manage scope covers creating, cancelling and reading the QR: a credential dedicated to charges can see whether the QR was paid without the general query scope (read), which also grants balance and statement. See the Authentication Guide.

Request

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

Possible Statuses

StatusDescription
ACTIVECreated / awaiting
AWAITING-PAYMENTAwaiting payment
PAIDPayment confirmed
CANCELLEDCancelled / rejected
EXPIREDExpired without payment
UNKNOWNPartner status outside the known mapping
Uppercase values

The status is returned in UPPERCASE (canonical). A PIX refund does not change the QR status to "refunded" — the QR stays PAID and the refund is tracked via the transaction/statement.

Response Examples by Status

Active (awaiting payment)

{
"txid": "88a38a908a92441a98dac228ee1d9507",
"emv": "00020101021226790014br.gov.bcb.pix2557brcode.starkinfra.com/v2/88a38a90...",
"type": "dynamic",
"status": "ACTIVE",
"value": 150.75,
"description": "Payment for order #12345",
"identifier": "order-12345",
"accountId": "6ff57bc1-e4a9-403b-be62-42378b8aafd7",
"pixKey": "8026ff12-cb4a-4d19-9638-08bb15d450e2",
"createdAt": "2026-02-09T01:50:36Z",
"expiresAt": "2026-02-09T02:50:34Z"
}
{
"txid": "b091da7bba6a45d1a9f709daaba04e30",
"emv": "00020101021226790014br.gov.bcb.pix...",
"type": "dynamic",
"status": "PAID",
"value": 150.75,
"message": "Payment for order #12345",
"description": "Payment for order #12345",
"identifier": "order-12345",
"accountId": "6ff57bc1-e4a9-403b-be62-42378b8aafd7",
"pixKey": "8026ff12-cb4a-4d19-9638-08bb15d450e2",
"allowChange": false,
"createdAt": "2026-02-05T22:08:02Z",
"expiresAt": "2026-02-05T23:08:00Z",
"paidAt": "2026-02-05T22:10:25Z",
"paidAmount": 150.75,
"endToEndId": "E303062942026020522100000005EXVX",
"payer": {
"name": "John Smith",
"document": "12345678901",
"bankIspb": "30306294",
"bankName": "BANCO EXEMPLO",
"branch": "0020",
"account": "004912314"
},
"payee": {
"name": "iDez Digital",
"document": "12345678000190",
"bankIspb": "50871921",
"bankCode": "681"
}
}

After a refund

A PIX refund (POST /v1/accounts/{accountId}/pix/out/refund) does not change the QR status to "refunded" — the QR stays PAID. The refund is a separate transaction; track it via the statement (GET .../statement) or the payment lookup (GET .../pix/payments/lookup). The QR lookup does not return refund fields.

Expired (charge expired)

{
"txid": "c1234567890abcdef1234567890abcde",
"emv": "00020101021226790014br.gov.bcb.pix...",
"type": "dynamic",
"status": "EXPIRED",
"value": 50.00,
"identifier": "order-99999",
"accountId": "6ff57bc1-e4a9-403b-be62-42378b8aafd7",
"pixKey": "8026ff12-cb4a-4d19-9638-08bb15d450e2",
"createdAt": "2026-02-01T10:00:00Z",
"expiresAt": "2026-02-01T10:30:00Z"
}

Field Reference

FieldPresentDescription
txidAlwaysInternal QR code transaction ID
emvAlwaysPIX Copy and Paste code (BR Code)
typeAlwaysQR code type (dynamic or static)
statusAlwaysCurrent status (ACTIVE, AWAITING-PAYMENT, PAID, CANCELLED, EXPIRED, UNKNOWN)
valueAlwaysCharge amount in BRL
identifierAlwaysYour unique charge identifier
accountIdAlwaysAccount that owns the charge
pixKeyWhen availablePIX key of the charge
createdAtWhen availableCreation timestamp
expiresAtWhen availableExpiration date (dynamic QR)
paidAtWhen paidPayment timestamp
paidAmountWhen paidAmount actually paid in BRL
endToEndIdWhen paidPIX E2E identifier
payerWhen paidPayer details (name, document, bankIspb, bankName, branch, account, pixKey)
payeeWhen availableReceiver (your account) details

The lookup is done by identifier (backward-compat alias: txid):

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

The lookup is resolved in real time at the partner and returns the flat QR object (status, value, emv and — when paid — payer/payee/endToEndId). This endpoint does not accept qrcodeId/endToEndId and does not return transaction/fees/refund data.

Step 4: Receive Payment Webhook

When the customer pays, you receive a qrcode.paid webhook in the canonical envelope (id, type, occurredAt, schemaVersion, data):

{
"id": "evt_987654321",
"type": "qrcode.paid",
"occurredAt": "2026-02-05T22:10:25.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-yourcompany",
"accountId": "{accountId}",
"data": {
"endToEnd": "E303062942026020522100000005EXVX",
"type": "dynamic",
"identifier": "order-12345",
"qrcodeId": "b091da7bba6a45d1a9f709daaba04e30",
"amount": 150.75,
"payer": {
"name": "John Smith",
"document": "12345678901",
"bankCode": "033",
"branch": "0001",
"accountNumber": "54321-0"
},
"payee": {
"name": "iDez Digital",
"document": "12345678000190"
},
"receivedAt": "2026-02-05T22:10:25.000Z"
}
}
tip

Configure webhooks via POST /v1/webhooks with event type qrcode.paid. See the Webhooks Guide.

Step 5: Cancel a Charge (Optional)

Cancel a pending charge before it's paid:

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

Response (200 OK):

{
"identifier": "order-12345",
"status": "CANCELLED"
}

Step 6: QR Code Metrics (optional)

There is no list endpoint for QR codes — lookups are always by identifier. For per-day aggregated metrics (generated/paid/refunded/value), use:

curl -X GET "https://tenant.api.corpx.com/v1/accounts/{accountId}/pix/qr-codes/stats?days=30" \
-H "Authorization: Bearer {token}" \
-H "X-Tenant-Id: {tenant_id}"

Full Example: E-commerce Integration

#!/bin/bash

# Configuration
API_URL="https://tenant.api.corpx.com"
TOKEN="your_token_here"
TENANT_ID="tenant-yourcompany"
ACCOUNT_ID="your-account-id"
PIX_KEY="your-pix-key"

# 1. Create charge for order
ORDER_ID="order-$(date +%s)"
AMOUNT=299.90
EXPIRATION=$(date -u -v+30M +"%Y-%m-%dT%H:%M:%SZ") # 30 minutes

echo "Creating charge: $ORDER_ID for R\$$AMOUNT..."

response=$(curl -s -X POST "$API_URL/v1/accounts/$ACCOUNT_ID/pix/qr-code/dynamic" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Tenant-Id: $TENANT_ID" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $ORDER_ID" \
-d "{
\"pixKey\": \"$PIX_KEY\",
\"value\": $AMOUNT,
\"expirationDate\": \"$EXPIRATION\",
\"identifier\": \"$ORDER_ID\",
\"message\": \"Store Purchase - $ORDER_ID\"
}")

# 2. Extract information
IDENTIFIER=$(echo $response | jq -r '.identifier')
EMV=$(echo $response | jq -r '.emv')

echo "Charge created!"
echo "ID: $IDENTIFIER"
echo "PIX Copy and Paste: $EMV"

# 3. Poll for payment (alternative to webhook)
while true; do
sleep 10

status_response=$(curl -s "$API_URL/v1/accounts/$ACCOUNT_ID/pix/qr-code/lookup?identifier=$IDENTIFIER" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Tenant-Id: $TENANT_ID")

current_status=$(echo $status_response | jq -r '.status')

if [ "$current_status" = "PAID" ]; then
paid_amount=$(echo $status_response | jq -r '.paidAmount')
e2e=$(echo $status_response | jq -r '.endToEndId')
echo "Payment confirmed! Amount: R\$$paid_amount, E2E: $e2e"
break
elif [ "$current_status" = "EXPIRED" ]; then
echo "Charge expired"
break
fi

echo "Awaiting payment... Status: $current_status"
done

Best Practices

  1. Use Idempotency Key - Always send a unique identifier per charge to prevent duplicates
  2. Set appropriate expiration - 30 minutes for checkout, 24h for invoices
  3. Configure webhooks - Don't rely solely on polling; use qrcode.paid events
  4. All amounts in BRL - Use max 2 decimal places (e.g., 150.75 for R$150,75)
  5. Handle expiration - Notify the customer when the charge expires
  6. Store the identifier - Use it to check status and reconcile payments

Common Errors

Errors come in the { "errorCode": "...", "message": "..." } shape:

errorCodeHTTPCauseSolution
missing_field400Missing pixKey or value less than or equal to zeroSend a registered key and a positive BRL amount
invalid_identifier400identifier outside the [A-Za-z0-9._-] charset or longer than 38 charactersAdjust the identifier
invalid_field400message longer than 140 characters or with unsupported charactersShorten the message
invalid_payload400Malformed JSON, expirationDate not RFC3339 or in the pastFix the request body
missing_param400Lookup/cancel without identifier in the queryProvide ?identifier= (alias: txid)
not_found404Charge not foundCheck the identifier and accountId
policy_denied422A policy rule refused the charge creationCheck violations in the body — Policies and Rules

Example:

{
"errorCode": "missing_field",
"message": "pixKey and positive value are required"
}

Next Steps