/**
* 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;
};
};
};
}