> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Click-to-chat QR codes for print and web

> Generate server-side QR codes for WhatsApp click-to-chat, SMS deep links, or arbitrary URLs — for print collateral, storefront signage, and web embeds.

# 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](/api-reference/qr). 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`.

| Route                     | Encoded payload                                     |
| ------------------------- | --------------------------------------------------- |
| `GET /api/v1/qr/generate` | Any URL or plain text you supply                    |
| `GET /api/v1/qr/whatsapp` | A `https://wa.me/<phone>?text=…` click-to-chat link |
| `GET /api/v1/qr/sms`      | An `sms:<phone>?body=…` native-composer deep link   |

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.

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/qr/generate?data=https%3A%2F%2Fyourbrand.com%2Fsale&size=400" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -o qr.png
```

## 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.

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/qr/whatsapp?phone=%2B14155552671&message=Hi%20there&size=400" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -o whatsapp.png
```

On the JSON envelope the response also echoes back `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`.

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/qr/sms?phone=%2B14155552671&message=Quote%20SAVE10&size=400" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -o sms.png
```

## 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:

```json theme={null}
{
  "data": {
    "qr_data_url": "data:image/png;base64,iVBORw0KGgoAAAANSU...",
    "size": 400
  },
  "meta": { "request_id": "req_..." }
}
```

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:

| Parameter | Type            | Default | Description                                     |
| --------- | --------------- | ------- | ----------------------------------------------- |
| `size`    | integer 50–1024 | `300`   | Edge length in pixels. Output is always square. |
| `format`  | `png` \| `json` | `png`   | Raw PNG vs. data-URL envelope.                  |

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>`:

```tsx theme={null}
function ChatQr({ phone, message }: { phone: string; message: string }) {
  const [src, setSrc] = useState<string | null>(null);
  useEffect(() => {
    const qs = new URLSearchParams({ phone, message, format: "json" });
    fetch(`/api/v1/qr/whatsapp?${qs}`, {
      headers: { "X-API-Key": process.env.NEXT_PUBLIC_ORBIT_KEY! },
    })
      .then((r) => r.json())
      .then((body) => setSrc(body.data.qr_data_url));
  }, [phone, message]);
  if (!src) return null;
  return <img src={src} alt="Scan to chat on WhatsApp" width={300} height={300} />;
}
```

**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:

```bash theme={null}
for code in SAVE10 SAVE20; do
  curl "https://api.orbit.devotel.io/api/v1/qr/generate?data=https%3A%2F%2Fyourbrand.com%2F$code&size=600" \
    -H "X-API-Key: dv_live_sk_your_key_here" \
    -o "qr-$code.png"
done
```

**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.
