/** * This file was auto-generated by openapi-typescript. * Do not make direct changes to the file. */ export interface paths { "/v1/jobs/init": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get?: never; put?: never; /** * Create a job and get an upload URL * @description Validates the file metadata and returns a short-lived signed URL to PUT the blueprint bytes to. Optional `Idempotency-Key` header. */ post: operations["postV1JobsInit"]; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/jobs/{id}/start": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get?: never; put?: never; /** * Start processing an uploaded job * @description Verifies the uploaded file and queues it for AI inference. Returns immediately; status moves queued → processing → done. */ post: operations["postV1JobsByIdStart"]; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/jobs/{id}": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * Poll job status / get results * @description While running, returns status only. When `done`, returns the take-off summary + per-page metadata. Query: `?confidence=` (flat override), `?include=detections` (inline detections for small jobs). */ get: operations["getV1JobsById"]; put?: never; post?: never; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/jobs/{id}/pages/{page}/detections": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * Per-page detections (paginated) * @description Detections for one page (0-based). Query: `?limit=&offset=&confidence=`. Same confidence filter as the summary, so counts reconcile. */ get: operations["getV1JobsByIdPagesByPageDetections"]; put?: never; post?: never; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/symbols": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * Symbol catalog * @description The full dictionary of detectable symbols (class → display_name → Talo 2010 code → icon). Fetch once and cache; map each `class`/`talo2010_code` to a product in your registry. */ get: operations["getV1Symbols"]; put?: never; post?: never; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/symbols/{class}/icon": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * Symbol icon (image) * @description Redirects (302) to an image of the symbol glyph. Safe to use as an src. */ get: operations["getV1SymbolsByClassIcon"]; put?: never; post?: never; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; } export type webhooks = Record; export interface components { schemas: { InitResponse: { job_id: string; /** @example awaiting_upload */ status: string; external_ref: string | null; upload: { /** @constant */ method: "PUT"; url: string; content_type: string; /** @example 52428800 */ max_bytes: number; expires_at: string; }; }; Error: { error: { /** @example not_found */ code: string; /** @example Job not found */ message: string; details?: unknown; }; }; StartResponse: { job_id: string; /** @example queued */ status: string; poll_url: string; }; JobDone: { job_id: string; /** @constant */ status: "done"; external_ref: string | null; completed_at: string | null; file_name: string | null; /** @example 1 */ page_count: number; /** @example 1 */ pages_with_detections: number; /** @example per_class_default */ confidence_mode: string; inference_thresholds: { [key: string]: { confidence: number; single_class: number; }; }; summary: components["schemas"]["TakeoffSummary"]; pages: { /** @example 0 */ page_index: number; /** @example 1654 */ width_px: number; /** @example 2339 */ height_px: number; /** @example 200 */ render_dpi: number; /** @example 14 */ detection_count: number; detections_url: string; image_url: string | null; detections?: components["schemas"]["Detection"][]; }[]; }; TakeoffSummary: { /** @example 51 */ total_detections: number; by_symbol: { class: string; display_name: string | null; talo2010_code: string | null; category: string | null; /** @example 13 */ count: number; }[]; /** * @example { * "Valot": 18, * "Sireenit": 9 * } */ by_category: { [key: string]: number; }; }; Detection: { /** @example a1b2c3d4-1111-2222-3333-444455556666 */ id: string; /** @example 2os_pistorasia_uppo */ class: string; /** @example 2-os pistorasia uppo */ display_name: string | null; /** @example Pistorasiat */ category: string | null; /** @example electricity_symbols */ symbol_category: string | null; /** @example #ee00ff */ color: string | null; /** @example S241 */ talo2010_code: string | null; icon_url: string | null; /** @example 0.94 */ confidence: number; /** @description Bounding box as fractions 0–1 of the page (top-left origin). Use this to place boxes regardless of render DPI. */ bbox_norm: { x: number; y: number; width: number; height: number; }; /** @description 200-DPI raster pixels (top-left origin). */ bbox_px: { x: number; y: number; width: number; height: number; }; source: { yolo: boolean; vector_matching: boolean; }; gemini?: { decision: string | null; confidence: number | null; }; }; PageDetections: { page_index: number; width_px: number; height_px: number; render_dpi: number; confidence_mode: string; total: number; limit: number; offset: number; next: number | null; detections: components["schemas"]["Detection"][]; }; SymbolCatalog: { /** * @example [ * "fire_alarm", * "electricity" * ] */ models: string[]; /** @example 101 */ count: number; symbols: { class: string; display_name: string | null; category: string | null; symbol_category: string | null; color: string | null; talo2010_code: string | null; icon_url: string | null; }[]; }; }; responses: never; parameters: never; requestBodies: never; headers: never; pathItems: never; } export type $defs = Record; export interface operations { postV1JobsInit: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; requestBody: { content: { /** * @example { * "file_name": "plan.pdf", * "mime_type": "application/pdf", * "size": 1090214, * "external_ref": "offer-123" * } */ "application/json": unknown; }; }; responses: { /** @description Job created; upload the file next. */ 201: { headers: { [name: string]: unknown; }; content: { /** * @example { * "job_id": "2b980b35-2fda-4e3e-b73f-91bfe9b4fd37", * "status": "awaiting_upload", * "external_ref": "offer-123", * "upload": { * "method": "PUT", * "url": "https://….supabase.co/storage/v1/object/upload/sign/pap-files/…?token=…", * "content_type": "application/pdf", * "max_bytes": 52428800, * "expires_at": "2026-06-04T15:05:19.430Z" * } * } */ "application/json": components["schemas"]["InitResponse"]; }; }; /** @description Missing or invalid API key */ 401: { headers: { [name: string]: unknown; }; content: { /** * @example { * "error": { * "code": "unauthorized", * "message": "Missing or invalid API key" * } * } */ "application/json": components["schemas"]["Error"]; }; }; /** @description Unsupported mime_type / size / callback_url */ 422: { headers: { [name: string]: unknown; }; content: { /** * @example { * "error": { * "code": "unprocessable", * "message": "Unsupported mime_type. Allowed: application/pdf, image/jpeg, image/jpg, image/png, image/webp" * } * } */ "application/json": components["schemas"]["Error"]; }; }; /** @description Rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { /** * @example { * "error": { * "code": "rate_limited", * "message": "Limit 120/min exceeded" * } * } */ "application/json": components["schemas"]["Error"]; }; }; }; }; postV1JobsByIdStart: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description Queued for processing. */ 202: { headers: { [name: string]: unknown; }; content: { /** * @example { * "job_id": "2b980b35-2fda-4e3e-b73f-91bfe9b4fd37", * "status": "queued", * "poll_url": "/v1/jobs/2b980b35-2fda-4e3e-b73f-91bfe9b4fd37" * } */ "application/json": components["schemas"]["StartResponse"]; }; }; /** @description Missing or invalid API key */ 401: { headers: { [name: string]: unknown; }; content: { /** * @example { * "error": { * "code": "unauthorized", * "message": "Missing or invalid API key" * } * } */ "application/json": components["schemas"]["Error"]; }; }; /** @description Already started/processed. */ 409: { headers: { [name: string]: unknown; }; content: { /** * @example { * "error": { * "code": "conflict", * "message": "Job already processing" * } * } */ "application/json": components["schemas"]["Error"]; }; }; /** @description No/invalid upload found. */ 422: { headers: { [name: string]: unknown; }; content: { /** * @example { * "error": { * "code": "unprocessable", * "message": "No uploaded file found for this job. PUT the file to the upload URL first." * } * } */ "application/json": components["schemas"]["Error"]; }; }; /** @description Rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { /** * @example { * "error": { * "code": "rate_limited", * "message": "Limit 120/min exceeded" * } * } */ "application/json": components["schemas"]["Error"]; }; }; }; }; getV1JobsById: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description Job status. Running states (`queued`/`processing`) return status only; `done` returns the take-off summary + pages; `failed` returns an error. */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["JobDone"]; }; }; /** @description Missing or invalid API key */ 401: { headers: { [name: string]: unknown; }; content: { /** * @example { * "error": { * "code": "unauthorized", * "message": "Missing or invalid API key" * } * } */ "application/json": components["schemas"]["Error"]; }; }; /** @description Job not found (or not yours). */ 404: { headers: { [name: string]: unknown; }; content: { /** * @example { * "error": { * "code": "not_found", * "message": "Job not found" * } * } */ "application/json": components["schemas"]["Error"]; }; }; /** @description Rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { /** * @example { * "error": { * "code": "rate_limited", * "message": "Limit 120/min exceeded" * } * } */ "application/json": components["schemas"]["Error"]; }; }; }; }; getV1JobsByIdPagesByPageDetections: { parameters: { query?: never; header?: never; path: { id: string; page: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description Paginated detections for the page. */ 200: { headers: { [name: string]: unknown; }; content: { /** * @example { * "page_index": 0, * "width_px": 11575, * "height_px": 4678, * "render_dpi": 200, * "confidence_mode": "per_class_default", * "total": 51, * "limit": 500, * "offset": 0, * "next": null, * "detections": [ * { * "id": "a1b2c3d4-1111-2222-3333-444455556666", * "class": "opasvalo_25m_alas_sivulle", * "display_name": "Opasvalo 25m alas sivulle", * "category": "Valot", * "symbol_category": "fire_alarm_symbols", * "color": "#ee2b79", * "talo2010_code": "S610", * "icon_url": "https://api.massalaskuri.com/v1/symbols/opasvalo_25m_alas_sivulle/icon", * "confidence": 0.95, * "bbox_norm": { * "x": 0.3727, * "y": 0.82343, * "width": 0.00553, * "height": 0.01026 * }, * "bbox_px": { * "x": 4314, * "y": 3852, * "width": 64, * "height": 48 * }, * "source": { * "yolo": true, * "vector_matching": false * } * } * ] * } */ "application/json": components["schemas"]["PageDetections"]; }; }; /** @description Missing or invalid API key */ 401: { headers: { [name: string]: unknown; }; content: { /** * @example { * "error": { * "code": "unauthorized", * "message": "Missing or invalid API key" * } * } */ "application/json": components["schemas"]["Error"]; }; }; /** @description Job/page not found. */ 404: { headers: { [name: string]: unknown; }; content: { /** * @example { * "error": { * "code": "not_found", * "message": "Page 0 not found" * } * } */ "application/json": components["schemas"]["Error"]; }; }; /** @description Rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { /** * @example { * "error": { * "code": "rate_limited", * "message": "Limit 120/min exceeded" * } * } */ "application/json": components["schemas"]["Error"]; }; }; }; }; getV1Symbols: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; requestBody?: never; responses: { /** @description Catalog of current symbols. */ 200: { headers: { [name: string]: unknown; }; content: { /** * @example { * "models": [ * "fire_alarm", * "electricity" * ], * "count": 101, * "symbols": [ * { * "class": "2os_pistorasia_uppo", * "display_name": "2-os pistorasia uppo", * "category": "Pistorasiat", * "symbol_category": "electricity_symbols", * "color": "#ee00ff", * "talo2010_code": "S241", * "icon_url": "https://api.massalaskuri.com/v1/symbols/2os_pistorasia_uppo/icon" * } * ] * } */ "application/json": components["schemas"]["SymbolCatalog"]; }; }; /** @description Missing or invalid API key */ 401: { headers: { [name: string]: unknown; }; content: { /** * @example { * "error": { * "code": "unauthorized", * "message": "Missing or invalid API key" * } * } */ "application/json": components["schemas"]["Error"]; }; }; }; }; getV1SymbolsByClassIcon: { parameters: { query?: never; header?: never; path: { class: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description Redirect to a signed icon image URL. */ 302: { headers: { [name: string]: unknown; }; content?: never; }; /** @description Missing or invalid API key */ 401: { headers: { [name: string]: unknown; }; content: { /** * @example { * "error": { * "code": "unauthorized", * "message": "Missing or invalid API key" * } * } */ "application/json": components["schemas"]["Error"]; }; }; /** @description No icon for this symbol. */ 404: { headers: { [name: string]: unknown; }; content?: never; }; }; }; }