Click-to-chat QR codes for print and web
Orbit renders QR codes server-side onGET /api/v1/qr/* and returns PNG bytes directly, so your payload (phone numbers, SMS bodies, URLs) never leaves the platform. Use these endpoints for print collateral, sticker campaigns, storefront signage, and “scan to start chatting” posters where a scanned phone should open a WhatsApp chat or the native SMS composer pre-addressed to your number.
For request and response schemas see the QR API reference. This page covers when to use each route and how to embed the result.
1. What the QR surface does
Three GET routes render the image in-cluster with theqrcode npm package and return image/png bytes. No third-party image proxy is involved at any point, so a raw phone number or message body you encode is never sent to an external server. Each route defaults to a raw PNG and can instead return a base64 data URL inside the standard { data, meta } envelope when you pass format=json.
Authentication is your normal API key (
X-API-Key) or session JWT; read rate limits apply.
2. Arbitrary payloads — GET /api/v1/qr/generate
Encode any URL or text (event check-in codes, coupon URLs, Wi-Fi join strings, support pages). Requests that start with a script-execution URL scheme — javascript:, data:, vbscript:, file:, about:, or blob: — are rejected with a validation error, because mobile QR-scanner apps can otherwise execute those schemes in a hosted webview. Plain text and http(s):// URLs pass through untouched.
3. WhatsApp click-to-chat — GET /api/v1/qr/whatsapp
Scanning opens WhatsApp addressed to your number, with an optional pre-filled message — the standard https://wa.me/<digits> deep link. The phone is validated as a dialable international number before encoding; undialable inputs return INVALID_PHONE (400). Non-digit characters (spaces, dashes, parentheses, the leading +) are stripped before the digits go into the link, but the number must exist as a real destination — the validation runs on your raw input.
whatsapp_link and the digit-normalized phone, so you can see exactly what was encoded.
4. SMS deep link — GET /api/v1/qr/sms
Scanning opens the device’s native SMS composer addressed to your number — the sms:<E.164>?body=… intent. The phone is normalized to E.164 (+<digits>) by the same international-number parser before it is encoded, so what lands in the QR is always a clean sms:+14155552671-style URI. As with WhatsApp, undialable numbers return INVALID_PHONE.
5. Response formats
The default response is rawimage/png bytes with Cache-Control: private, max-age=300, ready for a print template or <img> tag. Pass format=json for the standard envelope:
whatsapp and sms routes additionally return the encoded deep link and the normalized phone in data. Use qr_data_url when you need to carry the image through a JSON API or store it beside its source values; use raw PNG when you serve it directly.
6. Query parameters
All three routes share the same rendering parameters:
The WhatsApp and SMS routes take
phone (required) and message (optional, ≤ 1024 chars) as well; /generate takes data (required, ≤ 2048 chars).
7. Integration examples
React component fetching the PNG. The API key can live on a backend route you own; the browser then fetches the image through it, or you can hand the base64 data URL straight to an<img>:
for loop with your image targets writes the print assets to disk:
format=json, read data.qr_data_url, and set it as the src of an <img> in the template — the image inlines without an extra fetch at send time. Most email clients that block remote images render data URLs; treat the raw-PNG form as the safer fallback in strict CRMs.
8. When not to use this surface
These routes generate on demand and are rate-limited for interactive use. If you need millions of unique codes per minute on a send path — per-recipient one-time tokens, for example — generate the codes once, store them, and serve the assets yourself instead of pumping the render endpoint inline. For a fixed set of campaign codes (print collateral, signage, stickers) the on-demand route is the right tool exactly because the payload stays in-cluster.Troubleshooting
- 400
INVALID_PHONEon/whatsappor/sms. The phone must parse as a dialable international number. A shape like+12or a country code with no subscriber digits is rejected even if it consists only of digits — check the number against your contacts and pass the full international form. - 422 on
/generate.datais required and must not begin withjavascript:,data:,vbscript:,file:,about:, orblob:. Encode a plain URL or text payload. - Response is not an image. You passed
format=json. Readdata.qr_data_urlfrom the envelope, or dropformat=jsonto get raw PNG bytes.