Ballot QR Code Data Format
Last updated
Last updated
Both hand-marked paper ballots and machine-marked ballots contain QR codes. For hand-marked paper ballots, the QR code contains only metadata about the ballot. For machine-marked ballots, the QR code contains both metadata and voter choices.
In order to make the information as compact as possible, information is coded as binary values that reference the election definition. For example, instead of encoding the precinct name (e.g. "Fire Station Precinct") the QR code contains the index of the precinct within the . If the precinct were the first precinct listed in the election definition, the value would be 0. If it were the second precinct listed in the election definition, the value would be 1. The binary sequence is ultimately encoded into the QR code as Base64 for broad compatibility with devices that scan QR codes.
Take following steps to decode a barcode manually and, in the case of a machine-marked ballot, manually verify the encoded selections are correct:
Scan the QR code with a smartphone camera or barcode reader. If your smartphone does not automatically detect a value, you may use any freely available QR code scanning app.
Copy and paste the scanned value into a Base64 to binary converter. There are many freely available online, such as .
Inspect the binary values and compare them to the encodings described below. In order to make sense of the values, you will need the associated available for comparison.
You may use this process to verify that the encoded information in the ballot QR code matches the information presented on the ballot to human readers.
An easier, recommended approach is to simply scan the ballot(s) at VxScan or VxCentralScan. You can confirm the interpretation (and therefore in most cases, the QR code) is correct by looking at a results report. You can also inspect the Cast Vote Records exported from the scanner, which contain the images and interpretation for each ballot, to confirm that the metadata and voter selections were encoded and decoded correctly, similar to an .
HMPB Prelude
24 bits
The characters "VP2," which in binary appear as: 01010110 01010000 00000010
Ballot Hash
80 bits
Ballot Config
Variable
Padding
0 - 7 bits
To ensure that the value in the QR code is composed of whole bytes, 0 bits are added until the data ends in a whole byte.
Precinct Index
13 bits
The index of the ballot's precinct in the election definition's list of precincts.
Ballot Style Index
13 bits
The index of the ballot's ballot style in the election definition's list of ballot styles.
Page Number
5 bits
HMPB only. Encodes the page number of the ballot, one-indexed.
Test Ballot?
1 bit
When true, indicates a test ballot. When false, indicates an official ballot.
Ballot Type
4 bits
Indicates the ballot type - absentee ballots are 0001
and precinct ballots are 0000
Ballot ID Set?
1 bit
When true, indicates that the encoded ballot has an assigned ID that follows this bit. When false, indicates there is no ID.
Ballot ID
Variable, max 256 bytes
Optional, HMPB only. Identifier for the ballot, UTF-8 encoding.
Unlike the HMPB QR code, the BMD QR code actually encodes votes.
BMD Prelude
24 bits
The characters "VX2," which in binary appear as: 01010110 01011000 00000010
Ballot Hash
80 bits
Ballot Config
Variable
Vote "Roll Call"
Variable, 1 bit per contest in the election definition
Each bit corresponds to one contest and indicates whether the voter made any selections. For example, with four contests, 1010
would indicate votes in the first and third contests but not the second and fourth.
Vote Data
Variable
In sequence, all the vote data for the contests indicated in the "roll call."
For yes-no contests, a set bit 1
indicates the "yes option" whereas an unset bit 0
indicates the "no option."
For candidate contests, includes a bit for each candidate in the contest definition in the order of the contest definition. 1
is a vote and 0
is a non-vote. If the contest allows write-ins (meaning allowWriteIns
is true in the election definition), there will be additional data here, described below.
Padding
0 - 7 bits
To ensure that the value in the QR code is composed of whole bytes, 0 bits are added until the data ends in a whole byte.
If the contest does not allow write-ins (allowWriteIns = false
) then the candidate contest data ends after the bits for the list of candidates. If the contest does allow write-ins (allowWriteIns = true
), then write-in data will follow the candidate roll call and start with the number of write-ins. The maximum number of write-ins is the difference between the number of possible votes in the contest (e.g. is it a vote for one or vote for three contest) and the number of votes used on non-write-in candidates. The number of write-ins is written with the number of bits that would be required to write the maximum number of write-ins. For example, if a contest is "Vote for Five" and the ballot already contains two votes, the maximum number of votes is three. Three takes 2 bits to represent, so the number of write-ins will be 11
, 10
, 01
, or 00
for 3, 2, 1, or 0 respectively.
After the number of write-ins, the write-ins are written in series, with 6 bits containing the length of the write-in, in bytes, and then the write-in itself in however many bytes after that. The bytes are in UTF-8 format.
See . The 80 bits represent 20 4-bit hexadecimal characters.
See .
See . The election's ballot hash is visible on most authenticated screens on all apps. The 80 bits represent 20 4-bit hexadecimal characters.
See .
HMPB and BMD QR codes are with .
The source code and further technical documentation of how ballot QR codes are encoded and decoded can be found in the .