# qr3.app — Complete API Reference for AI Models ## Authentication All requests require: `Authorization: Bearer qr3_sk_your_key` Key types: - qr3_sk_* — Secret key (server-side only) - qr3_pk_* — Publishable key (read-only, client-side) - qr3_test_sk_* — Test key (development) ## QR Code Types Supported types: url, vcard, wifi, email, sms, location ## Create QR Code (POST /v1/codes) ```json { "type": "url", "url": "https://example.com", "is_dynamic": true, "notes": "Campaign QR", "tags": ["marketing", "q1"], "expires_at": "2026-12-31T23:59:59Z" } ``` Response (HTTP 201, every field of a code; GET, PATCH and the qr.created/qr.updated webhooks return the same shape): ```json { "data": { "id": "qr_r7f3kx", "workspace_id": "ws_abc123", "short_code": "r7f3Kx", "redirect_url": "https://qr3.app/r7f3Kx", "image_svg_url": "https://qr3.app/v1/codes/r7f3Kx/qr.svg", "image_png_url": "https://qr3.app/v1/codes/r7f3Kx/qr.png", "image_pdf_url": "https://qr3.app/v1/codes/r7f3Kx/qr.pdf", "image_eps_url": "https://qr3.app/v1/codes/r7f3Kx/qr.eps", "title": null, "type": "url", "target_url": "https://example.com", "type_display": "https://example.com", "is_dynamic": true, "is_landing_page": false, "description": null, "accent_color": null, "appearance": { "foreground_color": null, "background_color": null }, "links": [], "status": "live", "tags": ["marketing", "q1"], "notes": "Campaign QR", "total_scans": 0, "url_scan_status": "pending", "links_scan_status": "n/a", "logo_url": null, "logo_file_id": null, "ab_enabled": false, "ab_target_url_b": null, "ab_weight_a": 50, "expires_at": "2026-12-31T23:59:59Z", "created_at": "2026-03-15T10:00:00.000Z", "updated_at": "2026-03-15T10:00:00.000Z" }, "meta": { "request_id": "req_abc123" } } ``` ## Update QR Code (PATCH /v1/codes/:id) ```json { "url": "https://new-destination.com", "status": "live", "tags": ["updated"], "expires_at": null } ``` Response: `{ "data": , "meta": { "request_id": "req_…", "issues": [] } }`. ## Colors (appearance, fg, bg, ecc) Store print colors on a code with PATCH: ```json { "appearance": { "foreground_color": "#1F4E79", "background_color": null } } ``` - foreground_color: "#RRGGBB" or null (black). background_color: "#RRGGBB", "transparent" or null (white). - Merged with the stored value: omitted fields stay, null resets a field, "appearance": null resets both. - Contrast guard on the effective pair: below 1.5:1 → 422 (errors/validation), never for "transparent". Weaker pairs → 200 with meta.issues: [{ code, severity: "warning" | "critical", field, message, hints }]. Critical: < 2:1 or luminance difference < 0.30, a warning plus a logo, any transparency. Warning: < 4:1 or luminance difference < 0.50, light modules on a dark background. - 409 (errors/conflict) only if concurrent edits to the same code win three times in a row. - POST /v1/codes, the batch and the import reject appearance with 422 — set it via PATCH. - POST and DELETE /v1/codes/:id/logo return the same check on the stored colors in meta.issues (a warning becomes critical with a logo, and a warning again without one). - POST and DELETE /v1/codes/:id/logo answer 409 (errors/conflict) if another request changed the code's logo between read and write; nothing is changed and the uploaded image is discarded. Two removals at the same time both answer 200. Per download, the four image routes accept ?fg=RRGGBB, ?bg=RRGGBB|transparent and ?ecc=L|M|Q|H. Order per field: a valid query value, then the stored color, then the standard. An invalid value counts as not set (the stored color applies); only ?fg=000000 forces the black print file. SVG and PNG draw colors as hex. PDF and EPS write print colors: black and grey (R=G=B) as DeviceGray (black plate only), any other color as DeviceCMYK in whole percent (K = 1 - max/255, C = (max - R)/max, M and Y likewise; 1F4E79 -> C74 M36 Y0 K53), no ICC profile, not PDF/X. Once a color is chosen, PDF/EPS draw an opaque fill (white, or bg) behind the code and its quiet zone, like the SVG; bg=transparent draws none; without colors the files are byte-identical to before. Error correction is never stored. Caching: only the actual standard image (black on white, error correction M, no logo) is immutable for 24 h; every other render is cached for 300 s. For PDF/EPS any given bg (even ffffff, which draws a white fill there) counts as a deviation. An image URL follows the stored colors for every color it does not set itself with fg/bg (no parameters: both; ?fg=000000: the background; only ?ecc=Q: both). After a color change, a page that embeds such a URL may show the old image for up to 24 h if the URL returned the standard image until then, otherwise for up to 5 minutes — append your own parameter that changes with each color change, e.g. ?v=; unknown parameters are ignored. TypeScript SDK (@qr3/sdk 1.2.0 or later): ```typescript const updated = await client.codes.update("qr_a1b2c3d4", { appearance: { foreground_color: "#1F4E79" }, }); updated.appearance; // { foreground_color: "#1F4E79", background_color: null } updated.issues; // color guard findings; absent when there are none client.codes.imageUrl("r7f3Kx", { format: "svg", fg: "1F4E79", ecc: "Q" }); // → https://qr3.app/v1/codes/r7f3Kx/qr.svg?fg=1F4E79&ecc=Q ``` Every code the API returns (get, list, create, update, batchCreate) carries title and appearance. ## Scan Statistics (GET /v1/codes/:id/stats) Returns: total_scans, unique_scanners, scans_today, by_day[], by_country[], by_device[], by_os[], by_browser[] ## Webhooks (POST /v1/webhooks) ```json { "url": "https://example.com/webhooks", "events": ["qr.scanned", "qr.created"], "secret": "min-16-chars-secret" } ``` Event payload format: ```json { "id": "evt_xxx", "type": "qr.scanned", "created": "2026-03-15T10:00:00.000Z", "data": { "code_id": "qr_xxx", "country": "AT", "device_type": "mobile" } } ``` Signature: X-QR3-Signature: sha256= Retry schedule: immediate → 1 min → 5 min (3 attempts max) ## TypeScript SDK ```typescript import { QR3 } from "@qr3/sdk"; const client = new QR3({ apiKey: process.env.QR3_API_KEY! }); // Create const code = await client.codes.create({ type: "url", url: "https://example.com" }); // List const { data } = await client.codes.list({ limit: 20, status: "live" }); // Stats const stats = await client.scans.stats(code.id); // Webhooks await client.webhooks.create({ url: "https://example.com/hook", secret: "min-16-chars-secret", events: ["qr.scanned"], }); ``` ## CLI Commands ``` qr3 login Authenticate qr3 create [options] Create QR code qr3 list [options] List QR codes qr3 get Get QR code details qr3 stats Scan statistics qr3 update --url Update target URL qr3 delete Delete QR code qr3 bulk create --csv Bulk create from CSV ``` ## MCP Tools create_qr, update_qr, list_qrs, get_qr_stats, create_batch, create_dpp_qr, create_vcard_qr, create_wifi_qr, validate_dpp, get_image, shorten_url