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
| Field | Type | Required | Description |
|---|---|---|---|
pixKey | string | Yes | PIX key of the receiving account (must be registered) |
value | number | Yes | Charge amount in BRL, greater than zero. Max 2 decimal places (e.g., 150.75) |
expirationDate | datetime | Recommended | Expiration date/time (RFC3339, e.g., 2026-02-10T15:30:00Z). Without it the settlement bank default applies (MT: 1 day) |
identifier | string | Recommended | Unique charge identifier (max 38 characters, charset [A-Za-z0-9._-]). If omitted, the API generates a UUID without dashes |
message | string | No | Message displayed to the payer (max 140 characters) |
allowedPayerTaxNumber | string | No | Specific CPF/CNPJ authorized to pay |
Important Headers
| Header | Description |
|---|---|
Idempotency-Key | Unique 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"
}
data envelopeThe 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).
| Field | Description |
|---|---|
emv | PIX Copy and Paste code (EMV string). Render it as the QR. |
txid | Internal transaction ID |
identifier | Unique charge ID (use for lookups) |
status | Charge status (ACTIVE = active) |
pixKey | PIX key used for the charge |
value | Charge amount in BRL |
expiresAt | Expiration 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>
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:
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
| Status | Description |
|---|---|
ACTIVE | Created / awaiting |
AWAITING-PAYMENT | Awaiting payment |
PAID | Payment confirmed |
CANCELLED | Cancelled / rejected |
EXPIRED | Expired without payment |
UNKNOWN | Partner status outside the known mapping |
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"
}
Paid (payment confirmed)
{
"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
| Field | Present | Description |
|---|---|---|
txid | Always | Internal QR code transaction ID |
emv | Always | PIX Copy and Paste code (BR Code) |
type | Always | QR code type (dynamic or static) |
status | Always | Current status (ACTIVE, AWAITING-PAYMENT, PAID, CANCELLED, EXPIRED, UNKNOWN) |
value | Always | Charge amount in BRL |
identifier | Always | Your unique charge identifier |
accountId | Always | Account that owns the charge |
pixKey | When available | PIX key of the charge |
createdAt | When available | Creation timestamp |
expiresAt | When available | Expiration date (dynamic QR) |
paidAt | When paid | Payment timestamp |
paidAmount | When paid | Amount actually paid in BRL |
endToEndId | When paid | PIX E2E identifier |
payer | When paid | Payer details (name, document, bankIspb, bankName, branch, account, pixKey) |
payee | When available | Receiver (your account) details |
Lookup Endpoint (advanced search)
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}"
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"
}
}
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
- Use Idempotency Key - Always send a unique identifier per charge to prevent duplicates
- Set appropriate expiration - 30 minutes for checkout, 24h for invoices
- Configure webhooks - Don't rely solely on polling; use
qrcode.paidevents - All amounts in BRL - Use max 2 decimal places (e.g.,
150.75for R$150,75) - Handle expiration - Notify the customer when the charge expires
- Store the identifier - Use it to check status and reconcile payments
Common Errors
Errors come in the { "errorCode": "...", "message": "..." } shape:
errorCode | HTTP | Cause | Solution |
|---|---|---|---|
missing_field | 400 | Missing pixKey or value less than or equal to zero | Send a registered key and a positive BRL amount |
invalid_identifier | 400 | identifier outside the [A-Za-z0-9._-] charset or longer than 38 characters | Adjust the identifier |
invalid_field | 400 | message longer than 140 characters or with unsupported characters | Shorten the message |
invalid_payload | 400 | Malformed JSON, expirationDate not RFC3339 or in the past | Fix the request body |
missing_param | 400 | Lookup/cancel without identifier in the query | Provide ?identifier= (alias: txid) |
not_found | 404 | Charge not found | Check the identifier and accountId |
policy_denied | 422 | A policy rule refused the charge creation | Check violations in the body — Policies and Rules |
Example:
{
"errorCode": "missing_field",
"message": "pixKey and positive value are required"
}
Next Steps
- Cash Out Guide - Make PIX transfers
- Refund Guide - Reverse received payments
- Webhooks - Configure notifications