A QR code carries a string, and the string has to be right
A QR symbol carries bytes. What a reader can do with them depends on the text inside following a recognised format, so the QR maker has one builder per payload type. Each validates your fields and returns either the exact string the encoder receives or a list of errors tied to the fields that are wrong; nothing is silently repaired. Every string and error below is the engine’s own output on fixed, fictional inputs, produced at build. The formats follow the sources the engine cites — the ZXing Barcode Contents wiki for Wi-Fi, IETF RFCs for contact, email, phone, SMS, location and calendar, and the WhatsApp Help Center for wa.me — re-checked against their official text between 2026-10-02 and 2026-10-05. Verify against the standards’ own documentation before a critical print run or launch.
1. Website URL: validated, never rewritten
The URL builder validates your link but never rewrites it (only surrounding whitespace is trimmed): no shortening, no parameters added or stripped. The fictional https://example.com/autumn-launch is encoded as exactly https://example.com/autumn-launch. It refuses three inputs that would each make a code that does not act like a link:
- No scheme, example.com/autumn-launch: “Include the protocol (e.g. https://) so scanners recognize the link.”
- A scheme other than http or https, ftp://example.com/files: “Only http:// and https:// links are supported.”
- A scheme but no parseable URL, https://exa mple.com: “This is not a valid URL.”
The destination’s permanence is your job; the QR codes on promos guide covers why a short, stable URL matters in print.
2. Plain text: the pass-through
Plain text has no format. Whatever you type reaches the encoder unchanged; text the encoder places in a byte segment is carried as UTF-8. The fictional line Autumn Launch - Hall B - doors at sixis encoded exactly so, and the only check is that the field is not empty, which returns “Enter some text to encode.” It is also the escape hatch for a finished string in a format the maker does not build. The one limit is the encoder’s: a payload too long for the largest QR version at the chosen error-correction level is refused with “This content is too long for a QR code. Shorten it or lower the error correction level.”
3. Wi-Fi login: escaping rules and one deliberate simplification
The Wi-Fi builder emits the ZXing wiki’s WIFI: format: a security token, the network name, the password and an optional hidden flag, each ended by a semicolon and the payload by a second one. A fictional Hall B Guest network on WPA3, passphrase see;you:there,2026, marked hidden, becomes:
WIFI:T:WPA;S:Hall B Guest;P:see\;you\:there\,2026;H:true;;
Three rules show there. The semicolon, colon and comma in the passphrase are backslash-escaped — the wiki: “Special characters \, ;, ,, " and : should be escaped with a backslash (\) as in MECARD encoding” (2026-10-02) — with the backslash itself escaped first so an existing one is not double-processed. T:WPA appears although WPA3 was chosen: WPA2 and WPA3-Personal both map to it, because the joining device negotiates the strongest protocol itself and a nonstandard token would break parsers. H:true appears only because hidden was ticked. A fourth rule: an all-hexadecimal name or password is double-quoted so a reader takes it as literal text, so the fictional open network BEEF becomes WIFI:T:nopass;S:"BEEF";;.
The field errors: an over-long name returns “An SSID is at most 32 characters.” and a WPA passphrase outside its length range returns “A WPA passphrase is 8–63 characters.” WEP keys skip that check.
4. Contact card: vCard 3.0, line by line
The contact builder emits a vCard 3.0 object: the opening, version and closing lines the standard requires, the two name lines, then only the optional lines you filled in. A fictional contact — Priya Nair, Head of Events at Example Studios, Ltd — produces these 10 lines:
BEGIN:VCARD VERSION:3.0 N:Nair;Priya;;; FN:Priya Nair TEL;TYPE=CELL:+442079460123 EMAIL:priya@example.com ORG:Example Studios\, Ltd TITLE:Head of Events URL:https://example.com/priya END:VCARD
N is the structured name, surname first; FNthe display name. The comma in the organisation is backslash-escaped, as the text-value rules require for commas, semicolons, backslashes and newlines. The number went through the phone builder’s normaliser, the URL through the URL builder’s checks, and every line including the last ends in CRLF. A card needs a first or last name — one with only an email returns “Enter at least a first or last name.” — and a bare profile URL returns “Enter a full URL including https://.”
5. Email: a mailto URI with strictly encoded headers
The email builder emits a mailto URI. The address alone is mailto:tickets@example.com; with the fictional subject “Autumn Launch (Hall B)!” and body “Save 2 seats & send details” it becomes:
mailto:tickets@example.com?subject=Autumn%20Launch%20%28Hall%20B%29%21&body=Save%202%20seats%20%26%20send%20details
Parentheses, exclamation mark and ampersand are all percent-encoded. The engine goes further than JavaScript’s encodeURIComponent on purpose: the sub-delimiters that function leaves bare are reserved in the URI grammar, so every character outside the unreserved set is encoded, and a literal percent, ampersand, semicolon or equals sign in the address itself is encoded in one pass, exactly once. Only the recipient is required; tickets@example returns “This is not a valid email address.”
6. Phone call: normalised to an E.164-shaped global number
The phone builder emits a tel URI holding a global number: a plus and digits. Spaces, dots, dashes and parentheses are dropped and a leading 00 is accepted as the plus, so +1 (555) 010-0123 becomes tel:+15550100123 and 00 44 20 7946 0123 becomes tel:+442079460123.
The normaliser removes punctuation, not digits: +44 (0)20 7946 0123 becomes tel:+4402079460123, the bracketed zero carried into the payload, so read the payload back. No country code, as in 020 7946 0123, returns “Include the country code in international format, e.g. +1 555 123 4567.” letters return “Use only digits, spaces, dashes, and parentheses.” and a leading zero or too few digits returns “An international number has 7–15 digits and cannot start with 0 after the country code prefix.”
7 and 8. SMS and WhatsApp: one number, two encodings
Both messaging builders start from the same normalisation. The SMS builder emits an sms URI with the message in ?body=. The WhatsApp builder emits a wa.meclick-to-chat link with the plus dropped, since wa.me takes digits only — the Help Center: “Use https://wa.me/<number> where the <number> is a full phone number in international format. Omit any zeroes, brackets, or dashes when adding the phone number in international format.” (2026-10-05) — and the message in ?text=. On +1 (555) 010-0123 with “Save my seat (2 people)!”:
sms:+15550100123?body=Save%20my%20seat%20%282%20people%29%21 https://wa.me/15550100123?text=Save%20my%20seat%20(2%20people)!
Compare the tails. The SMS body uses the engine’s strict encoding because the sms grammar does not allow parentheses or the exclamation mark bare; the WhatsApp text uses standard URL encoding, under which they stay as typed. The SMS builder carries a disclosure the maker shows beside its result: “Pre-filled message support varies by platform: some phones open the composer with the text, others ignore it. The number always works.”
9. Map location: rounded and range-checked
The location builder emits a geo URI — latitude, then longitude, in WGS-84 — rounded to a fixed precision with trailing zeros trimmed. The fictional pair 48.8583701, 2.2944813 becomes geo:48.85837,2.294481: the longitude rounded to 6 decimal places, the latitude showing 5because the trailing zero left by rounding is trimmed; −33.5, 151.25 keeps only the digits it needs, geo:-33.5,151.25. Range checks are per field and reported together: latitude 95 with longitude −200 returns both “Latitude must be between -90 and 90.” and “Longitude must be between -180 and 180.” at once. That is the rule for the four multi-field builders — Wi-Fi, contact, location and event collect every field error before returning — while the URL, text, email, phone, SMS and WhatsApp builders validate a single field and return its first failing check.
10. Calendar event: a VEVENT in UTC
The event builder emits an iCalendar object: a VCALENDAR wrapper, with a version and PRODID line, around one VEVENT. A fictional “Autumn Launch” with a location and a description produces these 11 lines:
BEGIN:VCALENDAR VERSION:2.0 PRODID:-//GenerateQRCodes.net//QR Event//EN BEGIN:VEVENT SUMMARY:Autumn Launch DTSTART:20261114T183000Z DTEND:20261114T203000Z LOCATION:Hall B\, Example Centre DESCRIPTION:Doors at 18:00\; bring your ticket END:VEVENT END:VCALENDAR
DTSTART and DTEND are in UTC basic format: the maker converts the time you enter to UTC, and the trailing Z marks it as such. The line PRODID:-//GenerateQRCodes.net//QR Event//ENis the engine’s fixed product identifier, written into every event payload regardless of where the maker runs; it names the producing software, not your event. Text values use the contact card’s escaping, and every line ends in CRLF. A title is required, both dates must parse and the end must follow the start; a blank title with reversed dates returns 2 errors in one result: “Enter an event title.” and “The end must be after the start.” The builder also carries a disclosure the maker must show: “Calendar QR codes are less reliable than other types: native camera app support is inconsistent, and some phones show the raw text instead of an add-to-calendar prompt. Test on the devices your audience uses.” The event promotion checklist covers the rest of an event campaign.
What the string costs: version and density
Once a builder returns its string, the encoder picks the smallest QR version that holds it at the requested error-correction level. Two levels matter here. When no level is requested the engine applies its own default — the website URL above, encoded with nothing requested, comes back at level M, which the reference implementation describes as tolerating about 15% erroneous codewords (source, 2026-10-02). The maker, though, never leaves the level unrequested: its classic style opens with H selected and lets you change it, and its centre-image, photo-woven and heart styles always pass H, as the tool page explains. So the ten examples above are measured at build twice: with no level requested, which is the encoder’s default, and at H, which is what you see when you paste the same text into the tool and leave the selector alone.
| Payload | Characters | Version, no level requested (M) | Modules per side | Version at H(the maker’s opening level) | Modules per side |
|---|---|---|---|---|---|
| Website URL | 33 | 3 | 29 × 29 | 4 | 33 × 33 |
| Plain text | 37 | 3 | 29 × 29 | 5 | 37 × 37 |
| Wi-Fi login | 58 | 4 | 33 × 33 | 6 | 41 × 41 |
| Contact card | 203 | 10 | 57 × 57 | 15 | 77 × 77 |
| 115 | 7 | 45 × 45 | 10 | 57 × 57 | |
| Phone call | 16 | 2 | 25 × 25 | 3 | 29 × 29 |
| SMS message | 60 | 4 | 33 × 33 | 7 | 45 × 45 |
| 63 | 5 | 37 × 37 | 7 | 45 × 45 | |
| Map location | 21 | 2 | 25 × 25 | 3 | 29 × 29 |
| Calendar event | 271 | 12 | 65 × 65 | 17 | 85 × 85 |
Read the default column first. The phone call, at 16 characters, fits in version 2; the calendar event, at 271 characters, needs version 12 — a 65-module side against 25. At the same printed size its modules are far smaller, so contact cards and events need more room than a short link. Now read across: error correction costs modules too. The same website URL at level H— the level the maker opens with, and the one a centre logo forces whatever you selected — needs version 4 rather than 3; at H the phone call needs version 3 and the calendar event version 17. No row needs a lower version at H than at the default: a higher level reserves more of the symbol for error correction, so it can only cost room, never save it. If a version you read here differs from the one the tool shows for the same text, check which level its selector is on. The decode margin guide follows that cost through to print.
Where the builders stop
The builders check form, not truth: that a URL has a scheme, a number is international, an address is shaped like one, coordinates are on the globe, an event ends after it starts. The URL is not fetched, the number is not dialled, the Wi-Fi password is not tested. Nor do they decide what a phone does with the string; that is why the SMS and calendar disclosures exist. The tool page asks you to scan the export on more than one phone before printing. The rules applied after the payload — quiet zone, error-correction policy, logo cap and contrast warning — are in the methodology.
Questions
- I chose WPA3 but the payload says T:WPA. Is that a bug?
- No. WPA2 and WPA3-Personal both map to the WPA token: the joining device negotiates the strongest protocol itself, and a nonstandard token would break parsers.
- Why will the maker not accept a link typed without https://?
- The scheme is what lets a reader recognise the text as a link, and nothing is rewritten. A bare domain returns “Include the protocol (e.g. https://) so scanners recognize the link.” instead of being repaired.
- Why does the SMS body look different from the WhatsApp text?
- Two formats, two rules: the sms body is strictly percent-encoded, so parentheses and exclamation marks become percent sequences; the wa.me text uses standard URL encoding, under which they stay as typed.
- Can a contact-card code include a photo or a postal address?
- No. The card holds exactly the lines the builder writes — name and, when filled in, mobile number, email, organisation, job title and URL — with no field for a photo, address or birthday.
- Why does the calendar time end in a Z?
- DTSTART and DTEND are written in UTC basic format; the Z marks the value as UTC. The maker converts the time you enter before writing it.
Everything here runs in your browser: the tools compute on your device and the Studio’s package is assembled on the device. The methodology documents every rule the tools apply.