QR Atelier API · v1
API reference
Turn a design, the JSON the studio saves in design files, into its QR code, drawn as SVG by the studio’s engine. No key or account. Classic codes aren’t scan-tested on the server: test them with a scanner, or open the design in the studio.
- Base URL
- https://qratelier.app (Production)https://qr.cfstcyr.com (Production, the earlier address)
- OpenAPI
- /api/v1/openapi.json
POST/api/v1/codes
Make a QR code from a design
A classic design gives its classic code, byte for byte the studio’s. A picture design gives the automatic pick, scan-tested on the server. The answer is the SVG itself, or JSON with the SVG and the symbol’s details when Accept prefers application/json. The checks run in this order: preflight, method, rate limit, content type, body size, body, code.
Try it
POST https://qratelier.app/api/v1/codes
Sent as written.
Sends this request from your browser to this site.
curl -sS -X POST 'https://qratelier.app/api/v1/codes' \
-H 'content-type: application/json' \
-H 'accept: image/svg+xml' \
--data-binary '{
"design": {
"contentType": "url",
"contentValues": {
"url": {
"url": "https://example.com"
}
}
}
}' \
-o code.svgResponse
Nothing sent yet.
Request
The design file envelope, at most 1,048,576 bytes. app and version are optional: a minimal call is just { "design": { ... } }.
application/jsonCodeRequest
app"qr-atelier"Written by Save design file, so a design file posts as is.
versioninteger 1–4default 4The design version, as in design files: 1 for files written before design version 3, then the design version itself. Older designs are migrated.
designDesignrequiredA design as saved by the studio. It needs a known contentType and a contentValues object; every other field falls back to its default when missing or invalid.
contentType"url" | "text" | "wifi" | "vcard" | "email" | "phone" | "sms" | "event" | "location" | "crypto" | "epc" | "whatsapp" | "telegram" | "app" | "paypal"requiredWhat the code holds. Only contentValues[contentType] is encoded.
contentValuesobjectrequiredThe values of each content type, keyed by type.
urlobjectOpen a website
urlstringrequireddefault ""https:// is added when no scheme is given.
textobjectPlain text
textstringrequireddefault ""
wifiobjectJoin a Wi-Fi network
ssidstringrequireddefault ""encryption"WPA" | "WEP" | "nopass"default "WPA"hiddenbooleandefault falsepasswordstring (password)requireddefault ""
vcardobjectSave a contact card (vCard or MeCard)
firstNamestringdefault ""lastNamestringdefault ""organizationstringdefault ""A card needs a name, an organization, or both.
titlestringdefault ""mobilestringdefault ""workPhonestringdefault ""emailstringdefault ""websitestringdefault ""streetstringdefault ""citystringdefault ""regionstringdefault ""postcodestringdefault ""countrystringdefault ""notestringdefault ""format"vcard" | "mecard"default "vcard"MeCard is shorter, and some older Android phones read it better. It has no job title.
emailobjectCompose an email
tostringrequireddefault ""subjectstringdefault ""bodystringdefault ""
phoneobjectCall a number
phonestringrequireddefault ""Include the country code so it works abroad.
smsobjectSend a text message
phonestringrequireddefault ""messagestringdefault ""
eventobjectAdd an event to a calendar
titlestringrequireddefault ""locationstringdefault ""startstringrequireddefault ""endstringdefault ""allDaybooleandefault falseOnly the dates are used.
timeZone"floating" | "utc"default "floating"With Fixed, people in other time zones see it converted to their local time.
reminder"none" | "start" | "10m" | "30m" | "1h" | "1d"default "none"descriptionstringdefault ""
locationobjectShow a place on a map
format"geo" | "google" | "apple"default "geo"Google Maps and Apple Maps links also open on a computer, and can search an address.
addressstringdefault ""Or enter exact coordinates below.
latstringdefault ""lngstringdefault ""labelstringdefault ""
cryptoobjectRequest a Bitcoin or Ethereum payment
currency"bitcoin" | "ethereum"default "bitcoin"addressstringrequireddefault ""amountstringdefault ""labelstringdefault ""
epcobjectSEPA credit transfer (EPC / GiroCode)
namestringrequireddefault ""ibanstringrequireddefault ""bicstringdefault ""amountstringdefault ""referencestringdefault ""Up to 140 characters. A valid RF creditor reference is sent as a creditor reference, anything else as a message.
whatsappobjectOpen a WhatsApp chat with a number
phonestringrequireddefault ""With the country code: WhatsApp needs the full international number.
messagestringdefault ""
telegramobjectOpen a Telegram chat with a username
usernamestringrequireddefault ""A public username, or paste a t.me link.
appobjectOpen an app on the App Store or Google Play
store"apple" | "google"default "apple"appleIdstringrequireddefault ""The number after “id” in the app’s App Store link, or paste the link.
packagestringrequireddefault ""The id= part of the app’s Google Play link, or paste the link.
paypalobjectAsk for a payment on PayPal.me
usernamestringrequireddefault ""The name after paypal.me/, or paste your link (then enter its amount below).
amountstringdefault ""currency"account" | "AUD" | "BRL" | "CAD" | "CHF" | "CZK" | "DKK" | "EUR" | "GBP" | "HKD" | "HUF" | "JPY" | "MXN" | "NOK" | "NZD" | "PLN" | "SEK" | "SGD" | "TWD" | "USD"default "account"
mode"classic" | "image"default "classic"classic, or image for a picture code (needs picture).
ecl"L" | "M" | "Q" | "H"default "M"Error correction level. A logo can raise it: the answer is the smallest level that still reads under the logo.
styleobjectColours, dot and eye shapes, quiet zone, logo and frame, as in the studio.
foregroundobject | object | objectvariant 1objecttype"solid"colorstringdefault "#111111"
variant 2objecttype"linear" | "radial"fromstringdefault "#000000"tostringdefault "#000000"anglenumber
variant 3objecttype"palette"colorsarrayarrangement"scatter" | "diagonal" | "radial" | "picture"default "scatter"
backgroundobject | objectvariant 1objecttype"solid"colorstringdefault "#ffffff"
variant 2objecttype"linear" | "radial"fromstringdefault "#000000"tostringdefault "#000000"anglenumber
transparentBackgroundbooleandefault falsedotShape"square" | "dots" | "rounded" | "extra-rounded" | "classy" | "diamond" | "star" | "heart" | "vertical" | "horizontal" | "tiny" | "custom"default "square"dotScalenumbereyeFrameShape"square" | "rounded" | "extra-rounded" | "circle" | "leaf" | "dots" | "custom"default "square"eyeBallShape"square" | "rounded" | "circle" | "leaf" | "diamond" | "custom"default "square"customShapesobjectdotany | nulleyeFrameany | nulleyeBallany | null
eyeFrameColorstring | nulleyeBallColorstring | nullmarginnumbercornerRadiusnumberlogoobjectsrcstring | nullsizenumberclearBehindbooleandefault truepaddingnumbershape"square" | "rounded" | "circle"default "rounded"iconobject | object | null
frameobjectstyle"none" | "banner-bottom" | "banner-top" | "box" | "ticket"default "none"textstringcolorstringdefault "#111111"textColorstringdefault "#ffffff"
imageMode"modules" | "halftone" | "photo"default "modules"colorSource"style" | "picture"default "style"detail"off" | "subject"default "subject"halftoneDither"ordered" | "diffusion" | "atkinson" | "threshold"default "ordered"shape"square" | "circle"default "square"ringFillbooleandefault true
pictureobject | nullPicture mode: a base64 data: URL, at most 768 px and 524,288 bytes; invalid_picture lists the formats read.
variant 1objectnamestringsrcstringplacementobject | nullvariant 1objectxnumberynumberscalenumberrotationnumberflipbooleandefault false
toneobject | nullvariant 1objectbrightnessnumbercontrastnumbermidtonesnumbersmoothnumber
maskobject | nullvariant 1objectwnumberhnumberdatastring
textobject | nullvariant 1objectvaluestringfont"sans" | "serif" | "rounded" | "mono" | "display"default "sans"solidbooleandefault true
pictureSettingsobjectPicture mode: strength, invert, background, lock, clearance and detail (at most 6 versions).
strengthnumberdetailnumberclearancenumberinvertbooleandefault falsebackground"light" | "dark"default "light"lockobject | nullvariant 1objectecl"L" | "M" | "Q" | "H"versioninteger 1–40maskinteger 0–7 | null
variantIndexnumber | nullretouchobject | nullIgnored: retouches belong to a variant picked in the studio.
variant 1objectkeystringcellsarray
Example: A link
{
"design": {
"contentType": "url",
"contentValues": {
"url": {
"url": "https://example.com"
}
}
}
}Example: A Wi-Fi network
{
"design": {
"contentType": "wifi",
"contentValues": {
"wifi": {
"ssid": "Studio",
"encryption": "WPA",
"password": "correct horse",
"hidden": false
}
},
"ecl": "Q"
}
}Example: Colours, shapes and a frame
{
"design": {
"contentType": "text",
"contentValues": {
"text": {
"text": "Hello from the API"
}
},
"style": {
"foreground": {
"type": "solid",
"color": "#006241"
},
"dotShape": "dots",
"eyeFrameShape": "rounded",
"eyeBallShape": "circle",
"frame": {
"style": "banner-bottom",
"text": "Scan me",
"color": "#006241",
"textColor": "#ffffff"
}
}
}
}Example: A logo
The code grows until it still reads with every module under the logo flipped.
{
"design": {
"contentType": "url",
"contentValues": {
"url": {
"url": "https://example.com"
}
},
"style": {
"logo": {
"src": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMCAxMCI+PGNpcmNsZSBjeD0iNSIgY3k9IjUiIHI9IjUiIGZpbGw9IiNmNzVjMDAiLz48L3N2Zz4="
}
}
}
}Responses
200 The code: SVG by default, JSON with Accept: application/json.
image/svg+xmlstring
application/jsonCodeJson
svgstringrequiredThe code, a standalone SVG: no external references, no scripts.
versioninteger 1–40requiredQR version of the symbol.
ecl"L" | "M" | "Q" | "H"requiredError correction level of the symbol.
maskinteger 0–7requiredMask pattern of the symbol.
sizeinteger ≥ 21requiredWidth of the symbol, in modules.
verifiedbooleanrequiredTrue only for a picture code that passed the server’s scan test. Classic codes are always false.
| Header | Type | Description |
|---|---|---|
| x- | integer 1–40 | QR version of the symbol. |
| x- | "L" | "M" | "Q" | "H" | Error correction level of the symbol. |
| x- | integer 0–7 | Mask pattern of the symbol. |
| x- | "true" | "false" | true only for a picture code that passed the server’s scan test. |
Errors
| Status | error | Message |
|---|---|---|
| 400 | invalid_ | The body is not valid JSON. |
| 400 | invalid_ | The body must be { "design": { "contentType", "contentValues", ... } } with a known content type and its values; "version" (optional) is a design version and "app" (optional) is "qr-atelier". |
| 400 | unsupported_ | This design is from a newer version of QR Atelier than this API reads: send version 4. |
| 405 | method_ | Use POST. |
| 413 | payload_ | The body is over 1,048,576 bytes: send smaller pictures or logos. |
| 415 | unsupported_ | Send the body as JSON (Content-Type: application/json). |
| 422 | invalid_ | The content is empty or invalid for its type: check contentValues[contentType]. |
| 422 | content_ | The content is too long for any QR code at this error correction level: shorten it, or lower ecl. |
| 422 | logo_ | The logo covers too much of the code: no version still reads with it. Make it smaller. |
| 422 | design_ | This design would draw too large an SVG (a long custom module shape repeated on a large code). Use a simpler shape or shorter content. |
| 422 | invalid_ | The picture must be a WebP, PNG or JPEG data: URL (base64) that decodes. |
| 422 | picture_ | The picture is over 768 px on its long side: scale it down to 768 px, the size the studio stores. |
| 422 | invalid_ | In picture mode the logo must be a WebP, PNG or JPEG data: URL (base64) of at most 1024 px, or a Lucide icon from the studio. Other SVG logos are refused: use a PNG instead. |
| 422 | unsupported_ | style.imageMode "photo" isn’t available through the API: use "modules" or "halftone", each with style.colorSource "style" or "picture". |
| 422 | picture_ | Picture codes take short content: about 150, 120, 105 and 95 characters of a link at levels L, M, Q and H (versions 7, 7, 8 and 9). Shorten the content, or use a classic code. |
| 429 | rate_ | Too many requests from this address: try again in 60 seconds. |
| 500 | internal_ | The server couldn’t make this code. Try again later. |
| Header | Type | Description |
|---|---|---|
| allow | "POST, OPTIONS" | The methods the route answers. |
| retry- | 60 | Seconds to wait before trying again. |
Limits
| Limit | Value | Notes |
|---|---|---|
| Request body | 1,048,576 bytes | Logos are data: URLs inside the design and count toward it. |
| Requests per address | 60 requests / 60 s | Per IPv4 address or IPv6 /64, counted per Cloudflare location. Refused requests count too. |
| Picture designs per address | 10 requests / 60 s | Among the requests above: picture codes cost up to two seconds of CPU. |
| Wait after a refusal | 60 s | Sent as Retry-After with 429. |
| Picture size | 768 px | On its long side, the size the studio stores. |
| Logo size in picture mode | 1,024 px | On its long side. |
| Picture or logo file | 524,288 bytes | Decoded size of each file in picture mode. |
| Picture content length | L: version 7, M: version 7, Q: version 8, H: version 9 | The QR version the content’s classic code may need, per error correction level. Longer content rarely passes the scan test and needs a classic code. |
| Picture detail | 6 versions | Extra versions the search explores; more is lowered to it. |
| Design version read | version 4 | Newer designs are refused with unsupported_version. |