Skip to main content

Click-to-chat QR codes for print and web

Orbit renders QR codes server-side on GET /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 the qrcode 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.
On the JSON envelope the response also echoes back whatsapp_link and the digit-normalized phone, so you can see exactly what was encoded. 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 raw image/png bytes with Cache-Control: private, max-age=300, ready for a print template or <img> tag. Pass format=json for the standard envelope:
The 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>:
Node CLI — bulk-generate a batch of sticker codes. Each call is an authenticated GET, so a for loop with your image targets writes the print assets to disk:
Embed in an email via the data URL. Request 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_PHONE on /whatsapp or /sms. The phone must parse as a dialable international number. A shape like +12 or 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. data is required and must not begin with javascript:, data:, vbscript:, file:, about:, or blob:. Encode a plain URL or text payload.
  • Response is not an image. You passed format=json. Read data.qr_data_url from the envelope, or drop format=json to get raw PNG bytes.