{"openapi":"3.1.0","info":{"title":"QRX API","version":"1.0.0","summary":"Make artistic, scannable QR codes from other systems.","description":"Codes are made asynchronously: creating one answers at once with its id and the status `processing`, and the code usually succeeds in under a minute. Poll it, or pass `?wait=45` to hold the request open until it finishes. Every code counts against the account’s plan exactly as codes made in the app do.","contact":{"name":"QRX","email":"hello@qrx.codes","url":"https://qrx.codes/developers"}},"servers":[{"url":"https://qrx.codes"}],"security":[{"bearerAuth":[]},{"apiKeyHeader":[]}],"tags":[{"name":"Codes","description":"QR codes painted into pictures."},{"name":"Account","description":"The plan, allowance and limits of the account the credentials belong to."},{"name":"Styles","description":"Styles a code can be painted in."}],"paths":{"/v1/codes":{"post":{"operationId":"createCode","tags":["Codes"],"summary":"Create a code","description":"Starts a code from a prompt and a destination link (or Wi-Fi details, on Starter). Answers `202` at once with the code processing; with `?wait=`, answers when it finishes (`201`) or when the wait runs out (`202`). The `id` and `shortUrl` in the answer are the finished code’s and never change. Send an `Idempotency-Key` so a retry after a network failure cannot make a second code.","parameters":[{"$ref":"#/components/parameters/Wait"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCodeRequest"},"examples":{"link":{"summary":"A link code","value":{"prompt":"A lighthouse on a cliff at sunset","destination":"https://example.com/menu"}},"styled":{"summary":"A link code in a style","value":{"prompt":"A lighthouse on a cliff at sunset","destination":"https://example.com/menu","style":"a64c5"}},"wifi":{"summary":"A Wi-Fi code (Starter)","value":{"prompt":"A cosy café interior","wifi":{"ssid":"Cafe Guest","password":"espresso42","security":"wpa"}}}}}}},"responses":{"201":{"description":"The code finished within the wait.","headers":{"Request-Id":{"$ref":"#/components/headers/Request-Id"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"Location":{"$ref":"#/components/headers/Location"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Code"},"example":{"id":"k7m2qz","object":"code","status":"succeeded","type":"url","prompt":"A lighthouse on a cliff at sunset","style":null,"destination":"https://example.com/menu","wifi":null,"shortUrl":"https://qrx.to/k7m2qz","imageUrl":"https://qrx.codes/media/premium/k7m2qz.png","pageUrl":"https://qrx.codes/view/k7m2qz","error":null,"createdAt":"2026-10-08T01:23:45.000Z"}}}},"202":{"description":"The code has started and is processing.","headers":{"Request-Id":{"$ref":"#/components/headers/Request-Id"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"Location":{"$ref":"#/components/headers/Location"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Code"},"example":{"id":"k7m2qz","object":"code","status":"processing","type":"url","prompt":"A lighthouse on a cliff at sunset","style":null,"destination":"https://example.com/menu","wifi":null,"shortUrl":"https://qrx.to/k7m2qz","imageUrl":null,"pageUrl":"https://qrx.codes/view/k7m2qz","error":null,"createdAt":"2026-10-08T01:23:45.000Z"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Wi-Fi codes need the Starter plan, or the account is gone.","headers":{"Request-Id":{"$ref":"#/components/headers/Request-Id"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://qrx.codes/developers/docs/errors#plan-required","title":"Needs the Starter plan","status":403,"detail":"Wi-Fi codes need the Starter plan.","requestId":"req_8mGvXbQ2nT5wK1pL9sD3"}}},"x-problems":["plan-required","forbidden"]},"409":{"description":"A request with this Idempotency-Key is still running.","headers":{"Request-Id":{"$ref":"#/components/headers/Request-Id"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://qrx.codes/developers/docs/errors#idempotency-in-progress","title":"A request with this idempotency key is in progress","status":409,"detail":"A request with this Idempotency-Key is still running. Retry shortly.","requestId":"req_8mGvXbQ2nT5wK1pL9sD3"}}},"x-problems":["idempotency-in-progress"]},"413":{"description":"The body is larger than 16 KB.","headers":{"Request-Id":{"$ref":"#/components/headers/Request-Id"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://qrx.codes/developers/docs/errors#payload-too-large","title":"Request body too large","status":413,"detail":"The body is larger than 16 KB.","requestId":"req_8mGvXbQ2nT5wK1pL9sD3"}}},"x-problems":["payload-too-large"]},"422":{"description":"The code could not be started, or the Idempotency-Key was used for a different request.","headers":{"Request-Id":{"$ref":"#/components/headers/Request-Id"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://qrx.codes/developers/docs/errors#unprocessable","title":"Request could not be completed","status":422,"detail":"Only link codes can change where they point.","requestId":"req_8mGvXbQ2nT5wK1pL9sD3"}}},"x-problems":["unprocessable","idempotency-mismatch"]},"429":{"$ref":"#/components/responses/CodeLimitReached"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-codeSamples":[{"lang":"bash","label":"curl","source":"curl -X POST https://qrx.codes/v1/codes \\\n  -H \"Authorization: Bearer $QRX_API_KEY\" \\\n  -H \"Idempotency-Key: menu-2026-10-08\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"prompt\": \"A lighthouse on a cliff at sunset\",\n    \"destination\": \"https://example.com/menu\"\n  }'"},{"lang":"javascript","label":"JavaScript","source":"const QRX_API_KEY = process.env.QRX_API_KEY;\n\nconst response = await fetch('https://qrx.codes/v1/codes', {\n  method: 'POST',\n  headers: {\n    Authorization: `Bearer ${QRX_API_KEY}`,\n    'Content-Type': 'application/json',\n    'Idempotency-Key': 'menu-2026-10-08',\n  },\n  body: JSON.stringify({\n    prompt: 'A lighthouse on a cliff at sunset',\n    destination: 'https://example.com/menu',\n  }),\n});\nconst data = await response.json();"},{"lang":"python","label":"Python","source":"import os\nimport requests\n\nQRX_API_KEY = os.environ[\"QRX_API_KEY\"]\n\nresponse = requests.post(\n    \"https://qrx.codes/v1/codes\",\n    headers={\"Authorization\": f\"Bearer {QRX_API_KEY}\", \"Idempotency-Key\": \"menu-2026-10-08\"},\n    json={\n        \"prompt\": \"A lighthouse on a cliff at sunset\",\n        \"destination\": \"https://example.com/menu\",\n    },\n)\ndata = response.json()"}]},"get":{"operationId":"listCodes","tags":["Codes"],"summary":"List codes","description":"The account’s codes, newest first, a page at a time. Pass `nextCursor` as `cursor` for the next page.","parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"A page of codes.","headers":{"Request-Id":{"$ref":"#/components/headers/Request-Id"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CodeList"},"example":{"object":"list","data":[{"id":"k7m2qz","object":"code","status":"succeeded","type":"url","prompt":"A lighthouse on a cliff at sunset","style":null,"destination":"https://example.com/menu","wifi":null,"shortUrl":"https://qrx.to/k7m2qz","imageUrl":"https://qrx.codes/media/premium/k7m2qz.png","pageUrl":"https://qrx.codes/view/k7m2qz","error":null,"createdAt":"2026-10-08T01:23:45.000Z"}],"hasMore":false,"nextCursor":null}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-codeSamples":[{"lang":"bash","label":"curl","source":"curl \"https://qrx.codes/v1/codes?limit=10\" \\\n  -H \"Authorization: Bearer $QRX_API_KEY\""},{"lang":"javascript","label":"JavaScript","source":"const QRX_API_KEY = process.env.QRX_API_KEY;\n\nconst response = await fetch('https://qrx.codes/v1/codes?limit=10', {\n  headers: {\n    Authorization: `Bearer ${QRX_API_KEY}`,\n  },\n});\nconst data = await response.json();"},{"lang":"python","label":"Python","source":"import os\nimport requests\n\nQRX_API_KEY = os.environ[\"QRX_API_KEY\"]\n\nresponse = requests.get(\n    \"https://qrx.codes/v1/codes\",\n    headers={\"Authorization\": f\"Bearer {QRX_API_KEY}\"},\n    params={\n        \"limit\": 10,\n    },\n)\ndata = response.json()"}]}},"/v1/codes/{id}":{"get":{"operationId":"getCode","tags":["Codes"],"summary":"Get a code","description":"A code’s status and, once it has succeeded, its image. With `?wait=`, answers as soon as the code finishes or when the wait runs out, whichever is first, so one request replaces many polls.","parameters":[{"$ref":"#/components/parameters/CodeId"},{"$ref":"#/components/parameters/Wait"}],"responses":{"200":{"description":"The code.","headers":{"Request-Id":{"$ref":"#/components/headers/Request-Id"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Code"},"example":{"id":"k7m2qz","object":"code","status":"succeeded","type":"url","prompt":"A lighthouse on a cliff at sunset","style":null,"destination":"https://example.com/menu","wifi":null,"shortUrl":"https://qrx.to/k7m2qz","imageUrl":"https://qrx.codes/media/premium/k7m2qz.png","pageUrl":"https://qrx.codes/view/k7m2qz","error":null,"createdAt":"2026-10-08T01:23:45.000Z"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-codeSamples":[{"lang":"bash","label":"curl","source":"curl \"https://qrx.codes/v1/codes/k7m2qz?wait=45\" \\\n  -H \"Authorization: Bearer $QRX_API_KEY\""},{"lang":"javascript","label":"JavaScript","source":"const QRX_API_KEY = process.env.QRX_API_KEY;\n\nconst response = await fetch('https://qrx.codes/v1/codes/k7m2qz?wait=45', {\n  headers: {\n    Authorization: `Bearer ${QRX_API_KEY}`,\n  },\n});\nconst data = await response.json();"},{"lang":"python","label":"Python","source":"import os\nimport requests\n\nQRX_API_KEY = os.environ[\"QRX_API_KEY\"]\n\nresponse = requests.get(\n    \"https://qrx.codes/v1/codes/k7m2qz\",\n    headers={\"Authorization\": f\"Bearer {QRX_API_KEY}\"},\n    params={\n        \"wait\": 45,\n    },\n)\ndata = response.json()"}]},"patch":{"operationId":"updateCode","tags":["Codes"],"summary":"Change where a code points","description":"Points a link code somewhere else; printed codes follow at once. Needs the Starter plan.","parameters":[{"$ref":"#/components/parameters/CodeId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCodeRequest"},"example":{"destination":"https://example.com/new-menu"}}}},"responses":{"200":{"description":"The updated code.","headers":{"Request-Id":{"$ref":"#/components/headers/Request-Id"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Code"},"example":{"id":"k7m2qz","object":"code","status":"succeeded","type":"url","prompt":"A lighthouse on a cliff at sunset","style":null,"destination":"https://example.com/new-menu","wifi":null,"shortUrl":"https://qrx.to/k7m2qz","imageUrl":"https://qrx.codes/media/premium/k7m2qz.png","pageUrl":"https://qrx.codes/view/k7m2qz","error":null,"createdAt":"2026-10-08T01:23:45.000Z"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Changing where a code points needs the Starter plan, or the account is gone.","headers":{"Request-Id":{"$ref":"#/components/headers/Request-Id"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://qrx.codes/developers/docs/errors#plan-required","title":"Needs the Starter plan","status":403,"detail":"Wi-Fi codes need the Starter plan.","requestId":"req_8mGvXbQ2nT5wK1pL9sD3"}}},"x-problems":["plan-required","forbidden"]},"404":{"$ref":"#/components/responses/NotFound"},"422":{"description":"Wi-Fi codes cannot change.","headers":{"Request-Id":{"$ref":"#/components/headers/Request-Id"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://qrx.codes/developers/docs/errors#unprocessable","title":"Request could not be completed","status":422,"detail":"Only link codes can change where they point.","requestId":"req_8mGvXbQ2nT5wK1pL9sD3"}}},"x-problems":["unprocessable"]},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-codeSamples":[{"lang":"bash","label":"curl","source":"curl -X PATCH https://qrx.codes/v1/codes/k7m2qz \\\n  -H \"Authorization: Bearer $QRX_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"destination\": \"https://example.com/new-menu\"\n  }'"},{"lang":"javascript","label":"JavaScript","source":"const QRX_API_KEY = process.env.QRX_API_KEY;\n\nconst response = await fetch('https://qrx.codes/v1/codes/k7m2qz', {\n  method: 'PATCH',\n  headers: {\n    Authorization: `Bearer ${QRX_API_KEY}`,\n    'Content-Type': 'application/json',\n  },\n  body: JSON.stringify({\n    destination: 'https://example.com/new-menu',\n  }),\n});\nconst data = await response.json();"},{"lang":"python","label":"Python","source":"import os\nimport requests\n\nQRX_API_KEY = os.environ[\"QRX_API_KEY\"]\n\nresponse = requests.patch(\n    \"https://qrx.codes/v1/codes/k7m2qz\",\n    headers={\"Authorization\": f\"Bearer {QRX_API_KEY}\"},\n    json={\n        \"destination\": \"https://example.com/new-menu\",\n    },\n)\ndata = response.json()"}]}},"/v1/account":{"get":{"operationId":"getAccount","tags":["Account"],"summary":"Get the account","description":"The plan, the codes left today and when the count resets, the rate limits, and the credentials in use.","responses":{"200":{"description":"The account.","headers":{"Request-Id":{"$ref":"#/components/headers/Request-Id"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Account"},"example":{"object":"account","plan":"free","codes":{"dailyLimit":42,"remainingToday":38,"resetsAt":"2026-10-09T00:00:00.000Z"},"features":{"wifiCodes":false,"changeDestination":false},"rateLimits":{"requests":{"limit":30,"windowSeconds":60},"codes":{"limit":6,"windowSeconds":10}},"credentials":{"type":"api_key","prefix":"qrx_live_abc123def456","environment":"live"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-codeSamples":[{"lang":"bash","label":"curl","source":"curl https://qrx.codes/v1/account \\\n  -H \"Authorization: Bearer $QRX_API_KEY\""},{"lang":"javascript","label":"JavaScript","source":"const QRX_API_KEY = process.env.QRX_API_KEY;\n\nconst response = await fetch('https://qrx.codes/v1/account', {\n  headers: {\n    Authorization: `Bearer ${QRX_API_KEY}`,\n  },\n});\nconst data = await response.json();"},{"lang":"python","label":"Python","source":"import os\nimport requests\n\nQRX_API_KEY = os.environ[\"QRX_API_KEY\"]\n\nresponse = requests.get(\n    \"https://qrx.codes/v1/account\",\n    headers={\"Authorization\": f\"Bearer {QRX_API_KEY}\"},\n)\ndata = response.json()"}]}},"/v1/styles":{"get":{"operationId":"listStyles","tags":["Styles"],"summary":"List styles","description":"Styles a code can be painted in; pass an id as `style` when creating a code.","responses":{"200":{"description":"Every style.","headers":{"Request-Id":{"$ref":"#/components/headers/Request-Id"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StyleList"},"example":{"object":"list","data":[{"id":"a64c5","object":"style","name":"Anime","previewUrl":"https://qrx.codes/images/styles/anime.png"},{"id":"06184","object":"style","name":"Cartoon","previewUrl":"https://qrx.codes/images/styles/cartoon.png"}],"hasMore":false,"nextCursor":null}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-codeSamples":[{"lang":"bash","label":"curl","source":"curl https://qrx.codes/v1/styles \\\n  -H \"Authorization: Bearer $QRX_API_KEY\""},{"lang":"javascript","label":"JavaScript","source":"const QRX_API_KEY = process.env.QRX_API_KEY;\n\nconst response = await fetch('https://qrx.codes/v1/styles', {\n  headers: {\n    Authorization: `Bearer ${QRX_API_KEY}`,\n  },\n});\nconst data = await response.json();"},{"lang":"python","label":"Python","source":"import os\nimport requests\n\nQRX_API_KEY = os.environ[\"QRX_API_KEY\"]\n\nresponse = requests.get(\n    \"https://qrx.codes/v1/styles\",\n    headers={\"Authorization\": f\"Bearer {QRX_API_KEY}\"},\n)\ndata = response.json()"}]}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"An API key from the developer section of the app, e.g. `Authorization: Bearer qrx_live_abc123def456_…`. Preferred."},"apiKeyHeader":{"type":"apiKey","in":"header","name":"X-API-Key","description":"The same API key in an `X-API-Key` header, for clients that can set a header but not an authorisation scheme. Used only when there is no `Authorization` header. API keys only, never OAuth access tokens."}},"parameters":{"CodeId":{"name":"id","in":"path","required":true,"description":"The code’s id, as returned when it was created.","schema":{"type":"string","maxLength":64},"example":"k7m2qz"},"Wait":{"name":"wait","in":"query","description":"Seconds to wait for the code to finish before answering, up to 50; 45 is a good value. 0 answers at once.","schema":{"type":"integer","minimum":0,"maximum":50,"default":0}},"IdempotencyKey":{"name":"Idempotency-Key","in":"header","description":"A key of your choosing, unique per code you mean to create. A retry with the same key and body within a day answers with the code the first request made, as it is now, and never makes a second one. A retry that arrives before the first request has made its code answers `409` with `Retry-After`.","schema":{"type":"string","minLength":1,"maxLength":255}},"Limit":{"name":"limit","in":"query","description":"How many items to return.","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},"Cursor":{"name":"cursor","in":"query","description":"`nextCursor` from the previous page.","schema":{"type":"string"}}},"headers":{"Request-Id":{"description":"This request’s id. Quote it to support.","schema":{"type":"string"}},"RateLimit":{"description":"Where the credentials stand against each policy in `RateLimit-Policy` (IETF draft-ietf-httpapi-ratelimit-headers): `r` is what is left and `t` the seconds it applies for (until more is available), e.g. `\"requests\";r=29;t=2`. A create on a plan with a daily allowance adds a `\"codes\"` item: the codes left today and the seconds until the count resets.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The limits that apply: `q` is the quota and `w` its window in seconds, e.g. `\"requests\";q=30;w=60`. A create on a plan with a daily allowance adds `\"codes\";q=42;w=86400`. More policies may be added.","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait before trying again.","schema":{"type":"integer"}},"Location":{"description":"The code’s absolute URL on the API.","schema":{"type":"string","format":"uri"}},"Idempotent-Replayed":{"description":"`true` when this answers a retry of an earlier request with the same Idempotency-Key: the body is the code that request made, as it is now.","schema":{"type":"string","enum":["true"]}}},"responses":{"Unauthorized":{"description":"The credentials are missing, invalid or revoked.","headers":{"Request-Id":{"$ref":"#/components/headers/Request-Id"},"WWW-Authenticate":{"description":"The authentication scheme to use.","schema":{"type":"string"}}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://qrx.codes/developers/docs/errors#unauthorized","title":"Missing or invalid credentials","status":401,"detail":"That API key has been revoked.","requestId":"req_8mGvXbQ2nT5wK1pL9sD3"}}},"x-problems":["unauthorized"]},"InvalidRequest":{"description":"The request is not valid; `errors` lists each field.","headers":{"Request-Id":{"$ref":"#/components/headers/Request-Id"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://qrx.codes/developers/docs/errors#invalid-request","title":"Invalid request","status":400,"detail":"prompt is required","errors":[{"field":"prompt","message":"is required"}],"requestId":"req_8mGvXbQ2nT5wK1pL9sD3"}}},"x-problems":["invalid-request"]},"NotFound":{"description":"The account has no code with that id.","headers":{"Request-Id":{"$ref":"#/components/headers/Request-Id"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://qrx.codes/developers/docs/errors#not-found","title":"Not found","status":404,"detail":"You have no code k7m2qz.","requestId":"req_8mGvXbQ2nT5wK1pL9sD3"}}},"x-problems":["not-found"]},"Forbidden":{"description":"The account the credentials belong to is gone.","headers":{"Request-Id":{"$ref":"#/components/headers/Request-Id"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://qrx.codes/developers/docs/errors#forbidden","title":"Not allowed","status":403,"detail":"The account these credentials belong to no longer exists.","requestId":"req_8mGvXbQ2nT5wK1pL9sD3"}}},"x-problems":["forbidden"]},"TooManyRequests":{"description":"The key has made too many requests in a short time. `Retry-After` says when to try again.","headers":{"Request-Id":{"$ref":"#/components/headers/Request-Id"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://qrx.codes/developers/docs/errors#rate-limited","title":"Too many requests","status":429,"detail":"Too many requests. Try again in 4 seconds.","requestId":"req_8mGvXbQ2nT5wK1pL9sD3"}}},"x-problems":["rate-limited"]},"CodeLimitReached":{"description":"A rate limit or the Free plan’s daily allowance is used up. `Retry-After` says when to try again. With `daily-limit`, `RateLimit` carries the `\"codes\"` item.","headers":{"Request-Id":{"$ref":"#/components/headers/Request-Id"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://qrx.codes/developers/docs/errors#daily-limit","title":"Daily code limit reached","status":429,"detail":"No codes are left today. The Free plan resets at 2026-10-09T00:00:00.000Z; Starter has no daily limit.","requestId":"req_8mGvXbQ2nT5wK1pL9sD3"}}},"x-problems":["daily-limit","rate-limited"]},"InternalError":{"description":"Something failed on our side.","headers":{"Request-Id":{"$ref":"#/components/headers/Request-Id"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://qrx.codes/developers/docs/errors#internal","title":"Something went wrong","status":500,"detail":"The request failed on our side. Try again shortly.","requestId":"req_8mGvXbQ2nT5wK1pL9sD3"}}},"x-problems":["internal"]},"ServiceUnavailable":{"description":"A service QRX relies on, such as the one whose keys check OAuth access tokens, did not answer. The credentials are still good; retry after `Retry-After` seconds.","headers":{"Request-Id":{"$ref":"#/components/headers/Request-Id"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://qrx.codes/developers/docs/errors#unavailable","title":"Temporarily unavailable","status":503,"detail":"Access tokens cannot be checked right now. Try again in 5 seconds; the token is still good.","requestId":"req_8mGvXbQ2nT5wK1pL9sD3"}}},"x-problems":["unavailable"]}},"schemas":{"CreateCodeRequest":{"description":"A link code (with `destination`) or a Wi-Fi code (with `wifi`).","oneOf":[{"$ref":"#/components/schemas/CreateLinkCodeRequest"},{"$ref":"#/components/schemas/CreateWifiCodeRequest"}]},"CreateLinkCodeRequest":{"type":"object","title":"Link code","description":"A code that opens a web page.","additionalProperties":false,"required":["prompt","destination"],"properties":{"prompt":{"type":"string","minLength":1,"maxLength":1000,"description":"What the picture should show."},"destination":{"type":"string","format":"iri","pattern":"^[Hh][Tt][Tt][Pp][Ss]?://[^\\s\\\\]+$","minLength":1,"maxLength":2048,"description":"Where scans go. An absolute `http://` or `https://` URL, stored as given after trimming."},"style":{"type":"string","description":"A style id from `GET /v1/styles`."}}},"CreateWifiCodeRequest":{"type":"object","title":"Wi-Fi code","description":"A code that joins a Wi-Fi network (Starter).","additionalProperties":false,"required":["prompt","wifi"],"properties":{"prompt":{"type":"string","minLength":1,"maxLength":1000,"description":"What the picture should show."},"wifi":{"$ref":"#/components/schemas/WifiNetwork"},"style":{"type":"string","description":"A style id from `GET /v1/styles`."}}},"WifiNetwork":{"type":"object","description":"The network a Wi-Fi code joins.","additionalProperties":false,"required":["ssid","password"],"properties":{"ssid":{"type":"string","minLength":1,"maxLength":32,"description":"The network’s name."},"password":{"type":"string","minLength":1,"maxLength":63},"security":{"type":"string","enum":["wpa","wep"],"default":"wpa","description":"`wpa` or `wep`. `nopass`, for open networks, is planned."}}},"UpdateCodeRequest":{"type":"object","additionalProperties":false,"required":["destination"],"properties":{"destination":{"type":"string","format":"iri","pattern":"^[Hh][Tt][Tt][Pp][Ss]?://[^\\s\\\\]+$","minLength":1,"maxLength":2048,"description":"Where scans go from now on. An absolute `http://` or `https://` URL, stored as given after trimming."}}},"Code":{"type":"object","required":["id","object","status","type","prompt","style","destination","wifi","shortUrl","imageUrl","pageUrl","error","createdAt"],"properties":{"id":{"type":"string","maxLength":64,"description":"The code’s id. A link code’s id is also the path of its short link: lowercase letters and digits, currently 6 characters. A Wi-Fi code’s id is a UUID. Treat every id as an opaque, case-sensitive string of up to 64 characters. It never changes."},"object":{"const":"code"},"status":{"type":"string","x-extensible-enum":["queued","processing","succeeded","failed"],"examples":["queued","processing","succeeded","failed"],"description":"`processing` until the picture is painted and checked to scan; then `succeeded` or `failed`. `queued` is reserved for a code waiting to start. More values may be added: treat anything other than `succeeded` or `failed` as still in progress."},"type":{"type":"string","x-extensible-enum":["url","wifi"],"examples":["url","wifi"],"description":"`url` for a link code, `wifi` for a Wi-Fi code. More values may be added."},"prompt":{"type":"string"},"style":{"type":["string","null"],"description":"The style id, if one was chosen."},"destination":{"type":["string","null"],"description":"Where a link code’s scans go; null for a Wi-Fi code."},"wifi":{"type":["object","null"],"description":"The network a Wi-Fi code joins; null for a link code. The password is never returned.","required":["ssid","security"],"properties":{"ssid":{"type":"string"},"security":{"type":"string","x-extensible-enum":["wpa","wep"],"examples":["wpa","wep"],"description":"More values (such as `nopass`) may be added."}}},"shortUrl":{"type":["string","null"],"description":"The link the code encodes (`https://qrx.to/<id>`); scans go through it to `destination`. Null for a Wi-Fi code."},"imageUrl":{"type":["string","null"],"description":"The finished image (PNG), once the code has succeeded."},"pageUrl":{"type":"string","description":"The code’s page in the app."},"error":{"type":["object","null"],"required":["message"],"properties":{"message":{"type":"string"}},"description":"Why the code failed, when it did."},"createdAt":{"type":"string","format":"date-time"}}},"CodeList":{"type":"object","required":["object","data","hasMore","nextCursor"],"properties":{"object":{"const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Code"}},"hasMore":{"type":"boolean"},"nextCursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page; null on the last page."}}},"Style":{"type":"object","required":["id","object","name","previewUrl"],"properties":{"id":{"type":"string"},"object":{"const":"style"},"name":{"type":"string"},"previewUrl":{"type":["string","null"],"description":"A picture in the style."}}},"StyleList":{"type":"object","required":["object","data","hasMore","nextCursor"],"properties":{"object":{"const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Style"}},"hasMore":{"type":"boolean"},"nextCursor":{"type":"null"}}},"RateLimit":{"type":"object","required":["limit","windowSeconds"],"properties":{"limit":{"type":"integer"},"windowSeconds":{"type":"integer"}}},"Account":{"type":"object","required":["object","plan","codes","features","rateLimits","credentials"],"properties":{"object":{"const":"account"},"plan":{"type":"string","x-extensible-enum":["free","starter"],"examples":["free","starter"],"description":"What the account pays for. `features` says what it can do today, and the two can differ (for QRX staff, say), so act on `features`. More values may be added."},"codes":{"type":"object","required":["dailyLimit","remainingToday","resetsAt"],"description":"The daily allowance. All null when the account has no daily limit.","properties":{"dailyLimit":{"type":["integer","null"]},"remainingToday":{"type":["integer","null"]},"resetsAt":{"type":["string","null"],"format":"date-time"}}},"features":{"type":"object","required":["wifiCodes","changeDestination"],"properties":{"wifiCodes":{"type":"boolean"},"changeDestination":{"type":"boolean"}}},"rateLimits":{"type":"object","required":["requests","codes"],"properties":{"requests":{"$ref":"#/components/schemas/RateLimit"},"codes":{"$ref":"#/components/schemas/RateLimit"}}},"credentials":{"type":"object","required":["type","prefix","environment"],"properties":{"type":{"type":"string","x-extensible-enum":["api_key","oauth"],"examples":["api_key","oauth"],"description":"More values may be added."},"prefix":{"type":["string","null"],"description":"The start of the API key, enough to tell keys apart."},"environment":{"type":"string","x-extensible-enum":["test","live"],"examples":["test","live"],"description":"The QRX site the credentials work on: `live` on qrx.codes. More values may be added."}}}}},"Problem":{"type":"object","description":"RFC 9457 problem details.","required":["type","title","status","detail"],"properties":{"type":{"type":"string","format":"uri","examples":["https://qrx.codes/developers/docs/errors#unauthorized","https://qrx.codes/developers/docs/errors#forbidden","https://qrx.codes/developers/docs/errors#plan-required","https://qrx.codes/developers/docs/errors#not-found","https://qrx.codes/developers/docs/errors#invalid-request","https://qrx.codes/developers/docs/errors#payload-too-large","https://qrx.codes/developers/docs/errors#unprocessable","https://qrx.codes/developers/docs/errors#idempotency-mismatch","https://qrx.codes/developers/docs/errors#idempotency-in-progress","https://qrx.codes/developers/docs/errors#daily-limit","https://qrx.codes/developers/docs/errors#rate-limited","https://qrx.codes/developers/docs/errors#internal","https://qrx.codes/developers/docs/errors#unavailable"],"description":"A stable URI naming the problem: match on this. Each one is listed in `x-problem-types` and on the errors page; more may be added."},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string","description":"What went wrong, for people. It can change."},"requestId":{"type":"string","description":"The request’s id, also in the `Request-Id` header."},"errors":{"type":"array","description":"On `invalid-request`: each field that is wrong (dotted paths such as `wifi.ssid`), and why.","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}},"x-problem-types":[{"type":"https://qrx.codes/developers/docs/errors#unauthorized","code":"unauthorized","title":"Missing or invalid credentials","status":401,"when":"No credentials were sent, or they are not a QRX key or token, belong to another QRX site, or the key has been revoked.","fix":"Send `Authorization: Bearer <key>` (or `X-API-Key: <key>`) with an active key from the API keys page. A key works only on the QRX site that made it.","retry":false},{"type":"https://qrx.codes/developers/docs/errors#forbidden","code":"forbidden","title":"Not allowed","status":403,"when":"The account the credentials belong to no longer exists.","fix":"Make a key from an active account.","retry":false},{"type":"https://qrx.codes/developers/docs/errors#plan-required","code":"plan-required","title":"Needs the Starter plan","status":403,"when":"The request needs the Starter plan: Wi-Fi codes, or changing where a code points.","fix":"Upgrade the account to Starter, or make a link code instead.","retry":false},{"type":"https://qrx.codes/developers/docs/errors#not-found","code":"not-found","title":"Not found","status":404,"when":"There is no such endpoint, or the account has no code with that id.","fix":"Check the method and path, and use an id returned when the code was created.","retry":false},{"type":"https://qrx.codes/developers/docs/errors#invalid-request","code":"invalid-request","title":"Invalid request","status":400,"when":"The body, query or a header is not valid, for example a `destination` that is not an absolute `http` or `https` URL. `errors` lists each field and what is wrong with it.","fix":"Correct the fields named in `errors` and send the request again.","retry":false},{"type":"https://qrx.codes/developers/docs/errors#payload-too-large","code":"payload-too-large","title":"Request body too large","status":413,"when":"The body is larger than 16 KB.","fix":"Shorten the prompt. A prompt is at most 1,000 characters.","retry":false},{"type":"https://qrx.codes/developers/docs/errors#unprocessable","code":"unprocessable","title":"Request could not be completed","status":422,"when":"The request is valid but could not be carried out, for example a prompt our content filter refuses, or a Wi-Fi code asked to change its destination.","fix":"Read `detail`, change the request and try again.","retry":false},{"type":"https://qrx.codes/developers/docs/errors#idempotency-mismatch","code":"idempotency-mismatch","title":"Idempotency key reused with a different request","status":422,"when":"An `Idempotency-Key` was sent again with a different request body within a day.","fix":"Use a new key for each new request; reuse a key only to retry exactly the same request.","retry":false},{"type":"https://qrx.codes/developers/docs/errors#idempotency-in-progress","code":"idempotency-in-progress","title":"A request with this idempotency key is in progress","status":409,"when":"A request with the same `Idempotency-Key` has not made its code yet. Once it has, a retry answers with that code instead.","fix":"Wait for `Retry-After` seconds and retry with the same key; you then get the code the first request made.","retry":true},{"type":"https://qrx.codes/developers/docs/errors#daily-limit","code":"daily-limit","title":"Daily code limit reached","status":429,"when":"The Free plan’s daily allowance is used up. Codes made in the app count too.","fix":"Wait for `Retry-After` seconds (the `\"codes\"` item of `RateLimit` says the same), or upgrade to Starter, which has no daily limit.","retry":true},{"type":"https://qrx.codes/developers/docs/errors#rate-limited","code":"rate-limited","title":"Too many requests","status":429,"when":"The credentials have made too many requests, or started too many codes, in a short time.","fix":"Wait for `Retry-After` seconds before trying again. Wait for a code with `?wait=` rather than polling quickly.","retry":true},{"type":"https://qrx.codes/developers/docs/errors#internal","code":"internal","title":"Something went wrong","status":500,"when":"Something failed on our side.","fix":"Try again shortly. If it keeps happening, email hello@qrx.codes with the `requestId`.","retry":true},{"type":"https://qrx.codes/developers/docs/errors#unavailable","code":"unavailable","title":"Temporarily unavailable","status":503,"when":"A service QRX relies on did not answer, such as the sign-in service whose keys check OAuth access tokens.","fix":"Your credentials are still good: keep them, and try again after `Retry-After` seconds.","retry":true}],"x-qrx":{"environment":"live","freeDailyCodes":42,"maxActiveKeysPerUser":10,"maxWaitSeconds":50,"rateLimits":{"requests":{"capacity":30,"windowSeconds":60},"generations":{"capacity":6,"windowSeconds":10}}}}