QR code generation model
Orbit’s QR surface (/api/v1/qr) turns a payload into a scannable image, rendered in-cluster. The QR Code API reference holds the parameter shapes and request examples; this page names the model behind it — what each endpoint mints, why tenant-supplied payloads pass through a safety filter before they become an image, and when a QR code is the wrong tool because you actually wanted click tracking.
What the surface mints
Three GET endpoints share the same rendering pipeline and differ only in what they encode:GET /api/v1/qr/generateencodes an arbitrarydatapayload — plain text or a URL — as-is. Use it when the payload is already the final string you want a scanner to receive.GET /api/v1/qr/whatsapptakes aphonenumber and an optionalmessage, and encodes ahttps://wa.me/<digits>click-to-chat deep link. Scanning opens a WhatsApp chat with that number, pre-filled with the message. The encoded URL carries only the digits from the phone number.GET /api/v1/qr/smstakes aphonenumber and an optionalmessage, and encodes ansms:<E.164>?body=…URI. Scanning opens the device’s native SMS composer, addressed and pre-filled. The phone is normalized to E.164 before it goes into the URI.
The safety model
A QR code is a payload you ask a stranger’s phone to trust. Two facts about the scanners that read them drive the design:- Scanner apps preview the encoded string as a tappable link, and a small fraction execute
javascript:ordata:URIs in a hosted webview — so the payload itself must be filtered, not just rendered. - Everything you send is encoded verbatim into the image. A filter that only affects the HTTP response would change nothing; the filter has to run before encoding.
/generate. Requested payloads are rejected with a 400 when they begin with a script-execution or non-network URL scheme — javascript:, data:, vbscript:, file:, about:, or blob:. Plain text, http(s), mailto:, tel:, sms:, and wa.me links pass through. The check exists so a payload that came from an untrusted source (a form field, an import, a customer-supplied value) cannot ride your authenticated QR render into a scanner’s browser.
Two-stage phone validation on /whatsapp and /sms. The phone parameter passes through two independent gates:
- A shape floor — the input may contain only characters that legitimately appear in a human-typed international number (
+, digits, spaces, parentheses, dashes, dots), between 4 and 32 characters. Any letter, colon, or angle bracket fails with a validation error. This blocks the XSS-shaped payloads (phone=javascript:…) before they reach the URI assembly. - A dialability check — the surviving input is parsed by a full international phone library and must normalize to a real, dialable E.164 number. Valid-shape but undialable inputs (
+12, all zeros, a country code with no subscriber digits) fail here withINVALID_PHONE.
sms: URI contains only a leading + and digits regardless of how the caller formatted the number. The WhatsApp route keeps only digits, matching the wa.me contract.
Below the guards, rendering itself is local: the PNG is drawn in-cluster by the API process. No payload — phone number, message body, or arbitrary URL — leaves the platform to a third-party image service.
Encoding rules
The rendering parameters are uniform across all three endpoints:
PNG is the default because the common case is “put this in a template”; the JSON envelope exists for callers that need the image as a value rather than a stream.
Error vocabulary
Malformed query strings can also surface as the standard
422 parse envelope, in line with the rest of the API.
QR codes vs. short links
A generated QR is an untracked render. Nothing is recorded when it is scanned: there is no click row, no analytics rollup, no attribution, no webhook — you get back an image and that is the whole transaction. That is the right shape for point-of-sale posters, packaging, and badges where the scan happens on hardware you do not control and there is nothing to attribute. When you need the scan to be measurable — per-recipient attribution, campaign rollups, ashort_link.click event — mint a short link and encode its public URL (/l/:code) into the QR via /generate. The QR becomes the print-facing transport for a trackable redirect: every scan flows through the link’s click pipeline, while the image itself stays a dumb render. Use QR alone when anonymity and zero bookkeeping are the feature; QR-over-short-link when the campaign needs the scan counted.
See also
- QR Code API reference — parameters, response envelopes, and request examples.
- Short links, landing pages, and the publish lifecycle — the trackable artifact family to pair with QR codes when attribution matters.
- Public pixels and redirects — the anonymous, rate-limited edge those public URLs are served from.