# The PIX CRC16 checksum, and why hand-edited codes always fail

> PIX uses CRC16/CCITT-FALSE (polynomial 0x1021, initial value 0xFFFF, no reflection, no final XOR) computed over the entire payload including the literal 6304 prefix, and written as four uppercase hex digits in field 63. Any edit made without recomputing the checksum makes every banking app reject the code.

Source: https://useqr.app/docs/payments/pix-crc16-checksum · Last reviewed 2026-08-21 · UseQR is free forever, no signup.

---

## The parameters

Field `63` of every [BR Code](/docs/payments/pix-qr-code-format) is a CRC16 in the
CCITT-FALSE configuration:

| Parameter | Value |
|---|---|
| Polynomial | `0x1021` |
| Initial value | `0xFFFF` |
| Input/output reflection | None |
| Final XOR | None |
| Output | 4 uppercase hex digits |

This is the same CRC family used across [EMVCo merchant-presented
codes](/docs/payments/emvco-merchant-presented-qr), PIX inherited it from the EMVCo base
spec rather than inventing it. See [CRC16](/glossary/crc16) for the general concept.

## How it runs

The algorithm walks the payload one byte at a time. Each byte is XORed into the high byte of
a 16-bit register, then the register is shifted left eight times; whenever the top bit is
set before a shift, the register is XORed with `0x1021`. After the last byte, the register
value (printed as four uppercase hex digits) is the checksum. It is about ten lines in
any language, and the worked example below is the fastest way to check yours against a
known-good answer.

## The detail that catches implementers

The CRC is computed over the payload **including the literal `6304` field header**, as if
the four checksum digits themselves were simply absent:

```
input  = 000201...6207 0503 *** 6304
crc    = CRC16(input)          → e.g. A13F
output = input + "A13F"
```

The single most common implementation bug is computing the CRC over everything *before*
`6304`. The result is a well-formed string with a checksum that never matches, and a code
that no app in Brazil will accept. If your generated codes fail in every app, check this
first.

## Why a hand-edited PIX string always fails

People try to edit a working copia e cola string (swap the amount, change the name),
without regenerating it. This cannot work. The checksum was computed over the old bytes;
after any edit it no longer matches, and apps reject the whole string. For a random edit,
the chance the old checksum happens to remain valid is **1 in 65,536**.

That is by design. The CRC exists to catch corruption: a mangled character in a
[copied-and-pasted string](/docs/payments/pix-copia-e-cola), a misdecoded module in a
damaged print. A payments payload that tolerated single-character changes would be a fraud
vector, not a convenience.

Also note the interaction with field lengths: every TLV field carries a two-digit byte
length, so a non-ASCII character (whose UTF-8 encoding is more than one byte) silently breaks
the length counting, which changes the bytes, which breaks the CRC. Transliterate names
first, `São Paulo` must become `Sao Paulo`.

## Verifying a code

Three ways, in increasing rigour:

1. Decode the code with [/validate](/validate) and confirm the payload parses.
2. Regenerate the payload from the same inputs in the [PIX builder](/pix) and compare
   strings: same inputs must produce the identical string, checksum included.
3. Call `GET https://useqr.app/api/v1/verify?data=…`, which renders the code and decodes it
   back, proving the printed artefact will scan.

And always, before printing: scan with a real banking app and check the name and amount it
displays.

## FAQ

### What CRC does a PIX QR code use?
CRC16/CCITT-FALSE (polynomial 0x1021, initial value 0xFFFF, no input or output reflection,
no final XOR) output as four uppercase hexadecimal digits in field 63, computed over the
whole payload including the literal 6304 prefix.

### Why is my generated PIX code rejected by every app?
Most often the CRC was computed over the payload excluding the 6304 field header, or a
non-ASCII character broke the field-length counting. Both produce a checksum mismatch, and
apps reject mismatched codes without a specific error.

### Can I edit a PIX string to change the amount?
Not by editing text. Any change invalidates the CRC16 and the string will be rejected.
Regenerate the payload with the new amount so the checksum is recomputed over the new bytes.

### Does the CRC protect against fraud?
It protects against corruption, not substitution. A fraudster can generate a fully valid
code pointing at their own key. The fraud defence is the payee name shown on the
confirmation screen, not the checksum.

## Try it

- https://useqr.app/pix
- https://useqr.app/validate
