TEASEDocs
ProductsLinkQR codes

Design a QR code

Pick one of 100 catalog presets or build a custom look — module and eye shape, color or gradient, background, logo, caption — with a live preview.

The look of a QR code is a small, validated JSON config. Only that config is stored — the SVG is rendered from it fresh every time, so the same config always produces the same code (a requirement for anything you're about to print). You can either point at one of the 100 built-in looks, or send your own config.

Use a catalog preset

curl https://app.tease.link/api/admin/qr/presets \
  -H "Authorization: Bearer $TEASE_API_KEY"

Returns 100 entries — 10 shape families (classic, rounded, dots, diamond, classy, compact, brand, sticker, bold, sharp) crossed with 10 color palettes (mono, ink, ofblue, insta, sunset, neon, gold, rose, ice, pink). Each entry's id ("<family>-<palette>", e.g. rounded-insta) is what you pass as preset when creating or updating a code — the full design object is there too if you want to start from a preset and tweak it.

Build a custom design

Send a design object instead of (or as a starting point layered over) a preset:

KeyTypeDescription
modulestringDark-module shape: square, rounded (neighbour-aware, so touching modules blend into one connected shape), dot, or diamond.
eye_framestringFinder-eye frame: square, rounded, circle, or leaf.
eye_dotstringFinder-eye pupil: square, rounded, or circle.
fgstringSolid module color, #rrggbb.
fg_gradobjectGradient fill, wins over fg: { "kind": "linear" | "radial", "stops": [[0–1, "#hex"], …] }, 2–3 stops, plus an optional linear dir: [x1, y1, x2, y2] (fractions 0–1).
eye_fgstringSeparate eye color; omit to reuse the module color/gradient.
bgstringCard background, #rrggbb or none for transparent.
card_radiusintCard corner radius, 0–80.
logostringCenter logo: "" (none), tease (the brand mark), or icon:<name> — any name from GET /api/admin/qr/logo-icons. Tint with logo_color, plate color with logo_bg (#rrggbb or none).
captionstringText under the code (e.g. SCAN ME), up to 24 characters. Color via caption_color.

Anything you don't set falls back to the engine default (dark rounded modules on white). Sending an object with unknown keys, an out-of-range value, or a non-hex color returns 400 design_invalid with a message naming the offending field — nothing partial is ever saved.

Scannability is handled for you, not something to configure: error correction jumps to level H the moment a logo is set (the center cut-out stays inside the ~8% actual / 30% budget), and stays at level Q otherwise. The quiet zone is always 4 modules. You don't pick the error correction level directly.

Live preview while building

POST /api/admin/qr/render takes { "design": {...}, "url": "..." } and returns { "svg": "..." } — the same renderer the saved-code endpoint uses, so what you see is exactly what a scan of the finished carrier will look like. Use it to iterate on a design before attaching it to a code with preset/design on create or PATCH.

How to know it worked

The preview SVG renders without a design_invalid error, and a phone scan of the rendered code resolves to the URL you passed. If you added a logo, check the preview at a small size too — a busy custom-icon logo can still read badly even though the encoder guarantees it decodes.

If it didn't work

  • 400 design_invalid — the error message names the exact key. Check it against the table above; a common one is a logo value that isn't "", tease, or icon:<name> for a name GET /api/admin/qr/logo-icons actually lists.
  • 404 preset_not_found — the preset id doesn't match "<family>-<palette>" from GET /api/admin/qr/presets; ids are case-sensitive.
  • Render succeeds but an already-saved code 500s instead — that's a removed icon file, not a bad config; see Troubleshooting.

What's next

On this page