How to make QR codes for event tickets
Everything else in this cluster prints a pointer: a code that opens a menu, a listing, a review page. A ticket is different — the code on it is not a pointer to the valuable thing, it is the valuable thing. Whoever presents it gets a seat. That one property decides every choice below, and none of the answers require a ticketing platform, a subscription, or even a network connection at the door.
The five-minute version
- Make a spreadsheet with one row per ticket: a random, opaque
ticket_id(a formula generates it — below), and a caption column with the holder’s name or seat. - Generate one static QR code per row in a batch. Place each code on its ticket — printed, or attached to a confirmation email.
- The same spreadsheet is your door list. Put it on the phone that scans, so it works with the venue wifi down.
- At the door: scan, find the ID, mark it used. First scan wins — a second presentation of the same ID is refused, which is the entire anti-copying mechanism.
- Size the printed code at 2.5 cm or more; test a phone-screen ticket at half brightness before the day.
A ticket is not a coupon
The nearest neighbour in this cluster is the coupon guide: both print one single-use code per piece of paper, both generate them from a spreadsheet, both keep validity in a list rather than in the code. But a copied coupon costs you a discount; a copied ticket costs a paying guest their seat, in person, at your door, with a queue behind them. A ticket is a bearer instrument — like a banknote, it belongs to whoever is holding it — so the stakes of duplication are higher and the door procedure, not the code, is what carries them.
The temptation this raises is to reach for a “secure” dynamic ticketing code, and it misreads where the security can live. The static-vs-dynamic guide concedes that a destination which has to change is real dynamic value — but a ticket’s destination never changes, and often there is no destination at all, just an ID your own scanner looks up. What changes is the ticket’s validity: unused, used, refunded. That is the coupon guide’s move exactly — the state lives in your system and is decided at check time, not at scan time — and it needs no vendor in the middle.
You cannot stop copying, so make copies worthless
Any QR code can be screenshotted, photographed over a shoulder, or forwarded. No amount of styling, encryption talk or vendor “secure code” branding changes this: if a camera can read it, a camera can copy it. Every real ticketing system — airlines included — accepts this and solves it one step later.
First scan wins. The code only carries an ID. The door list carries the truth. The first time an ID is scanned, it is marked used; every subsequent scan of that ID — screenshot, forward, reprint — finds a crossed-off row and is refused. A copy is not prevented; it is worthless. The person refused is asked to step aside, and the one case that genuinely occurs — a buyer whose ticket was copied by the person they forwarded it to — is resolved by the name in the caption column, which the copier does not match.
Notice what this buys: the entire security of the system lives in a spreadsheet you own, on a device in your pocket, with no subscription and nothing to expire while printed tickets are in circulation — the failure the expiry guide documents for rented codes generally.
What to encode
- A random, opaque ID — never a sequential one. Ticket
0042tells everyone that0041and0043exist, and a guessed ID is a forged ticket. The coupon guide has the spreadsheet recipe: a formula that generates codes likeTKT-K7RM2Q, paste-as-values so they stop changing, and a COUNTIF check for duplicates before you generate. - Plain ID or URL? Decide by who scans it. The packaging guide’s rule: a plain ID is enough when the scanner is your own staff with your own list — which the door is — so
TKT-K7RM2Qalone works, exactly as it does on the asset tags. Encode a URL on a domain you own (yourevent.com/t/K7RM2Q) only if you want the holder’s scan to show event details before the day; the door then matches the tail. Never encode a ticket vendor’s redirect — that adds a landlord to your own front door. - Nothing but the ID in the pattern. The seat, the price paid, the ticket class all live in the row, where you can correct them after printing. Data in the pattern is frozen at print time and readable by anyone — the same reasoning the name-badges guide uses for keeping the person out of the code.
The batch is a spreadsheet
Three hundred tickets is three hundred rows, which makes this a spreadsheet job like the rest of the paid cluster — and the spreadsheet does double duty here, because the file that generates the codes is the file the door checks against.
- Columns:
ticket_id(random, the code content), a caption — holder name for named tickets, seat or just the ID for open ones — and whatever the door wants at a glance: ticket class, table, plus-ones. - Export a ZIP of PNGs named by caption when each code goes into a designed ticket or a mail merge; a print sheet when the codes with captions are the tickets, cut into strips — fine for a fundraiser, a school play, a members’ night.
- Because the codes are static, a re-send is the same PNG attached to a new email, and a reprint is pixel-identical. A refund is one row marked refunded in the list — the printed code dies without being touched.
- Add door-list columns before the day:
usedandtime_in. Empty until the door fills them.
The door
- The list lives on the scanning phone. The name-badges guide’s argument holds with more force here: venue wifi dies at doors time, and a door that cannot admit anyone is worse than a slow one. A spreadsheet app with the file stored locally is enough — scan with the camera, tap the ID into the search box if the scanner app doesn’t paste it, mark the row.
- Two doors, two halves. If two staff scan against separate local copies, split the list alphabetically or by ticket class so no ID can be admitted twice — one list, one owner. A shared online sheet is fine only as long as you would bet the queue on the connection.
- Refusals are a procedure, not an argument. Already-used ID: step aside, check the caption name against who’s holding it. Unknown ID: it was never issued — that is a forgery or a wrong event, not a lookup failure.
- The paper fallback. Print the list sorted by caption. If every phone fails, the door falls back to a name on a clipboard — degraded, but moving.
Sizes: paper and screens
- Printed tickets are scanned in the hand at 25–30 cm, so the one-tenth rule says 2.5–3 cm across — comfortable on any ticket. A short random ID makes a sparse code, so 2 cm still scans if the design is tight. On thermal receipt stock, the coupon guide’s constraints apply: darkest density, code across the paper width, no lamination shine.
- Phone-screen tickets (a PNG in a confirmation email) fail two ways the print never does: low screen brightness — test a scan at half brightness, and tell staff the fix is asking the guest to brighten, not re-scanning — and email clients that shrink images: attach the PNG at full size rather than inlining it small. The screen guide covers the rest of that path, including the dark-mode inversion that a transparent PNG invites.
- Home-printed A4 is the normal case for emailed tickets and is the easiest scan in the room; a greyscale inkjet at draft quality still reads. The not-scanning guide covers the rest.
When you should use a platform instead
Honesty about the boundary: this setup issues tickets, it does not sell them. If you need online card sales to strangers, automatic refunds, resale, or thousands through the door in an hour, a ticketing platform is doing real work for its fee, and the restaurant-tables guide’s landlord reasoning applies — a platform you have signed with is a dependency you chose, and its codes are its concern. The spreadsheet method is for the events below that line: tickets paid by bank transfer, at the till, or free-but-limited; a few hundred people; one or two doors. That is most fundraisers, club nights, school productions and members’ events — which is to say, most events.
Before doors open
- Scan three tickets from the batch — first, last, middle — with the actual door phone, and confirm each ID finds its row.
- Run the duplicate check on
ticket_idafter the final additions; late rows are where duplicates sneak in. - Rehearse a refusal: scan one ticket twice and make sure the second scan is visibly, unambiguously a “used” row on the door device.
- Mark test-scanned rows back to unused, and mark refunded rows refunded — the door list ships with exactly the truth.
- Store the CSV with the PNGs afterwards. Next year’s event is the same sheet with new IDs — regenerate, never reuse, because last year’s tickets are still in the world.