{"openapi":"3.0.3","info":{"title":"QRocodile QR Code API","version":"1.0.0","description":"Generate styled QR codes — the same designs producible in the QR Designer — for any content, without a browser.\n\n**Simple:** `GET /v1/qr?content=...&preset=...` returns the image directly.\n**Full control:** `POST /v1/qr` with a `{ content, design }` body for palettes, gradients, logos, and custom style parameters.\n\nEvery `/v1/qr` request needs an API key: `Authorization: Bearer qk_live_…`. Get one free via `POST /v1/keys` (email → confirm → key). Output is SVG or PNG. Errors use `{ error: { code, message } }`."},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Your API key from POST /v1/keys → email confirmation."}},"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":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"},"timestamp":{"type":"string"}},"required":["status","timestamp"],"additionalProperties":false,"example":{"status":"ok","timestamp":"2026-07-12T10:30:00.000Z"}}}}}}}},"/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; no API key is issued until the code is confirmed via POST /v1/keys/confirm. Rate-limited per IP.","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,}$"},"lang":{"default":"en","type":"string","enum":["en","de"]}},"required":["email"]}}}},"responses":{"202":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"],"additionalProperties":false}}}},"400":{"description":"Invalid email address, malformed code, or neither identifier supplied.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"example":{"error":{"code":"VALIDATION_ERROR","message":"body/size Too big"}},"description":"Invalid email address, malformed code, or neither identifier supplied."}}}},"401":{"description":"The code is wrong, expired, or the pending signup was burned by too many attempts.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"example":{"error":{"code":"VALIDATION_ERROR","message":"body/size Too big"}},"description":"The code is wrong, expired, or the pending signup was burned by too many attempts."}}}},"429":{"description":"Rate limit exceeded. Carries `retry-after` (seconds to wait) plus `x-ratelimit-limit`, `x-ratelimit-remaining` and `x-ratelimit-reset` (seconds until the window resets). Successful responses carry the three `x-ratelimit-*` headers too, so a client can pace itself without ever provoking a 429.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"example":{"error":{"code":"VALIDATION_ERROR","message":"body/size Too big"}},"description":"Rate limit exceeded. Carries `retry-after` (seconds to wait) plus `x-ratelimit-limit`, `x-ratelimit-remaining` and `x-ratelimit-reset` (seconds until the window resets). Successful responses carry the three `x-ratelimit-*` headers too, so a client can pace itself without ever provoking a 429."}}}}}}},"/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.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","pattern":"^\\d{6}$"},"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,}$"},"rid":{"type":"string","pattern":"^[0-9a-f]{32}$"}},"required":["code"]}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"apiKey":{"type":"string"},"replaced":{"type":"boolean"}},"required":["apiKey","replaced"],"additionalProperties":false}}}},"400":{"description":"Invalid email address, malformed code, or neither identifier supplied.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"example":{"error":{"code":"VALIDATION_ERROR","message":"body/size Too big"}},"description":"Invalid email address, malformed code, or neither identifier supplied."}}}},"401":{"description":"The code is wrong, expired, or the pending signup was burned by too many attempts.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"example":{"error":{"code":"VALIDATION_ERROR","message":"body/size Too big"}},"description":"The code is wrong, expired, or the pending signup was burned by too many attempts."}}}},"429":{"description":"Rate limit exceeded. Carries `retry-after` (seconds to wait) plus `x-ratelimit-limit`, `x-ratelimit-remaining` and `x-ratelimit-reset` (seconds until the window resets). Successful responses carry the three `x-ratelimit-*` headers too, so a client can pace itself without ever provoking a 429.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"example":{"error":{"code":"VALIDATION_ERROR","message":"body/size Too big"}},"description":"Rate limit exceeded. Carries `retry-after` (seconds to wait) plus `x-ratelimit-limit`, `x-ratelimit-remaining` and `x-ratelimit-reset` (seconds until the window resets). Successful responses carry the three `x-ratelimit-*` headers too, so a client can pace itself without ever provoking a 429."}}}}}}},"/v1/qr":{"get":{"operationId":"getQr","summary":"Render a QR code (simple)","tags":["qr"],"description":"Render a QR code from a content string and an optional preset. The content is encoded exactly as given. Returns the image directly. For full designs (palettes, gradients, logos), use POST /v1/qr. Requires an API key: Authorization: Bearer qk_live_….","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":4096},"in":"query","name":"content","required":true},{"schema":{"default":"svg","type":"string","enum":["svg","png"]},"in":"query","name":"format","required":false},{"schema":{"type":"string","enum":["classic","modern","elegant","midnight","ocean","sunset","forest","brushstroke","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"]},"in":"query","name":"preset","required":false},{"schema":{"type":"integer","minimum":64,"maximum":4096},"in":"query","name":"size","required":false},{"schema":{"type":"integer","minimum":0,"maximum":20},"in":"query","name":"margin","required":false},{"schema":{"type":"string","pattern":"^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$"},"in":"query","name":"dark","required":false},{"schema":{"type":"string","pattern":"^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$"},"in":"query","name":"bg","required":false}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The rendered QR code image (SVG or PNG per `format`).","content":{"image/svg+xml":{"schema":{"type":"string"}},"image/png":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Content or design failed validation, or the format is unsupported.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"example":{"error":{"code":"VALIDATION_ERROR","message":"body/size Too big"}},"description":"Content or design failed validation, or the format is unsupported."}}}},"401":{"description":"API key missing, malformed, unknown, or revoked.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"example":{"error":{"code":"VALIDATION_ERROR","message":"body/size Too big"}},"description":"API key missing, malformed, unknown, or revoked."}}}},"413":{"description":"Custom logo or requested PNG size over the cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"example":{"error":{"code":"VALIDATION_ERROR","message":"body/size Too big"}},"description":"Custom logo or requested PNG size over the cap."}}}},"422":{"description":"Valid input that cannot be encoded — usually content too long for a QR code.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"example":{"error":{"code":"VALIDATION_ERROR","message":"body/size Too big"}},"description":"Valid input that cannot be encoded — usually content too long for a QR code."}}}},"429":{"description":"Rate limit exceeded. Carries `retry-after` (seconds to wait) plus `x-ratelimit-limit`, `x-ratelimit-remaining` and `x-ratelimit-reset` (seconds until the window resets). Successful responses carry the three `x-ratelimit-*` headers too, so a client can pace itself without ever provoking a 429.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"example":{"error":{"code":"VALIDATION_ERROR","message":"body/size Too big"}},"description":"Rate limit exceeded. Carries `retry-after` (seconds to wait) plus `x-ratelimit-limit`, `x-ratelimit-remaining` and `x-ratelimit-reset` (seconds until the window resets). Successful responses carry the three `x-ratelimit-*` headers too, so a client can pace itself without ever provoking a 429."}}}},"500":{"description":"Render failure. Logged on our side.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"example":{"error":{"code":"VALIDATION_ERROR","message":"body/size Too big"}},"description":"Render failure. Logged on our side."}}}}}},"post":{"operationId":"createQr","summary":"Render a QR code (full design)","tags":["qr"],"description":"Render a QR code from a content string and a full QrDesignConfig. Returns the image bytes (SVG or PNG per `format`). Requires an API key: Authorization: Bearer qk_live_…. Despite using POST (to carry the design body), this is a safe, idempotent operation with no side effects — responses may be freely retried and cached.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"string","minLength":1,"maxLength":4096},"design":{"default":{},"type":"object","properties":{"preset":{"type":"string","enum":["classic","modern","elegant","midnight","ocean","sunset","forest","brushstroke","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"]},"moduleStyleId":{"type":"string","enum":["classic","photo-overlay","bacteria","blocky","botanical","bubbles","calligraphy","vortex","chevron","circuits","connected","diamond","doodle","double-bubble","drip","explosion","film","ink-pen","washi-tape","comic-fries","woodwork","blotchy","glitch","grass","halfmoon","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}]}},"finderStyleId":{"type":"string","enum":["classic","modern","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}]}},"finderUseModuleStyle":{"type":"boolean"},"finderColorInherit":{"type":"boolean"},"moduleColor":{"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"]},"angle":{"type":"number"},"stops":{"minItems":2,"maxItems":16,"type":"array","items":{"anyOf":[{"type":"string"},{"type":"object","properties":{"color":{"type":"string"},"offset":{"type":"number","minimum":0,"maximum":1}},"required":["color","offset"]}]}}},"required":["type","stops"]}]},"modulePalette":{"maxItems":32,"type":"array","items":{"type":"string","pattern":"^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$"}},"paletteMode":{"type":"string","enum":["scatter","horizontal","vertical","diagonal","radial","checkerboard","spiral","wave","noise","rings","mandala","lava","group"]},"paletteScale":{"type":"number","minimum":0,"exclusiveMinimum":true,"maximum":100},"paletteDither":{"type":"number","minimum":0,"maximum":10},"finderFrameColor":{"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"]},"angle":{"type":"number"},"stops":{"minItems":2,"maxItems":16,"type":"array","items":{"anyOf":[{"type":"string"},{"type":"object","properties":{"color":{"type":"string"},"offset":{"type":"number","minimum":0,"maximum":1}},"required":["color","offset"]}]}}},"required":["type","stops"]}]},"finderEyeColor":{"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"]},"angle":{"type":"number"},"stops":{"minItems":2,"maxItems":16,"type":"array","items":{"anyOf":[{"type":"string"},{"type":"object","properties":{"color":{"type":"string"},"offset":{"type":"number","minimum":0,"maximum":1}},"required":["color","offset"]}]}}},"required":["type","stops"]}]},"background":{"anyOf":[{"type":"string","pattern":"^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$"},{"type":"string","enum":["transparent"]}]},"margin":{"type":"integer","minimum":0,"maximum":20},"logo":{"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","github","kim"]},"color":{"type":"string","pattern":"^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$"},"regionWidth":{"type":"number","minimum":0.1,"maximum":1},"mode":{"type":"string","enum":["clear","float","fill"]},"halo":{"type":"number","minimum":0,"maximum":4},"fillInterior":{"type":"boolean"},"posX":{"type":"number","minimum":0,"maximum":1},"posY":{"type":"number","minimum":0,"maximum":1}},"required":["id","color"],"additionalProperties":false},{"type":"object","properties":{"data":{"type":"string","minLength":1,"maxLength":1000000},"regionWidth":{"type":"number","minimum":0.1,"maximum":1},"mode":{"type":"string","enum":["clear","float","fill"]},"halo":{"type":"number","minimum":0,"maximum":4},"fillInterior":{"type":"boolean"},"posX":{"type":"number","minimum":0,"maximum":1},"posY":{"type":"number","minimum":0,"maximum":1}},"required":["data"],"additionalProperties":false}]},"halo":{"type":"object","properties":{"enabled":{"type":"boolean"},"mode":{"type":"string","enum":["composite","unified"]},"quietZone":{"type":"number","minimum":0,"maximum":20},"spread":{"type":"number","minimum":0,"maximum":40},"margin":{"type":"number","minimum":0,"maximum":20},"density":{"type":"number","minimum":0,"maximum":1},"curve":{"type":"string","enum":["none","linear","ease","steep"]},"falloff":{"type":"string","enum":["square","squircle","round"]},"cluster":{"type":"number","minimum":0,"maximum":1},"seed":{"type":"integer","minimum":0,"maximum":1000000},"color":{"type":"object","properties":{"mode":{"type":"string","enum":["inherit","palette"]},"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":{"type":"string","enum":["scatter","horizontal","vertical","diagonal","radial","checkerboard","spiral","wave","noise","rings","mandala","lava","group"]},"fade":{"type":"number","minimum":0,"maximum":1}},"additionalProperties":false}},"additionalProperties":false}},"additionalProperties":false},"format":{"default":"svg","type":"string","enum":["svg","png"]},"size":{"type":"integer","minimum":64,"maximum":4096},"fixContrast":{"default":true,"type":"boolean"}},"required":["content"],"additionalProperties":false,"example":{"content":"https://qrocodile.io","design":{"preset":"ocean","moduleColor":"#0d9488","moduleStyleId":"roundedSquares"},"format":"svg"}}}}},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The rendered QR code image (SVG or PNG per `format`).","content":{"image/svg+xml":{"schema":{"type":"string"}},"image/png":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Content or design failed validation, or the format is unsupported.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"example":{"error":{"code":"VALIDATION_ERROR","message":"body/size Too big"}},"description":"Content or design failed validation, or the format is unsupported."}}}},"401":{"description":"API key missing, malformed, unknown, or revoked.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"example":{"error":{"code":"VALIDATION_ERROR","message":"body/size Too big"}},"description":"API key missing, malformed, unknown, or revoked."}}}},"413":{"description":"Custom logo or requested PNG size over the cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"example":{"error":{"code":"VALIDATION_ERROR","message":"body/size Too big"}},"description":"Custom logo or requested PNG size over the cap."}}}},"422":{"description":"Valid input that cannot be encoded — usually content too long for a QR code.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"example":{"error":{"code":"VALIDATION_ERROR","message":"body/size Too big"}},"description":"Valid input that cannot be encoded — usually content too long for a QR code."}}}},"429":{"description":"Rate limit exceeded. Carries `retry-after` (seconds to wait) plus `x-ratelimit-limit`, `x-ratelimit-remaining` and `x-ratelimit-reset` (seconds until the window resets). Successful responses carry the three `x-ratelimit-*` headers too, so a client can pace itself without ever provoking a 429.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"example":{"error":{"code":"VALIDATION_ERROR","message":"body/size Too big"}},"description":"Rate limit exceeded. Carries `retry-after` (seconds to wait) plus `x-ratelimit-limit`, `x-ratelimit-remaining` and `x-ratelimit-reset` (seconds until the window resets). Successful responses carry the three `x-ratelimit-*` headers too, so a client can pace itself without ever provoking a 429."}}}},"500":{"description":"Render failure. Logged on our side.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"example":{"error":{"code":"VALIDATION_ERROR","message":"body/size Too big"}},"description":"Render failure. Logged on our side."}}}}}}}},"servers":[{"url":"https://api.qrocodile.io"}],"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."}]}