{"openapi":"3.0.3","info":{"title":"QRocodile QR Code API","version":"1.0.0","description":"Generate styled QR codes for any content, without a browser — the same designs the [QR Designer](https://qrocodile.io/en/) produces, drawn by the same render engine.\n\n**Simple:** `GET /v1/qr?content=...&preset=...` returns the image directly.\n\n**Full control:** `POST /v1/qr` with a `{ content, design }` body for palettes, gradients, logos and halos. Design it visually in the [QR Designer](https://qrocodile.io/en/), open the encoded-content bar with the chevron at the bottom right of the preview, choose **Copy JSON**, and send that object as `design` — the render matches what you were looking at.\n\n**Keys.** Every `/v1/qr` request needs one: `Authorization: Bearer qk_live_…`. They are free — get one on the [qrocodile.io/en/qr-code-api](https://qrocodile.io/en/qr-code-api/) page, confirm the 6-digit code from the mail, and it is shown exactly once. Renders are capped at 60 per minute per key; ask us if you need more.\n\n**Output** is SVG or PNG, chosen by `format` alone — the `Accept` header is not consulted.\n\n**Failures** all answer `{ error: { code, message } }`, an unknown path included. `code` comes from a fixed set and is the field to branch on; `message` is for a human and may be reworded.\n\n**Not callable from a browser.** CORS withholds `Authorization` from browser origins on purpose, so an API key cannot end up in frontend code. Call the render endpoints from your own backend.\n\nThe preset, module-style and finder-style IDs, and the parameters each style accepts, are listed on the [API page](https://qrocodile.io/en/qr-code-api/).","contact":{"name":"QRocodile","url":"https://qrocodile.io/en/feedback/"}},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","bearerFormat":"qk_live_…","description":"Your API key, sent as a bearer token: `Authorization: Bearer qk_live_…`. Get one free on the [qrocodile.io/en/qr-code-api](https://qrocodile.io/en/qr-code-api/) page: enter your email, confirm the 6-digit code from the mail, and the key is shown exactly once — it cannot be retrieved afterwards. Keep it private: it identifies your usage, and anyone who holds it can spend your quota."}},"schemas":{}},"paths":{"/v1/health":{"get":{"operationId":"getHealth","summary":"Health check","tags":["health"],"description":"Basic liveness check. Returns 200 when the API is up.","responses":{"200":{"description":"The API is up. This says nothing about the database — nothing here connects to it — so it is a liveness check, not a readiness one.","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","description":"Always `ok`. A process that cannot answer is down."},"timestamp":{"type":"string","description":"When the API answered, as an ISO 8601 timestamp in UTC."}},"required":["status","timestamp"],"additionalProperties":false,"description":"The API is up. This says nothing about the database — nothing here connects to it — so it is a liveness check, not a readiness one.","example":{"status":"ok","timestamp":"2026-07-12T10:30:00.000Z"}}}},"headers":{}}}}},"/v1/keys":{"post":{"operationId":"registerKey","summary":"Request an API key","tags":["keys"],"description":"Start API-key signup for an email address. Emails a 6-digit verification code, valid for 24 hours; no API key is issued until the code is confirmed via POST /v1/keys/confirm. Asking again for an address that already has a key is how you replace a lost one — confirming rotates it in place. A repeat request within two minutes sends no second email, so an impatient retry cannot invalidate the code that just arrived. Rate-limited to five requests per hour per IP address.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","maxLength":254,"format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$","description":"The address the API key is issued to. The 6-digit verification code is sent here. Confirming again later rotates the key in place, so this address stays the account."},"lang":{"default":"en","description":"Language for the verification email.","type":"string","enum":["en","de"]}},"required":["email"]}}}},"responses":{"202":{"description":"The signup was accepted, and the code is on its way if the address can receive it. Deliberately identical whether or not that address already has a key, so this endpoint cannot be used to find out who holds an account — which also means it is not a delivery receipt. If no code arrives, the address was undeliverable or the mail is still in flight.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"A sentence to show a human. Not worth branching on — it is the same sentence in every case."}},"required":["message"],"additionalProperties":false,"description":"The signup was accepted, and the code is on its way if the address can receive it. Deliberately identical whether or not that address already has a key, so this endpoint cannot be used to find out who holds an account — which also means it is not a delivery receipt. If no code arrives, the address was undeliverable or the mail is still in flight.","example":{"message":"Check your email for the verification code to receive your API key."}}}},"headers":{"x-ratelimit-limit":{"schema":{"type":"integer"},"description":"Requests allowed in the current window."},"x-ratelimit-remaining":{"schema":{"type":"integer"},"description":"Requests still allowed in the current window."},"x-ratelimit-reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets and `x-ratelimit-remaining` returns to `x-ratelimit-limit`."}}},"400":{"description":"The email address is missing or not an address.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["VALIDATION_ERROR","UNAUTHORIZED","NOT_FOUND","PAYLOAD_TOO_LARGE","UNSUPPORTED_MEDIA_TYPE","UNPROCESSABLE","RATE_LIMITED","INTERNAL_ERROR"],"description":"Machine-readable failure code. Stable across releases, so this is the field to branch on. `message` is not stable — it is written for a human reading a log and may be reworded at any time."},"message":{"type":"string","description":"What went wrong, in English, for a human to read. Do not match on it — see `code`."}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"description":"The email address is missing or not an address.","example":{"error":{"code":"VALIDATION_ERROR","message":"body/email Invalid email address"}}}}},"headers":{"x-ratelimit-limit":{"schema":{"type":"integer"},"description":"Requests allowed in the current window."},"x-ratelimit-remaining":{"schema":{"type":"integer"},"description":"Requests still allowed in the current window."},"x-ratelimit-reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets and `x-ratelimit-remaining` returns to `x-ratelimit-limit`."}}},"415":{"description":"The request body arrived with a `Content-Type` the API has no parser for. Send `application/json`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["VALIDATION_ERROR","UNAUTHORIZED","NOT_FOUND","PAYLOAD_TOO_LARGE","UNSUPPORTED_MEDIA_TYPE","UNPROCESSABLE","RATE_LIMITED","INTERNAL_ERROR"],"description":"Machine-readable failure code. Stable across releases, so this is the field to branch on. `message` is not stable — it is written for a human reading a log and may be reworded at any time."},"message":{"type":"string","description":"What went wrong, in English, for a human to read. Do not match on it — see `code`."}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"description":"The request body arrived with a `Content-Type` the API has no parser for. Send `application/json`.","example":{"error":{"code":"UNSUPPORTED_MEDIA_TYPE","message":"Unsupported Media Type"}}}}},"headers":{"x-ratelimit-limit":{"schema":{"type":"integer"},"description":"Requests allowed in the current window."},"x-ratelimit-remaining":{"schema":{"type":"integer"},"description":"Requests still allowed in the current window."},"x-ratelimit-reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets and `x-ratelimit-remaining` returns to `x-ratelimit-limit`."}}},"429":{"description":"Rate limit exceeded. Signup is capped per IP address — five key requests and twenty confirmation attempts per hour. See the response headers below for how long to wait.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["VALIDATION_ERROR","UNAUTHORIZED","NOT_FOUND","PAYLOAD_TOO_LARGE","UNSUPPORTED_MEDIA_TYPE","UNPROCESSABLE","RATE_LIMITED","INTERNAL_ERROR"],"description":"Machine-readable failure code. Stable across releases, so this is the field to branch on. `message` is not stable — it is written for a human reading a log and may be reworded at any time."},"message":{"type":"string","description":"What went wrong, in English, for a human to read. Do not match on it — see `code`."}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"description":"Rate limit exceeded. Signup is capped per IP address — five key requests and twenty confirmation attempts per hour. See the response headers below for how long to wait.","example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Please try again in 1 hour."}}}}},"headers":{"x-ratelimit-limit":{"schema":{"type":"integer"},"description":"Requests allowed in the current window."},"x-ratelimit-remaining":{"schema":{"type":"integer"},"description":"Requests still allowed in the current window."},"x-ratelimit-reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets and `x-ratelimit-remaining` returns to `x-ratelimit-limit`."},"retry-after":{"schema":{"type":"integer"},"description":"Seconds to wait before retrying. Sent only with a 429."}}},"500":{"description":"Something failed on our side — including a mail server that would not accept the verification email, in which case no code was sent and the request is worth retrying.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["VALIDATION_ERROR","UNAUTHORIZED","NOT_FOUND","PAYLOAD_TOO_LARGE","UNSUPPORTED_MEDIA_TYPE","UNPROCESSABLE","RATE_LIMITED","INTERNAL_ERROR"],"description":"Machine-readable failure code. Stable across releases, so this is the field to branch on. `message` is not stable — it is written for a human reading a log and may be reworded at any time."},"message":{"type":"string","description":"What went wrong, in English, for a human to read. Do not match on it — see `code`."}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"description":"Something failed on our side — including a mail server that would not accept the verification email, in which case no code was sent and the request is worth retrying.","example":{"error":{"code":"INTERNAL_ERROR","message":"An unexpected error occurred."}}}}},"headers":{"x-ratelimit-limit":{"schema":{"type":"integer"},"description":"Requests allowed in the current window."},"x-ratelimit-remaining":{"schema":{"type":"integer"},"description":"Requests still allowed in the current window."},"x-ratelimit-reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets and `x-ratelimit-remaining` returns to `x-ratelimit-limit`."}}}}}},"/v1/keys/confirm":{"post":{"operationId":"confirmKey","summary":"Confirm the code and receive the API key","tags":["keys"],"description":"Exchange the 6-digit code from the verification email for the API key. Identify the pending signup with either `email` or the `rid` from the email link. The API key is returned exactly once — it is stored hashed and cannot be recovered. Five wrong attempts discard the pending signup. If the address already had a key, this replaces it and the old one stops working immediately.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","pattern":"^\\d{6}$","description":"The 6-digit code from the verification email. Five wrong attempts void the pending signup and you have to start over."},"email":{"description":"The address the code was sent to. Provide either this or `rid`.","type":"string","maxLength":254,"format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"rid":{"description":"Reference from the link in the verification email (`?r=…`), so someone arriving that way need not retype their address. Provide either this or `email`. It selects the pending signup but authorizes nothing on its own — the code does that.","type":"string","pattern":"^[0-9a-f]{32}$"}},"required":["code"]}}}},"responses":{"200":{"description":"The code was correct. The key is in the body, for the only time.","content":{"application/json":{"schema":{"type":"object","properties":{"apiKey":{"type":"string","description":"The API key, in full, the only time it is ever shown. It is stored as a SHA-256 hash, so it cannot be recovered or displayed again — store it before you discard the response."},"replaced":{"type":"boolean","description":"Whether this address already had a key. `true` means the previous one stopped working the moment this one was issued, so anything still using it now gets a 401. `false` is a first-time signup."}},"required":["apiKey","replaced"],"additionalProperties":false,"description":"The code was correct. The key is in the body, for the only time.","example":{"apiKey":"qk_live_EXAMPLEONLYnotarealkey","replaced":false}}}},"headers":{"x-ratelimit-limit":{"schema":{"type":"integer"},"description":"Requests allowed in the current window."},"x-ratelimit-remaining":{"schema":{"type":"integer"},"description":"Requests still allowed in the current window."},"x-ratelimit-reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets and `x-ratelimit-remaining` returns to `x-ratelimit-limit`."}}},"400":{"description":"The code is malformed, the email address is not an address, or neither identifier was supplied.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["VALIDATION_ERROR","UNAUTHORIZED","NOT_FOUND","PAYLOAD_TOO_LARGE","UNSUPPORTED_MEDIA_TYPE","UNPROCESSABLE","RATE_LIMITED","INTERNAL_ERROR"],"description":"Machine-readable failure code. Stable across releases, so this is the field to branch on. `message` is not stable — it is written for a human reading a log and may be reworded at any time."},"message":{"type":"string","description":"What went wrong, in English, for a human to read. Do not match on it — see `code`."}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"description":"The code is malformed, the email address is not an address, or neither identifier was supplied.","example":{"error":{"code":"VALIDATION_ERROR","message":"body/code The code is 6 digits"}}}}},"headers":{"x-ratelimit-limit":{"schema":{"type":"integer"},"description":"Requests allowed in the current window."},"x-ratelimit-remaining":{"schema":{"type":"integer"},"description":"Requests still allowed in the current window."},"x-ratelimit-reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets and `x-ratelimit-remaining` returns to `x-ratelimit-limit`."}}},"401":{"description":"The code is wrong, expired, or the pending signup was burned by too many attempts. One message covers all three on purpose, so a wrong guess reveals nothing about which part was wrong.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["VALIDATION_ERROR","UNAUTHORIZED","NOT_FOUND","PAYLOAD_TOO_LARGE","UNSUPPORTED_MEDIA_TYPE","UNPROCESSABLE","RATE_LIMITED","INTERNAL_ERROR"],"description":"Machine-readable failure code. Stable across releases, so this is the field to branch on. `message` is not stable — it is written for a human reading a log and may be reworded at any time."},"message":{"type":"string","description":"What went wrong, in English, for a human to read. Do not match on it — see `code`."}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"description":"The code is wrong, expired, or the pending signup was burned by too many attempts. One message covers all three on purpose, so a wrong guess reveals nothing about which part was wrong.","example":{"error":{"code":"UNAUTHORIZED","message":"That code is invalid or has expired. Please request a new one."}}}}},"headers":{"x-ratelimit-limit":{"schema":{"type":"integer"},"description":"Requests allowed in the current window."},"x-ratelimit-remaining":{"schema":{"type":"integer"},"description":"Requests still allowed in the current window."},"x-ratelimit-reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets and `x-ratelimit-remaining` returns to `x-ratelimit-limit`."}}},"415":{"description":"The request body arrived with a `Content-Type` the API has no parser for. Send `application/json`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["VALIDATION_ERROR","UNAUTHORIZED","NOT_FOUND","PAYLOAD_TOO_LARGE","UNSUPPORTED_MEDIA_TYPE","UNPROCESSABLE","RATE_LIMITED","INTERNAL_ERROR"],"description":"Machine-readable failure code. Stable across releases, so this is the field to branch on. `message` is not stable — it is written for a human reading a log and may be reworded at any time."},"message":{"type":"string","description":"What went wrong, in English, for a human to read. Do not match on it — see `code`."}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"description":"The request body arrived with a `Content-Type` the API has no parser for. Send `application/json`.","example":{"error":{"code":"UNSUPPORTED_MEDIA_TYPE","message":"Unsupported Media Type"}}}}},"headers":{"x-ratelimit-limit":{"schema":{"type":"integer"},"description":"Requests allowed in the current window."},"x-ratelimit-remaining":{"schema":{"type":"integer"},"description":"Requests still allowed in the current window."},"x-ratelimit-reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets and `x-ratelimit-remaining` returns to `x-ratelimit-limit`."}}},"429":{"description":"Rate limit exceeded. Signup is capped per IP address — five key requests and twenty confirmation attempts per hour. See the response headers below for how long to wait.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["VALIDATION_ERROR","UNAUTHORIZED","NOT_FOUND","PAYLOAD_TOO_LARGE","UNSUPPORTED_MEDIA_TYPE","UNPROCESSABLE","RATE_LIMITED","INTERNAL_ERROR"],"description":"Machine-readable failure code. Stable across releases, so this is the field to branch on. `message` is not stable — it is written for a human reading a log and may be reworded at any time."},"message":{"type":"string","description":"What went wrong, in English, for a human to read. Do not match on it — see `code`."}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"description":"Rate limit exceeded. Signup is capped per IP address — five key requests and twenty confirmation attempts per hour. See the response headers below for how long to wait.","example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Please try again in 1 hour."}}}}},"headers":{"x-ratelimit-limit":{"schema":{"type":"integer"},"description":"Requests allowed in the current window."},"x-ratelimit-remaining":{"schema":{"type":"integer"},"description":"Requests still allowed in the current window."},"x-ratelimit-reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets and `x-ratelimit-remaining` returns to `x-ratelimit-limit`."},"retry-after":{"schema":{"type":"integer"},"description":"Seconds to wait before retrying. Sent only with a 429."}}},"500":{"description":"Something failed on our side — including a mail server that would not accept the verification email, in which case no code was sent and the request is worth retrying.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["VALIDATION_ERROR","UNAUTHORIZED","NOT_FOUND","PAYLOAD_TOO_LARGE","UNSUPPORTED_MEDIA_TYPE","UNPROCESSABLE","RATE_LIMITED","INTERNAL_ERROR"],"description":"Machine-readable failure code. Stable across releases, so this is the field to branch on. `message` is not stable — it is written for a human reading a log and may be reworded at any time."},"message":{"type":"string","description":"What went wrong, in English, for a human to read. Do not match on it — see `code`."}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"description":"Something failed on our side — including a mail server that would not accept the verification email, in which case no code was sent and the request is worth retrying.","example":{"error":{"code":"INTERNAL_ERROR","message":"An unexpected error occurred."}}}}},"headers":{"x-ratelimit-limit":{"schema":{"type":"integer"},"description":"Requests allowed in the current window."},"x-ratelimit-remaining":{"schema":{"type":"integer"},"description":"Requests still allowed in the current window."},"x-ratelimit-reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets and `x-ratelimit-remaining` returns to `x-ratelimit-limit`."}}}}}},"/v1/qr":{"get":{"operationId":"renderQrCode","summary":"Render a QR code (simple)","tags":["qr"],"description":"Render a QR code from a content string, plus a preset and a few colors if you want them. The content is encoded exactly as given, and the image itself is the response body. Colors too close in contrast to scan reliably are nudged apart, always: POST /v1/qr can switch that off with `fixContrast`, this route cannot. For a full design — palettes, gradients, logos, halos — use POST /v1/qr.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":4096,"example":"https://qrocodile.io"},"in":"query","name":"content","required":true,"description":"The value encoded into the QR code, verbatim — usually a URL, but any string works, including payload formats such as `WIFI:T:WPA;S:Cafe;P:secret;;`, `mailto:…` or a vCard. The API does not assemble those formats for you. The 4,096-character cap is a safety bound, not the real ceiling: a QR code holds roughly 2,300 bytes at most, and content past what the encoder can fit comes back as a 422 rather than an image."},{"schema":{"default":"svg","type":"string","enum":["svg","png"]},"in":"query","name":"format","required":false,"description":"Output format. This alone selects the response media type — the `Accept` header is not consulted."},{"schema":{"example":"ocean","type":"string","enum":["classic","modern","elegant","midnight","ocean","fireworks","lazyArt","sunset","forest","brushstroke","inkBloom","brushFiesta","calligraphy","mosaic","doodle","paintSplash","critters","pacman","halloween","brixx","tetris","bubbles","botanical","autumn","grass","bacteria","organic","circuit","connected","rainyDay","blocky","christmas","valentines","icy","summer","newYear","fiesta","carnival","purple-chain","explosion","nickelodeon","glitched","cyberpunk","handmade","retroWave","neonDrip","slimer","arcade","tropicalPunch","discoFloor","vinylGroove","zigzag","midCentury","retroTriangle","retroWave70s","synthwave","hypnotic","pinwheel","galaxy","patchwork","stitches","puzzle","instagram","whatsapp","youtube","tiktok","linkedin","facebook","darkBush","minecraft","architecture","candyBlocks","escher","dither","ditherRainbow","deepBlue","oldFilm","el-nino","petals","washi-tape","comic-fries","vernissage","comic-bricks","windswept","woodwork","blotchy","morseCode","ripple","florist","boa","creeper","bats","flourish","bigFish","paperKoi","deepSea"]},"in":"query","name":"preset","required":false,"description":"A built-in design preset, which brings the module and finder styles along with its own colors. Every preset is shown, by name, in the [QR Designer](https://qrocodile.io/en/) — the enum here lists the IDs but not what they look like. `background` and `margin` always override it. `moduleColor` fully repaints the pattern only for presets that draw their modules in a single color. Most carry a multi-color palette, which takes precedence, and then `moduleColor` changes part of the design or nothing at all — recolor one of those with `modulePalette` on POST /v1/qr instead."},{"schema":{"example":512,"type":"integer","minimum":64,"maximum":4096},"in":"query","name":"size","required":false,"description":"Image width and height in pixels. Defaults to 300 for SVG and 1,024 for PNG."},{"schema":{"default":2,"type":"integer","minimum":0,"maximum":20},"in":"query","name":"margin","required":false,"description":"Quiet zone around the QR code, in modules. One module is the minimum, so 0 and 1 both render a single-module zone."},{"schema":{"default":"#000000","type":"string","pattern":"^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$"},"in":"query","name":"moduleColor","required":false,"description":"Module (foreground) color, as `#rgb`, `#rrggbb` or `#rrggbbaa`. A preset sets its own — see `preset` for when this overrides it. For a gradient or a multi-color palette, use POST /v1/qr."},{"schema":{"default":"#ffffff","anyOf":[{"type":"string","pattern":"^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$"},{"type":"string","enum":["transparent"]}]},"in":"query","name":"background","required":false,"description":"Background color, as `#rgb`, `#rrggbb` or `#rrggbbaa`, or `transparent` to leave it unpainted. A preset sets its own, which this overrides."}],"security":[{"apiKey":[]}],"responses":{"200":{"description":"The rendered QR code image — SVG or PNG per the requested `format`. The response `Content-Type` names the format actually returned and is authoritative.","content":{"image/svg+xml":{"schema":{"type":"string","format":"binary","description":"SVG markup, UTF-8. Returned for `format=svg`, the default. Square, sized by `size`, and self-contained — a logo travels inside it as data, never as a link."}},"image/png":{"schema":{"type":"string","format":"binary","description":"PNG bytes, rasterized from the same SVG at `size` pixels square. Returned for `format=png`."}}},"headers":{"x-ratelimit-limit":{"schema":{"type":"integer"},"description":"Requests allowed in the current window."},"x-ratelimit-remaining":{"schema":{"type":"integer"},"description":"Requests still allowed in the current window."},"x-ratelimit-reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets and `x-ratelimit-remaining` returns to `x-ratelimit-limit`."}}},"400":{"description":"A parameter failed validation, or the JSON body could not be parsed.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["VALIDATION_ERROR","UNAUTHORIZED","NOT_FOUND","PAYLOAD_TOO_LARGE","UNSUPPORTED_MEDIA_TYPE","UNPROCESSABLE","RATE_LIMITED","INTERNAL_ERROR"],"description":"Machine-readable failure code. Stable across releases, so this is the field to branch on. `message` is not stable — it is written for a human reading a log and may be reworded at any time."},"message":{"type":"string","description":"What went wrong, in English, for a human to read. Do not match on it — see `code`."}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"description":"A parameter failed validation, or the JSON body could not be parsed.","example":{"error":{"code":"VALIDATION_ERROR","message":"querystring/size Too big: expected number to be <=4096"}}}}},"headers":{"x-ratelimit-limit":{"schema":{"type":"integer"},"description":"Requests allowed in the current window."},"x-ratelimit-remaining":{"schema":{"type":"integer"},"description":"Requests still allowed in the current window."},"x-ratelimit-reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets and `x-ratelimit-remaining` returns to `x-ratelimit-limit`."}}},"401":{"description":"API key missing, malformed, unknown, or revoked.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["VALIDATION_ERROR","UNAUTHORIZED","NOT_FOUND","PAYLOAD_TOO_LARGE","UNSUPPORTED_MEDIA_TYPE","UNPROCESSABLE","RATE_LIMITED","INTERNAL_ERROR"],"description":"Machine-readable failure code. Stable across releases, so this is the field to branch on. `message` is not stable — it is written for a human reading a log and may be reworded at any time."},"message":{"type":"string","description":"What went wrong, in English, for a human to read. Do not match on it — see `code`."}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"description":"API key missing, malformed, unknown, or revoked.","example":{"error":{"code":"UNAUTHORIZED","message":"Missing API key. Send it as: Authorization: Bearer qk_live_…."}}}}},"headers":{"x-ratelimit-limit":{"schema":{"type":"integer"},"description":"Requests allowed in the current window."},"x-ratelimit-remaining":{"schema":{"type":"integer"},"description":"Requests still allowed in the current window."},"x-ratelimit-reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets and `x-ratelimit-remaining` returns to `x-ratelimit-limit`."}}},"422":{"description":"Valid input that cannot be encoded — in practice, content too long for a QR code. A QR code holds roughly 2,300 bytes at most, well under the 4,096 characters `content` accepts.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["VALIDATION_ERROR","UNAUTHORIZED","NOT_FOUND","PAYLOAD_TOO_LARGE","UNSUPPORTED_MEDIA_TYPE","UNPROCESSABLE","RATE_LIMITED","INTERNAL_ERROR"],"description":"Machine-readable failure code. Stable across releases, so this is the field to branch on. `message` is not stable — it is written for a human reading a log and may be reworded at any time."},"message":{"type":"string","description":"What went wrong, in English, for a human to read. Do not match on it — see `code`."}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"description":"Valid input that cannot be encoded — in practice, content too long for a QR code. A QR code holds roughly 2,300 bytes at most, well under the 4,096 characters `content` accepts.","example":{"error":{"code":"UNPROCESSABLE","message":"The content is too long to encode in a QR code (3200 characters)."}}}}},"headers":{"x-ratelimit-limit":{"schema":{"type":"integer"},"description":"Requests allowed in the current window."},"x-ratelimit-remaining":{"schema":{"type":"integer"},"description":"Requests still allowed in the current window."},"x-ratelimit-reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets and `x-ratelimit-remaining` returns to `x-ratelimit-limit`."}}},"429":{"description":"Rate limit exceeded — renders are capped at 60 per minute per API key. See the response headers below for how long to wait. The `x-ratelimit-*` three come back on every response, successful ones included, so a client can pace itself without ever provoking this.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["VALIDATION_ERROR","UNAUTHORIZED","NOT_FOUND","PAYLOAD_TOO_LARGE","UNSUPPORTED_MEDIA_TYPE","UNPROCESSABLE","RATE_LIMITED","INTERNAL_ERROR"],"description":"Machine-readable failure code. Stable across releases, so this is the field to branch on. `message` is not stable — it is written for a human reading a log and may be reworded at any time."},"message":{"type":"string","description":"What went wrong, in English, for a human to read. Do not match on it — see `code`."}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"description":"Rate limit exceeded — renders are capped at 60 per minute per API key. See the response headers below for how long to wait. The `x-ratelimit-*` three come back on every response, successful ones included, so a client can pace itself without ever provoking this.","example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Please try again in 1 minute."}}}}},"headers":{"x-ratelimit-limit":{"schema":{"type":"integer"},"description":"Requests allowed in the current window."},"x-ratelimit-remaining":{"schema":{"type":"integer"},"description":"Requests still allowed in the current window."},"x-ratelimit-reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets and `x-ratelimit-remaining` returns to `x-ratelimit-limit`."},"retry-after":{"schema":{"type":"integer"},"description":"Seconds to wait before retrying. Sent only with a 429."}}},"500":{"description":"Render failure. Logged on our side, deliberately without the content or design that caused it — so if you hit one, tell us what you sent.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["VALIDATION_ERROR","UNAUTHORIZED","NOT_FOUND","PAYLOAD_TOO_LARGE","UNSUPPORTED_MEDIA_TYPE","UNPROCESSABLE","RATE_LIMITED","INTERNAL_ERROR"],"description":"Machine-readable failure code. Stable across releases, so this is the field to branch on. `message` is not stable — it is written for a human reading a log and may be reworded at any time."},"message":{"type":"string","description":"What went wrong, in English, for a human to read. Do not match on it — see `code`."}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"description":"Render failure. Logged on our side, deliberately without the content or design that caused it — so if you hit one, tell us what you sent.","example":{"error":{"code":"INTERNAL_ERROR","message":"An unexpected error occurred."}}}}},"headers":{"x-ratelimit-limit":{"schema":{"type":"integer"},"description":"Requests allowed in the current window."},"x-ratelimit-remaining":{"schema":{"type":"integer"},"description":"Requests still allowed in the current window."},"x-ratelimit-reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets and `x-ratelimit-remaining` returns to `x-ratelimit-limit`."}}}}},"post":{"operationId":"renderQrCodeWithDesign","summary":"Render a QR code (full design)","tags":["qr"],"description":"Render a QR code from a content string and a full design — the object the QR Designer’s “Copy JSON” button produces (see the `design` field for how to get it). Returns the image bytes. Despite the POST verb, which is here only to carry the body, this is a safe and idempotent operation with no side effects: a response may be cached, and a request may be retried — though every retry spends one of the minute’s sixty.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"string","minLength":1,"maxLength":4096,"description":"The value encoded into the QR code, verbatim — usually a URL, but any string works, including payload formats such as `WIFI:T:WPA;S:Cafe;P:secret;;`, `mailto:…` or a vCard. The API does not assemble those formats for you. The 4,096-character cap is a safety bound, not the real ceiling: a QR code holds roughly 2,300 bytes at most, and content past what the encoder can fit comes back as a 422 rather than an image."},"design":{"default":{},"description":"The full design — module and finder styles, colors, logo and halo. The [QR Designer](https://qrocodile.io/en/) produces exactly this object: build the QR code there, open the encoded-content bar with the chevron at the bottom right of the preview, then choose “Copy JSON” and paste the result here. Every ID and style parameter is also tabulated on the [API page](https://qrocodile.io/en/qr-code-api/). One exception: an animated design also carries an `animation` field, which this endpoint rejects because it renders a single image — remove that field to render a still.","type":"object","properties":{"preset":{"description":"A built-in preset loaded as the starting point. Fields set alongside it override the preset’s own values, so a preset plus `moduleColor` is a recolored preset.","type":"string","enum":["classic","modern","elegant","midnight","ocean","fireworks","lazyArt","sunset","forest","brushstroke","inkBloom","brushFiesta","calligraphy","mosaic","doodle","paintSplash","critters","pacman","halloween","brixx","tetris","bubbles","botanical","autumn","grass","bacteria","organic","circuit","connected","rainyDay","blocky","christmas","valentines","icy","summer","newYear","fiesta","carnival","purple-chain","explosion","nickelodeon","glitched","cyberpunk","handmade","retroWave","neonDrip","slimer","arcade","tropicalPunch","discoFloor","vinylGroove","zigzag","midCentury","retroTriangle","retroWave70s","synthwave","hypnotic","pinwheel","galaxy","patchwork","stitches","puzzle","instagram","whatsapp","youtube","tiktok","linkedin","facebook","darkBush","minecraft","architecture","candyBlocks","escher","dither","ditherRainbow","deepBlue","oldFilm","el-nino","petals","washi-tape","comic-fries","vernissage","comic-bricks","windswept","woodwork","blotchy","morseCode","ripple","florist","boa","creeper","bats","flourish","bigFish","paperKoi","deepSea"]},"moduleStyleId":{"description":"Shape the modules (the pattern) are drawn with. The enum lists every style this build can render.","type":"string","enum":["classic","photo-overlay","bacteria","blocky","botanical","bubbles","calligraphy","vortex","chevron","circuits","connected","diamond","doodle","double-bubble","drip","explosion","film","fireworks","fish","splatter","brush","ink-pen","washi-tape","comic-fries","woodwork","blotchy","glitch","grass","halfmoon","flourish","spooky","leaves","characters","brixx","wavy","dither","megaShapes","mosaic","neighborAware","organic","pacman","patchwork","pizza","puzzle","raindrop","artist","paint-strokes","retro","rotated","roundedDots","roundedSquares","tetris","sketch","3d","triangle","spiky","overlay","petals","variedDots","ripple","isometric"]},"moduleStyleParams":{"type":"object","additionalProperties":{"anyOf":[{"type":"number"},{"type":"string","maxLength":256}]},"description":"Tuning parameters for the module style. Which keys it accepts depends on the style: the [QR Designer](https://qrocodile.io/en/) exposes them as that style’s own sliders, and they are tabulated per style on the [API page](https://qrocodile.io/en/qr-code-api/). An unrecognized key is ignored rather than rejected — a style that drops a parameter should not break a stored design — though a key over 64 characters is refused."},"finderStyleId":{"description":"Shape the three corner finders are drawn with.","type":"string","enum":["classic","modern","brush","wobbly","carved","scribble","drip","eroded","comic","blotchy","shifted","meadow","leaf","morphing","pill","sketchy","stamp","sticker","segmented","dotted"]},"finderStyleParams":{"type":"object","additionalProperties":{"anyOf":[{"type":"number"},{"type":"string","maxLength":256}]},"description":"Tuning parameters for the finder style. Which keys it accepts depends on the style: the [QR Designer](https://qrocodile.io/en/) exposes them as that style’s own sliders, and they are tabulated per style on the [API page](https://qrocodile.io/en/qr-code-api/). An unrecognized key is ignored rather than rejected — a style that drops a parameter should not break a stored design — though a key over 64 characters is refused."},"finderUseModuleStyle":{"description":"Let the module style draw the corner finders itself. Only effective for styles that can render finders, and the only way to express relief and 3D designs whose corners must stay depth-sorted with the pattern. Supersedes `finderStyleId`.","type":"boolean"},"finderColorInherit":{"description":"Corners take their per-cell color from the pattern instead of `finderFrameColor` and `finderEyeColor`.","type":"boolean"},"moduleColor":{"description":"Color of the modules — a solid hex color or a gradient.","default":"#000000","anyOf":[{"type":"string","pattern":"^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$"},{"type":"object","properties":{"type":{"type":"string","enum":["linear","radial"],"description":"Gradient geometry."},"angle":{"description":"Angle of a linear gradient, in degrees. Ignored by a radial gradient.","default":0,"type":"number"},"stops":{"minItems":2,"maxItems":16,"type":"array","items":{"anyOf":[{"type":"string","description":"A color on its own, placed evenly among the other stops. Use hex — `#0d9488`."},{"type":"object","properties":{"color":{"type":"string","description":"The stop’s color, as hex — `#0d9488`."},"offset":{"type":"number","minimum":0,"maximum":1,"description":"Where the stop sits along the gradient, 0 = start, 1 = end."}},"required":["color","offset"]}]},"description":"Two to sixteen color stops, spread evenly unless placed. A stop is either a color string or `{ color, offset }` with `offset` between 0 and 1."}},"required":["type","stops"]}]},"modulePalette":{"description":"Draw the modules from several colors at once, arranged by `paletteMode`. Use this instead of `moduleColor` for multi-color patterns.","maxItems":32,"type":"array","items":{"type":"string","pattern":"^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$"}},"paletteMode":{"default":"scatter","type":"string","enum":["scatter","horizontal","vertical","diagonal","radial","checkerboard","spiral","wave","noise","rings","mandala","lava","group"],"description":"How the palette colors are distributed across the modules."},"paletteScale":{"description":"Scale of the pattern that `paletteMode` lays over the modules.","default":1,"type":"number","minimum":0,"exclusiveMinimum":true,"maximum":100},"paletteDither":{"description":"Jitter of the color-sample position, in modules, which breaks hard palette bands into a grain, off at 0.","default":0,"type":"number","minimum":0,"maximum":10},"finderFrameColor":{"description":"Color of the outer frame of each corner finder.","anyOf":[{"type":"string","pattern":"^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$"},{"type":"object","properties":{"type":{"type":"string","enum":["linear","radial"],"description":"Gradient geometry."},"angle":{"description":"Angle of a linear gradient, in degrees. Ignored by a radial gradient.","default":0,"type":"number"},"stops":{"minItems":2,"maxItems":16,"type":"array","items":{"anyOf":[{"type":"string","description":"A color on its own, placed evenly among the other stops. Use hex — `#0d9488`."},{"type":"object","properties":{"color":{"type":"string","description":"The stop’s color, as hex — `#0d9488`."},"offset":{"type":"number","minimum":0,"maximum":1,"description":"Where the stop sits along the gradient, 0 = start, 1 = end."}},"required":["color","offset"]}]},"description":"Two to sixteen color stops, spread evenly unless placed. A stop is either a color string or `{ color, offset }` with `offset` between 0 and 1."}},"required":["type","stops"]}]},"finderEyeColor":{"description":"Color of the inner eye of each corner finder.","anyOf":[{"type":"string","pattern":"^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$"},{"type":"object","properties":{"type":{"type":"string","enum":["linear","radial"],"description":"Gradient geometry."},"angle":{"description":"Angle of a linear gradient, in degrees. Ignored by a radial gradient.","default":0,"type":"number"},"stops":{"minItems":2,"maxItems":16,"type":"array","items":{"anyOf":[{"type":"string","description":"A color on its own, placed evenly among the other stops. Use hex — `#0d9488`."},{"type":"object","properties":{"color":{"type":"string","description":"The stop’s color, as hex — `#0d9488`."},"offset":{"type":"number","minimum":0,"maximum":1,"description":"Where the stop sits along the gradient, 0 = start, 1 = end."}},"required":["color","offset"]}]},"description":"Two to sixteen color stops, spread evenly unless placed. A stop is either a color string or `{ color, offset }` with `offset` between 0 and 1."}},"required":["type","stops"]}]},"background":{"description":"Background color, or `transparent` to leave it unpainted.","default":"#ffffff","anyOf":[{"type":"string","pattern":"^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$"},{"type":"string","enum":["transparent"]}]},"margin":{"description":"Quiet zone around the QR code, in modules. One module is the minimum, so 0 and 1 both render a single-module zone.","default":2,"type":"integer","minimum":0,"maximum":20},"errorCorrection":{"description":"Error correction level. Omit to auto-resolve from the logo/style — set this only when a design has a fixed level rather than an auto-resolved one, e.g. a content type whose standard mandates it (L 7%, M 15%, Q 25%, H 30% redundancy).","type":"string","enum":["L","M","Q","H"]},"logo":{"description":"A logo placed on the QR code: either a built-in icon by `id`, or your own artwork as base64 in `data`. Not both.","anyOf":[{"type":"object","properties":{"id":{"type":"string","enum":["youtube","instagram","facebook","x","linkedin","tiktok","snapchat","pinterest","threads","bluesky","whatsapp","telegram","discord","spotify","apple","twitch","website","email","phone","location","wifi","google","paypal","amazon","googleplay","euro","swissCross","github","kim"],"description":"Which built-in icon to place. The enum lists every icon this build ships."},"color":{"type":"string","pattern":"^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"Color the icon is recolored to."},"regionWidth":{"description":"Logo width as a fraction of the QR code’s width, between 0.1 and 1. The height follows from the artwork’s aspect ratio.","default":0.8,"type":"number","minimum":0.1,"maximum":1},"mode":{"type":"string","enum":["clear","float","fill"],"description":"How the modules behave around the logo. `clear` clears a plain rectangle behind it; `float` clears only where the logo is opaque, so the pattern flows around the silhouette — use it for logos with a transparent background; `fill` clears nothing and draws the modules behind the logo.","default":"clear"},"halo":{"description":"Extra cleared margin around the logo silhouette, in modules, which keeps floating modules off the logo’s edge. Float mode only.","default":0.75,"type":"number","minimum":0,"maximum":4},"fillInterior":{"description":"Draw modules inside the logo’s enclosed gaps — the counter of an “o”, for instance — instead of leaving them clear. Float mode only.","default":false,"type":"boolean"},"posX":{"description":"Horizontal placement of the logo: 0 pushes it as far left as it goes, 0.5 centers it, 1 as far right. The logo stays fully inside the image at either extreme, so neither end crops it.","default":0.5,"type":"number","minimum":0,"maximum":1},"posY":{"description":"Vertical placement of the logo: 0 pushes it as far up as it goes, 0.5 centers it, 1 as far down. The logo stays fully inside the image at either extreme, so neither end crops it.","default":0.5,"type":"number","minimum":0,"maximum":1}},"required":["id","color"],"additionalProperties":false},{"type":"object","properties":{"data":{"type":"string","minLength":1,"maxLength":1000000,"description":"Your own logo as a base64-encoded image — PNG, JPEG or SVG — either a bare base64 string or a `data:` URL, which is what the QR Designer’s “Copy JSON” emits. Any mime type declared in that URL is ignored: the real format is detected from the bytes. SVG is sanitized before it is drawn. Nothing is ever fetched over the network — an `https:` URL in this field is not downloaded, it just fails to decode."},"regionWidth":{"description":"Logo width as a fraction of the QR code’s width, between 0.1 and 1. The height follows from the artwork’s aspect ratio.","default":0.8,"type":"number","minimum":0.1,"maximum":1},"mode":{"type":"string","enum":["clear","float","fill"],"description":"How the modules behave around the logo. `clear` clears a plain rectangle behind it; `float` clears only where the logo is opaque, so the pattern flows around the silhouette — use it for logos with a transparent background; `fill` clears nothing and draws the modules behind the logo.","default":"clear"},"halo":{"description":"Extra cleared margin around the logo silhouette, in modules, which keeps floating modules off the logo’s edge. Float mode only.","default":0.75,"type":"number","minimum":0,"maximum":4},"fillInterior":{"description":"Draw modules inside the logo’s enclosed gaps — the counter of an “o”, for instance — instead of leaving them clear. Float mode only.","default":false,"type":"boolean"},"posX":{"description":"Horizontal placement of the logo: 0 pushes it as far left as it goes, 0.5 centers it, 1 as far right. The logo stays fully inside the image at either extreme, so neither end crops it.","default":0.5,"type":"number","minimum":0,"maximum":1},"posY":{"description":"Vertical placement of the logo: 0 pushes it as far up as it goes, 0.5 centers it, 1 as far down. The logo stays fully inside the image at either extreme, so neither end crops it.","default":0.5,"type":"number","minimum":0,"maximum":1}},"required":["data"],"additionalProperties":false}]},"halo":{"description":"Decoration scattered around the QR code, drawn with the design’s own module style.","type":"object","properties":{"enabled":{"description":"Draw the halo. Sending a `halo` object at all switches it on, so this need only be set to turn one off: `false` keeps the settings below while suppressing the decoration. Omit the whole `halo` object for a design that has none.","type":"boolean"},"mode":{"description":"How the halo and the QR code are drawn together. `composite` (layered) leaves the code exactly as it looks without a halo. `unified` (integrated) merges both into one drawing, so 3D styles cast shadows across the halo — at the cost of the code itself looking slightly different.","default":"composite","type":"string","enum":["composite","unified"]},"quietZone":{"description":"The total light ring the viewer sees between the pattern and the halo, in modules. The code’s own `margin` counts toward it rather than adding to it, so a value at or below `margin` leaves no gap at all — with the default margin of 2, the halo starts right at the code until this reaches 3.","default":1,"type":"number","minimum":0,"maximum":20},"spread":{"description":"How far the halo reaches beyond the quiet zone, in modules. One module is the minimum, so 0 renders as 1.","default":8,"type":"number","minimum":0,"maximum":40},"margin":{"description":"Plain background kept outside the halo, in modules.","default":0,"type":"number","minimum":0,"maximum":20},"density":{"description":"Peak fraction of cells filled, measured nearest the code.","default":0.65,"type":"number","minimum":0,"maximum":1},"curve":{"description":"How quickly the halo thins out with distance from the code.","default":"ease","type":"string","enum":["none","linear","ease","steep"]},"falloff":{"description":"Shape the halo fades along — square, soft square, or round.","default":"square","type":"string","enum":["square","squircle","round"]},"cluster":{"description":"How much the halo modules clump together: 0 scatters them at random, 1 gathers them into smooth clumps.","default":0.3,"type":"number","minimum":0,"maximum":1},"seed":{"description":"Scatter seed. The same seed redraws the same halo, so a render is reproducible; change it for a different arrangement of the same settings.","default":0,"type":"integer","minimum":0,"maximum":1000000},"color":{"description":"Where the halo takes its colors from.","type":"object","properties":{"mode":{"description":"Where the halo takes its colors from: `inherit` reuses the code’s own pattern colors, `palette` uses the `palette` below.","default":"inherit","type":"string","enum":["inherit","palette"]},"palette":{"description":"Colors the halo is drawn from when `mode` is `palette`.","maxItems":32,"type":"array","items":{"type":"string","pattern":"^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$"}},"paletteMode":{"description":"How the halo’s palette colors are distributed. Defaults to the code’s own `paletteMode`, or `scatter` when that is unset too.","type":"string","enum":["scatter","horizontal","vertical","diagonal","radial","checkerboard","spiral","wave","noise","rings","mandala","lava","group"]},"fade":{"description":"How far the outermost halo modules fade toward the background. 0 is no fade.","default":0,"type":"number","minimum":0,"maximum":1}},"additionalProperties":false}},"additionalProperties":false}},"additionalProperties":false},"format":{"default":"svg","description":"Output format. This alone selects the response media type — the `Accept` header is not consulted.","type":"string","enum":["svg","png"]},"size":{"description":"Image width and height in pixels. Defaults to 300 for SVG and 1,024 for PNG.","type":"integer","minimum":64,"maximum":4096},"fixContrast":{"default":true,"description":"Nudge low-contrast color combinations apart so the QR code stays scannable. Turn it off to get the colors exactly as given.","type":"boolean"}},"required":["content"],"additionalProperties":false,"example":{"content":"https://qrocodile.io","design":{"moduleStyleId":"woodwork","background":"#f4ead8","margin":2,"moduleStyleParams":{"construction":"crossing","length":8,"logEnds":0.6,"grain":0.6,"depth":0.6,"variation":1,"tilt":0.45,"knotholes":0.3,"knots":1,"inkWeight":0.2,"inkMode":"color"},"finderStyleId":"classic","finderUseModuleStyle":true,"moduleColor":"#8a5a2b","modulePalette":["#8a5a2b","#a06a34","#6f4420"],"paletteMode":"scatter","paletteScale":1,"paletteDither":0,"finderColorInherit":true},"format":"svg"}}}}},"security":[{"apiKey":[]}],"responses":{"200":{"description":"The rendered QR code image — SVG or PNG per the requested `format`. The response `Content-Type` names the format actually returned and is authoritative.","content":{"image/svg+xml":{"schema":{"type":"string","format":"binary","description":"SVG markup, UTF-8. Returned for `format=svg`, the default. Square, sized by `size`, and self-contained — a logo travels inside it as data, never as a link."}},"image/png":{"schema":{"type":"string","format":"binary","description":"PNG bytes, rasterized from the same SVG at `size` pixels square. Returned for `format=png`."}}},"headers":{"x-ratelimit-limit":{"schema":{"type":"integer"},"description":"Requests allowed in the current window."},"x-ratelimit-remaining":{"schema":{"type":"integer"},"description":"Requests still allowed in the current window."},"x-ratelimit-reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets and `x-ratelimit-remaining` returns to `x-ratelimit-limit`."}}},"400":{"description":"Content or design failed validation. Unlike the GET query, this body is strict: an unrecognized key is an error rather than ignored, `animation` included.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["VALIDATION_ERROR","UNAUTHORIZED","NOT_FOUND","PAYLOAD_TOO_LARGE","UNSUPPORTED_MEDIA_TYPE","UNPROCESSABLE","RATE_LIMITED","INTERNAL_ERROR"],"description":"Machine-readable failure code. Stable across releases, so this is the field to branch on. `message` is not stable — it is written for a human reading a log and may be reworded at any time."},"message":{"type":"string","description":"What went wrong, in English, for a human to read. Do not match on it — see `code`."}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"description":"Content or design failed validation. Unlike the GET query, this body is strict: an unrecognized key is an error rather than ignored, `animation` included.","example":{"error":{"code":"VALIDATION_ERROR","message":"body/design Unrecognized key: \"animation\""}}}}},"headers":{"x-ratelimit-limit":{"schema":{"type":"integer"},"description":"Requests allowed in the current window."},"x-ratelimit-remaining":{"schema":{"type":"integer"},"description":"Requests still allowed in the current window."},"x-ratelimit-reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets and `x-ratelimit-remaining` returns to `x-ratelimit-limit`."}}},"401":{"description":"API key missing, malformed, unknown, or revoked.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["VALIDATION_ERROR","UNAUTHORIZED","NOT_FOUND","PAYLOAD_TOO_LARGE","UNSUPPORTED_MEDIA_TYPE","UNPROCESSABLE","RATE_LIMITED","INTERNAL_ERROR"],"description":"Machine-readable failure code. Stable across releases, so this is the field to branch on. `message` is not stable — it is written for a human reading a log and may be reworded at any time."},"message":{"type":"string","description":"What went wrong, in English, for a human to read. Do not match on it — see `code`."}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"description":"API key missing, malformed, unknown, or revoked.","example":{"error":{"code":"UNAUTHORIZED","message":"Missing API key. Send it as: Authorization: Bearer qk_live_…."}}}}},"headers":{"x-ratelimit-limit":{"schema":{"type":"integer"},"description":"Requests allowed in the current window."},"x-ratelimit-remaining":{"schema":{"type":"integer"},"description":"Requests still allowed in the current window."},"x-ratelimit-reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets and `x-ratelimit-remaining` returns to `x-ratelimit-limit`."}}},"413":{"description":"A custom logo is over its cap — 100 KB decoded, 64 KB for SVG, 1,024 px per side for a raster image — or the whole request body is over 1 MB.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["VALIDATION_ERROR","UNAUTHORIZED","NOT_FOUND","PAYLOAD_TOO_LARGE","UNSUPPORTED_MEDIA_TYPE","UNPROCESSABLE","RATE_LIMITED","INTERNAL_ERROR"],"description":"Machine-readable failure code. Stable across releases, so this is the field to branch on. `message` is not stable — it is written for a human reading a log and may be reworded at any time."},"message":{"type":"string","description":"What went wrong, in English, for a human to read. Do not match on it — see `code`."}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"description":"A custom logo is over its cap — 100 KB decoded, 64 KB for SVG, 1,024 px per side for a raster image — or the whole request body is over 1 MB.","example":{"error":{"code":"PAYLOAD_TOO_LARGE","message":"Logo is 262144 bytes; max is 102400."}}}}},"headers":{"x-ratelimit-limit":{"schema":{"type":"integer"},"description":"Requests allowed in the current window."},"x-ratelimit-remaining":{"schema":{"type":"integer"},"description":"Requests still allowed in the current window."},"x-ratelimit-reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets and `x-ratelimit-remaining` returns to `x-ratelimit-limit`."}}},"415":{"description":"The request body arrived with a `Content-Type` the API has no parser for. Send `application/json`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["VALIDATION_ERROR","UNAUTHORIZED","NOT_FOUND","PAYLOAD_TOO_LARGE","UNSUPPORTED_MEDIA_TYPE","UNPROCESSABLE","RATE_LIMITED","INTERNAL_ERROR"],"description":"Machine-readable failure code. Stable across releases, so this is the field to branch on. `message` is not stable — it is written for a human reading a log and may be reworded at any time."},"message":{"type":"string","description":"What went wrong, in English, for a human to read. Do not match on it — see `code`."}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"description":"The request body arrived with a `Content-Type` the API has no parser for. Send `application/json`.","example":{"error":{"code":"UNSUPPORTED_MEDIA_TYPE","message":"Unsupported Media Type"}}}}},"headers":{"x-ratelimit-limit":{"schema":{"type":"integer"},"description":"Requests allowed in the current window."},"x-ratelimit-remaining":{"schema":{"type":"integer"},"description":"Requests still allowed in the current window."},"x-ratelimit-reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets and `x-ratelimit-remaining` returns to `x-ratelimit-limit`."}}},"422":{"description":"Valid input that cannot be encoded — in practice, content too long for a QR code. A QR code holds roughly 2,300 bytes at most, well under the 4,096 characters `content` accepts.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["VALIDATION_ERROR","UNAUTHORIZED","NOT_FOUND","PAYLOAD_TOO_LARGE","UNSUPPORTED_MEDIA_TYPE","UNPROCESSABLE","RATE_LIMITED","INTERNAL_ERROR"],"description":"Machine-readable failure code. Stable across releases, so this is the field to branch on. `message` is not stable — it is written for a human reading a log and may be reworded at any time."},"message":{"type":"string","description":"What went wrong, in English, for a human to read. Do not match on it — see `code`."}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"description":"Valid input that cannot be encoded — in practice, content too long for a QR code. A QR code holds roughly 2,300 bytes at most, well under the 4,096 characters `content` accepts.","example":{"error":{"code":"UNPROCESSABLE","message":"The content is too long to encode in a QR code (3200 characters)."}}}}},"headers":{"x-ratelimit-limit":{"schema":{"type":"integer"},"description":"Requests allowed in the current window."},"x-ratelimit-remaining":{"schema":{"type":"integer"},"description":"Requests still allowed in the current window."},"x-ratelimit-reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets and `x-ratelimit-remaining` returns to `x-ratelimit-limit`."}}},"429":{"description":"Rate limit exceeded — renders are capped at 60 per minute per API key. See the response headers below for how long to wait. The `x-ratelimit-*` three come back on every response, successful ones included, so a client can pace itself without ever provoking this.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["VALIDATION_ERROR","UNAUTHORIZED","NOT_FOUND","PAYLOAD_TOO_LARGE","UNSUPPORTED_MEDIA_TYPE","UNPROCESSABLE","RATE_LIMITED","INTERNAL_ERROR"],"description":"Machine-readable failure code. Stable across releases, so this is the field to branch on. `message` is not stable — it is written for a human reading a log and may be reworded at any time."},"message":{"type":"string","description":"What went wrong, in English, for a human to read. Do not match on it — see `code`."}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"description":"Rate limit exceeded — renders are capped at 60 per minute per API key. See the response headers below for how long to wait. The `x-ratelimit-*` three come back on every response, successful ones included, so a client can pace itself without ever provoking this.","example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Please try again in 1 minute."}}}}},"headers":{"x-ratelimit-limit":{"schema":{"type":"integer"},"description":"Requests allowed in the current window."},"x-ratelimit-remaining":{"schema":{"type":"integer"},"description":"Requests still allowed in the current window."},"x-ratelimit-reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets and `x-ratelimit-remaining` returns to `x-ratelimit-limit`."},"retry-after":{"schema":{"type":"integer"},"description":"Seconds to wait before retrying. Sent only with a 429."}}},"500":{"description":"Render failure. Logged on our side, deliberately without the content or design that caused it — so if you hit one, tell us what you sent.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["VALIDATION_ERROR","UNAUTHORIZED","NOT_FOUND","PAYLOAD_TOO_LARGE","UNSUPPORTED_MEDIA_TYPE","UNPROCESSABLE","RATE_LIMITED","INTERNAL_ERROR"],"description":"Machine-readable failure code. Stable across releases, so this is the field to branch on. `message` is not stable — it is written for a human reading a log and may be reworded at any time."},"message":{"type":"string","description":"What went wrong, in English, for a human to read. Do not match on it — see `code`."}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"description":"Render failure. Logged on our side, deliberately without the content or design that caused it — so if you hit one, tell us what you sent.","example":{"error":{"code":"INTERNAL_ERROR","message":"An unexpected error occurred."}}}}},"headers":{"x-ratelimit-limit":{"schema":{"type":"integer"},"description":"Requests allowed in the current window."},"x-ratelimit-remaining":{"schema":{"type":"integer"},"description":"Requests still allowed in the current window."},"x-ratelimit-reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets and `x-ratelimit-remaining` returns to `x-ratelimit-limit`."}}}}}}},"servers":[{"url":"https://api.qrocodile.io","description":"This deployment"}],"tags":[{"name":"keys","description":"Free self-serve API-key signup: register an email, confirm, receive the API key."},{"name":"qr","description":"Render a QR code from a content string + a design config. GET when the whole request fits in a URL, POST for a full design. Requires an API key."},{"name":"health","description":"Liveness probe for load balancers and uptime monitoring."}],"externalDocs":{"url":"https://qrocodile.io/en/qr-code-api/","description":"Guides, the design ID tables, and copy-paste examples in cURL, JavaScript and Python"}}