{"components":{"parameters":{"cooldown":{"description":"Seconds a code must be absent before it is reported as lost (and may be reported again)","in":"query","name":"cooldown","schema":{"default":3,"maximum":3600,"minimum":0,"type":"number"}},"emit_lost":{"description":"Include `lost` events","in":"query","name":"emit_lost","schema":{"default":true,"type":"boolean"}},"engine":{"description":"Decoding engine","in":"query","name":"engine","schema":{"enum":["rxing","zxing-cpp"],"type":"string"}},"formats":{"description":"Comma separated formats to look for (see /v1/formats), or `all`, `1d`, `2d`","in":"query","name":"formats","schema":{"default":"all","examples":["qr_code,ean_13"],"type":"string"}},"fps":{"description":"Frames per second to scan","in":"query","name":"fps","schema":{"default":5,"exclusiveMinimum":0,"maximum":120,"type":"number"}},"id":{"description":"Task id","in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},"mode":{"description":"`sync` waits for the result; `async` returns a task","in":"query","name":"mode","schema":{"default":"sync","enum":["sync","async"],"type":"string"}},"replay":{"description":"Replay buffered events (last 200) before live ones","in":"query","name":"replay","schema":{"default":true,"type":"boolean"}},"try_harder":{"description":"Spend more time per image (rotation, inversion; for images also retry with the other engine and a 2x upscale when nothing is found)","in":"query","name":"try_harder","schema":{"default":true,"type":"boolean"}},"url":{"description":"http(s) URL to download and decode instead of an upload (send an empty body). Public instances only fetch from public hosts on ports 80/443 and follow at most 5 redirects","in":"query","name":"url","schema":{"format":"uri","type":"string"}},"webhook":{"description":"Async mode only: http(s) URL that receives `detected`, `lost` and the final `status` events as JSON POSTs","in":"query","name":"webhook","schema":{"format":"uri","type":"string"}}},"requestBodies":{"Upload":{"content":{"application/octet-stream":{"schema":{"format":"binary","type":"string"}},"image/*":{"schema":{"format":"binary","type":"string"}},"multipart/form-data":{"schema":{"properties":{"file":{"format":"binary","type":"string"}},"required":["file"],"type":"object"}},"video/*":{"schema":{"format":"binary","type":"string"}}},"required":true},"UploadOrUrl":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UrlInput"}},"application/octet-stream":{"schema":{"format":"binary","type":"string"}},"image/*":{"schema":{"format":"binary","type":"string"}},"multipart/form-data":{"schema":{"properties":{"file":{"format":"binary","type":"string"}},"required":["file"],"type":"object"}},"video/*":{"schema":{"format":"binary","type":"string"}}},"description":"The file itself, or a JSON object with the `url` to download (omit the body when `url` is in the query string)"}},"responses":{"BadRequest":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid parameters"},"Busy":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"All decode (or download) slots are busy; retry shortly"},"Forbidden":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Disabled on this server"},"NotFound":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unknown task (finished tasks are kept for 1 hour)"},"RateLimited":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Too many requests (or too many running tasks); see Retry-After","headers":{"Retry-After":{"description":"Seconds to wait","schema":{"type":"integer"}}}},"TooLarge":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Upload exceeds the size limit"},"Unprocessable":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unsupported or corrupt input, or `url` could not be downloaded (non-2xx answer, network error, timeout); `error` says which"}},"schemas":{"Camera":{"properties":{"description":{"type":"string"},"index":{"type":"integer"},"name":{"type":"string"}},"type":"object"},"DetectedEvent":{"allOf":[{"$ref":"#/components/schemas/Detection"},{"properties":{"event":{"const":"detected"},"frame":{"description":"Decoded frame index","type":"integer"},"seq":{"description":"Task events only","type":"integer"},"t":{"description":"Seconds","type":"number"},"task_id":{"description":"Task events only","type":"string"}},"required":["event","t","frame"],"type":"object"}]},"Detection":{"properties":{"engine":{"$ref":"#/components/schemas/Engine"},"format":{"$ref":"#/components/schemas/Symbology"},"position":{"description":"Pixel coordinates. zxing-cpp: 4 corners (TL, TR, BR, BL); rxing: finder-pattern / scan-line points","items":{"$ref":"#/components/schemas/Point"},"type":"array"},"raw_b64":{"description":"Raw payload bytes, base64","type":"string"},"text":{"description":"Decoded text","type":"string"}},"required":["format","text","raw_b64","position","engine"],"type":"object"},"Engine":{"enum":["rxing","zxing-cpp"],"type":"string"},"Error":{"properties":{"error":{"type":"string"}},"required":["error"],"type":"object"},"Event":{"discriminator":{"propertyName":"event"},"oneOf":[{"$ref":"#/components/schemas/DetectedEvent"},{"$ref":"#/components/schemas/LostEvent"},{"$ref":"#/components/schemas/StatusEvent"}]},"Formats":{"properties":{"engines":{"items":{"$ref":"#/components/schemas/Engine"},"type":"array"},"formats":{"items":{"properties":{"kind":{"enum":["1d","2d"],"type":"string"},"name":{"$ref":"#/components/schemas/Symbology"}},"type":"object"},"type":"array"}},"type":"object"},"Health":{"properties":{"default_engine":{"$ref":"#/components/schemas/Engine"},"engines":{"items":{"$ref":"#/components/schemas/Engine"},"type":"array"},"ffmpeg":{"type":"string"},"status":{"type":"string"},"version":{"type":"string"}},"type":"object"},"ImageResult":{"properties":{"detections":{"items":{"$ref":"#/components/schemas/Detection"},"type":"array"},"elapsed_ms":{"type":"number"},"height":{"type":"integer"},"strategy":{"description":"Which pass found the codes. When the first pass finds nothing (and try_harder is true) the other engine is tried, then, for images under 1600px, a 2x upscaled copy. null = nothing found","enum":["direct","alt-engine","upscale-2x","upscale-2x-alt-engine",null],"type":["string","null"]},"width":{"type":"integer"}},"required":["width","height","detections","elapsed_ms"],"type":"object"},"LostEvent":{"allOf":[{"$ref":"#/components/schemas/Occurrence"},{"properties":{"event":{"const":"lost"},"seq":{"type":"integer"},"t":{"type":"number"},"task_id":{"type":"string"}},"required":["event","t"],"type":"object"}]},"Occurrence":{"description":"One appearance of a code, from first detection until it was lost","properties":{"first_seen":{"description":"Seconds (media time for uploaded videos)","type":"number"},"format":{"$ref":"#/components/schemas/Symbology"},"frames":{"description":"Scanned frames the code was seen in","type":"integer"},"last_seen":{"type":"number"},"raw_b64":{"type":"string"},"text":{"type":"string"}},"required":["format","text","raw_b64","first_seen","last_seen","frames"],"type":"object"},"Point":{"properties":{"x":{"type":"number"},"y":{"type":"number"}},"required":["x","y"],"type":"object"},"ScanStats":{"properties":{"duration":{"type":"number"},"frames_decoded":{"type":"integer"},"frames_scanned":{"type":"integer"},"frames_skipped":{"type":"integer"},"reconnects":{"type":"integer"}},"type":"object"},"StatusEvent":{"properties":{"attempt":{"type":"integer"},"event":{"const":"status"},"message":{"type":"string"},"seq":{"type":"integer"},"state":{"$ref":"#/components/schemas/TaskState"},"t":{"type":"number"},"task_id":{"type":"string"}},"required":["event","t","state"],"type":"object"},"StreamRequest":{"description":"Exactly one of `url` or `camera`","properties":{"camera":{"description":"Camera index (see /v1/cameras) or device name","oneOf":[{"type":"integer"},{"type":"string"}]},"cooldown":{"type":"number"},"emit_lost":{"type":"boolean"},"engine":{"$ref":"#/components/schemas/Engine"},"formats":{"oneOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"}]},"fps":{"type":"number"},"io_timeout":{"description":"Seconds an open/read may block","type":"number"},"max_retries":{"description":"Give up after this many consecutive failed reconnects (default: forever)","type":"integer"},"rtsp_transport":{"enum":["tcp","udp","udp_multicast","http","https"],"type":"string"},"try_harder":{"type":"boolean"},"url":{"description":"rtsp, rtsps, rtmp, rtmps, rtp, srtp, srt, udp, http or https URL","type":"string"},"video_size":{"examples":["1280x720"],"type":"string"},"webhook":{"format":"uri","type":"string"}},"type":"object"},"Symbology":{"enum":["qr_code","micro_qr_code","rmqr_code","data_matrix","aztec","pdf417","maxicode","ean_13","ean_8","upc_a","upc_e","upc_ean_extension","code_128","code_39","code_93","codabar","itf","databar","databar_expanded","telepen","dx_film_edge","other"],"type":"string"},"TaskInfo":{"properties":{"active":{"description":"Codes currently in view","items":{"$ref":"#/components/schemas/Occurrence"},"type":"array"},"created_at":{"description":"Unix seconds","type":"number"},"error":{"type":"string"},"events_url":{"type":"string"},"id":{"format":"uuid","type":"string"},"kind":{"enum":["stream","camera","video"],"type":"string"},"occurrences":{"description":"Finished appearances (latest 1000)","items":{"$ref":"#/components/schemas/Occurrence"},"type":"array"},"source":{"description":"Source description (URL passwords masked)","type":"string"},"state":{"$ref":"#/components/schemas/TaskState"},"stats":{"$ref":"#/components/schemas/ScanStats"},"ws_url":{"type":"string"}},"required":["id","kind","source","state","created_at","events_url","ws_url","active","occurrences"],"type":"object"},"TaskState":{"enum":["created","connecting","running","reconnecting","finished","stopped","failed"],"type":"string"},"UrlInput":{"description":"Any query parameter of the endpoint may also be given here (body values win)","properties":{"cooldown":{"type":"number"},"emit_lost":{"type":"boolean"},"engine":{"enum":["rxing","zxing-cpp"],"type":"string"},"formats":{"oneOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"}]},"fps":{"type":"number"},"mode":{"enum":["sync","async"],"type":"string"},"try_harder":{"type":"boolean"},"url":{"description":"http(s) URL to download and decode","format":"uri","type":"string"}},"required":["url"],"type":"object"},"VideoResult":{"properties":{"error":{"type":"string"},"events":{"items":{"$ref":"#/components/schemas/Event"},"type":"array"},"occurrences":{"items":{"$ref":"#/components/schemas/Occurrence"},"type":"array"},"source":{"type":"string"},"state":{"$ref":"#/components/schemas/TaskState"},"stats":{"$ref":"#/components/schemas/ScanStats"}},"required":["source","state","stats","occurrences","events"],"type":"object"}}},"info":{"description":"Read QR codes and barcodes (QR, Micro QR, rMQR, DataMatrix, Aztec, PDF417, MaxiCode, EAN/UPC, Code 128/39/93, Codabar, ITF, DataBar, Telepen, DX film edge) from images and videos.\n\nVideos are scanned at `fps` frames per second. Results are de-duplicated: a code is reported once when it appears (`detected`) and `lost` once it has been absent for longer than `cooldown` seconds; it is reported again if it re-appears after that.\n\nPublic instance: uploads and `url` downloads only (stream/camera tasks and webhooks are disabled).","license":{"name":"Apache-2.0"},"title":"codereader API","version":"0.1.0"},"openapi":"3.1.0","paths":{"/healthz":{"get":{"operationId":"health","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}},"description":"Service is up"}},"summary":"Health and build information","tags":["meta"]}},"/llms.txt":{"get":{"operationId":"llmsTxt","responses":{"200":{"content":{"text/plain":{}},"description":"Markdown text"}},"summary":"Usage guide for AI assistants (llms.txt)","tags":["meta"]}},"/openapi.json":{"get":{"operationId":"openapi","responses":{"200":{"content":{"application/json":{}},"description":"OpenAPI 3.1 document"}},"summary":"This OpenAPI document","tags":["meta"]}},"/v1/decode/image":{"get":{"description":"Same as the POST form with `url`, for clients that can only send GET requests (e.g. AI assistants with a browsing tool). The server downloads the image and decodes it.","operationId":"decodeImageUrl","parameters":[{"description":"http(s) URL of the image","in":"query","name":"url","required":true,"schema":{"format":"uri","type":"string"}},{"$ref":"#/components/parameters/engine"},{"$ref":"#/components/parameters/formats"},{"$ref":"#/components/parameters/try_harder"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageResult"}}},"description":"Decoded codes (possibly none)"},"400":{"$ref":"#/components/responses/BadRequest"},"413":{"$ref":"#/components/responses/TooLarge"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/Busy"}},"summary":"Decode all codes in an image at a URL","tags":["decode"]},"post":{"description":"Send the image as the raw request body, or as the `file` field of a multipart form — or let the server download it: give `url` as a query parameter (with an empty body) or as a JSON body `{\"url\": \"https://…\"}`. PNG, JPEG, BMP, GIF, WebP and TIFF are supported.","operationId":"decodeImage","parameters":[{"$ref":"#/components/parameters/url"},{"$ref":"#/components/parameters/engine"},{"$ref":"#/components/parameters/formats"},{"$ref":"#/components/parameters/try_harder"}],"requestBody":{"$ref":"#/components/requestBodies/UploadOrUrl"},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageResult"}}},"description":"Decoded codes (possibly none)"},"400":{"$ref":"#/components/responses/BadRequest"},"413":{"$ref":"#/components/responses/TooLarge"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/Busy"}},"summary":"Decode all codes in an image","tags":["decode"]}},"/v1/decode/video":{"post":{"description":"Send the video as the raw request body or as the `file` field of a multipart form — or give `url` (query parameter with an empty body, or JSON body `{\"url\": \"https://…\"}`) and the server downloads it first. Common containers are accepted (MP4/MOV, MKV/WebM, AVI, FLV, MPEG-TS, MPEG-PS, ASF, Ogg, MJPEG, GIF, raw H.264/HEVC).\n\n`mode=sync` (default) returns every appearance when the scan is done. `mode=async` returns a task immediately (HTTP 202); follow it with `GET /v1/streams/{id}/events` (SSE), `/ws` (WebSocket) or poll `GET /v1/streams/{id}`.","operationId":"decodeVideo","parameters":[{"$ref":"#/components/parameters/url"},{"$ref":"#/components/parameters/mode"},{"$ref":"#/components/parameters/engine"},{"$ref":"#/components/parameters/formats"},{"$ref":"#/components/parameters/try_harder"},{"$ref":"#/components/parameters/fps"},{"$ref":"#/components/parameters/cooldown"},{"$ref":"#/components/parameters/emit_lost"}],"requestBody":{"$ref":"#/components/requestBodies/UploadOrUrl"},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoResult"}}},"description":"Synchronous result. `state` is `finished`, or `stopped` when the server's time limit cut the scan short (`error` says so; results up to that point are included)."},"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskInfo"}}},"description":"Async task created"},"400":{"$ref":"#/components/responses/BadRequest"},"413":{"$ref":"#/components/responses/TooLarge"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoResult"}}},"description":"The upload is not a readable video, or `url` could not be downloaded (non-2xx answer, network error, timeout); `error` says which"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/Busy"}},"summary":"Decode codes in a video","tags":["decode"]}},"/v1/formats":{"get":{"operationId":"formats","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Formats"}}},"description":"Formats and engines"}},"summary":"Supported barcode formats and decoding engines","tags":["meta"]}},"/v1/streams/{id}":{"delete":{"operationId":"deleteStream","responses":{"204":{"description":"Stopped and removed"},"404":{"$ref":"#/components/responses/NotFound"}},"summary":"Stop and remove a task","tags":["tasks"]},"get":{"operationId":"getStream","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskInfo"}}},"description":"Task"},"404":{"$ref":"#/components/responses/NotFound"}},"summary":"Task state, visible codes, finished appearances and stats","tags":["tasks"]},"parameters":[{"$ref":"#/components/parameters/id"}]},"/v1/streams/{id}/events":{"get":{"description":"Each SSE message has `event:` set to `detected`, `lost` or `status`, `id:` set to the sequence number, and `data:` holding an `Event` JSON object. The last 200 events are replayed first (unless `replay=false`). The stream ends after the task's final `status` event (`finished`, `stopped` or `failed`).","operationId":"streamEvents","responses":{"200":{"content":{"text/event-stream":{"schema":{"$ref":"#/components/schemas/Event"}}},"description":"SSE stream"},"404":{"$ref":"#/components/responses/NotFound"}},"summary":"Task events as Server-Sent Events","tags":["tasks"]},"parameters":[{"$ref":"#/components/parameters/id"},{"$ref":"#/components/parameters/replay"}]},"/v1/streams/{id}/ws":{"get":{"description":"Upgrade to a WebSocket; every text frame is one `Event` JSON object. Same replay and end-of-stream behaviour as the SSE endpoint.","operationId":"streamWebSocket","responses":{"101":{"description":"Switching protocols"},"404":{"$ref":"#/components/responses/NotFound"}},"summary":"Task events over WebSocket","tags":["tasks"]},"parameters":[{"$ref":"#/components/parameters/id"},{"$ref":"#/components/parameters/replay"}]}},"servers":[{"url":"https://decode.ivjn.us"}],"tags":[{"description":"Decode uploaded images and videos","name":"decode"},{"description":"Long running scans (async video uploads, streams, cameras) and their event feeds","name":"tasks"},{"description":"Service information","name":"meta"}]}