# REST API contract Cross-origin browser access is allowed only from `https://git.sadentel.net`, with GET, POST and OPTIONS and the request headers `X-API-Key`, `X-Viewer-Token` and `Content-Type` (plus the framework's standard safelisted headers). Preflight OPTIONS requires no API key; POST authentication and private-read permissions remain enforced. Cookie credentials are not enabled. Authentication for upload is `X-API-Key`. Private reads accept the same service key or `X-Viewer-Token`, returned only at creation. The service uses one integration key, not multi-tenant user authentication. Use a gateway for organization-specific account roles, key rotation, quotas and rate limits. `POST /api/v1/schematics/render` accepts exactly one multipart file field `file`, with `.SchDoc` suffix (case insensitive). The client-provided content type is not trusted. The request body is capped before multipart spooling, and extracted file bytes are separately capped. Initial format identification accepts OLE signature or an ASCII SchDoc header; complete structural/version validation runs asynchronously in an isolated worker. Optional fields: - `access`: `private` (default) or `unlisted`. - `expires_in`: positive integer seconds, maximum `LINK_TTL_SECONDS`. Expiry starts at upload acceptance, including queue time. HTTP 202 includes `id`, `status`, `viewer_url`, `status_url`, `expires_at` (Unix seconds), `access`, and a `viewer_token` for private uploads. `Location` points to status and `Retry-After: 1` provides a polling interval. Do not automatically retry a POST after a transport failure: each accepted request creates a new resource. Idempotency keys are not implemented. `GET .../{id}/status` returns HTTP 200 for existing, accessible, unexpired jobs, including failed jobs with `success: false` and a stable `error.code`. Failed artifacts return HTTP 422. Status does not disclose the original filename, source bytes or read token. The returned base `viewer_url` omits the original token: retain the initial URL or append your retained read token as a fragment. Completed responses add `schema_version`, `compatibility` (`partial` / `supported_subset`), `record_count`, `component_count`, `primitive_count`, and `diagnostics` (severity, code, message, record id/type, count). Current font metric warnings mean typical documents are explicitly partial. Warnings must be checked by applications where accurate reproduction matters. `GET .../{id}/data` returns the document schema from `backend/app/models/schematic.py`: - `schema_version`, `document_type`, `id`, `filename`, and `metadata` (source SHA-256, format/header, streams/counts). - `sheet` (original properties, page bounds/background), `bounds` (content bounds). - `components` / `symbols` / `objects` with ownership IDs, placement, raw properties, active visibility, pin anchors and primitive references. - `primitives` in source paint order (paths, text, sanitized embedded rasters). - `diagnostics` with compatibility failures/warnings. Raw properties are data, never executable DOM content. Coordinates use 10 mil units, X to the right and Y downward (inverted once at parse time). The page begins at `(0, -height)` and ends at `(width, 0)`. Page dimensions are independent of content bounds. Consumers should not rotate/mirror placed component children again. `GET .../{id}/svg` uses the same document primitives and page dimensions as the viewer; it has no interaction code. The interactive `/view/{id}` loads it and changes only its camera viewBox. Wheel zoom retains the point beneath the cursor; pointer events support drag/pinch. Double-click or Home resets fit; +/− and arrows provide keyboard control. Browser zoom, exact font availability and gesture behavior need actual browser validation. Error code examples: | HTTP | Code | | --- | --- | | 401 | unauthorized | | 404 | not_found (also private permission denied) | | 409 | not_ready | | 410 | link_expired (until retention cleanup) | | 413 | upload_too_large | | 415 | unsupported_file_type | | 422 | invalid_request / invalid_expiry / invalid_schematic / parse_timeout / parse_resource_limit | | 503 | queue_full / storage_unavailable / database_unavailable | A worker crash is retried after its lease expires, at most twice total. A parse failure is terminal for that upload. No automatic retry of side-effecting API requests is performed. Data and SVG reads are immutable after a successful attempt; access/expiry remain server-authoritative on every read. HTTP caches are disabled to protect private drawings. ## Schematic comparisons `POST /api/v1/comparisons` (alias `/api/compare`) accepts `old_file` and `new_file`, with the same API key, private tokens, expiry, durable queue and error conventions. `GET /api/v1/comparisons/{id}` (alias `/api/compare/{id}`) returns status and the completed structural diff. `/compare/{id}` opens the side-by-side workspace. See [the comparison contract](SCHEMATIC_DIFF.md).