Skip to main content

API overview

Everything is served by one FastAPI service on port 4603 (host port API_PORT). There are two surfaces:

SurfacePrefixVersioned twinContract
Core inference and search/detect, /faces, /persons, /embed, /search, /ingest, /analyze, /ocr, /clusters, /query, /modelsYes: every core router is also mounted under /v1FastAPI's live schema at /docs and /openapi.json
Curation (experimental)/curation (OP_API_PREFIX); data routes are /curation/projects/{project}/...No /v1 twincontracts/openapi/curation.json (generated) and docs/design/curation_api_contract.md (hand-written)

Health and metrics routes (/, /live, /ready, /health, /health/pe_text, /metrics, /connection_pool_info) sit at the root with no prefix; see Health and stall reasons.

curl http://localhost:4603/ | jq '.api_versions'
# {"current":"v1","supported":["v1"],"deprecated":[]}

Conventions​

  • Images are multipart uploads. Single-image routes take a file field named image; batch routes take a repeated images field. /faces/verify takes image1 and image2.
  • Coordinates are normalized to 0.0-1.0 (x1, y1, x2, y2); multiply by the image size for pixels.
  • Tuning knobs are query parameters (confidence, top_k, min_score, model_name, ...), not JSON bodies. /search/text and /search/ocr take text as a query parameter.
  • Embeddings from MobileCLIP and ArcFace are 512-dim and L2-normalized; curation's PE-Core pe_embedding is 1024-dim.
  • No authentication, authorization or rate limiting on any route. Run it on a trusted network behind your own reverse proxy; see Security.

The curation contract​

contracts/openapi/curation.json is app.openapi() filtered to the /curation paths, regenerated with make contracts and checked for drift by a pre-commit hook, so it always matches the code. TypeScript types for the item wire format, class sources and region statuses are generated next to it under contracts/ts/. Point any OpenAPI client generator at that file.

The item wire shape is also exported as contracts/json/item_wire.json and contracts/ts/itemWire.ts, and the keymap action registry as contracts/json/keymap_actions.json.

The hand-written contract (docs/design/curation_api_contract.md) adds the parts a schema can't express: which names are frozen, the one-vocabulary wire rule, undo semantics, error behavior, and breaking-change notes for consumers.

Three rules apply to every curation route:

  • Projects scope the data. Every route that touches data is under /curation/projects/{project}/. The only routes outside a project are project lifecycle and combine, the VLM endpoint registry and catalog, GET /curation/health and GET /curation/events. See Curation routes.
  • The wire vocabulary is generic and fixed. Item-level region summary fields go out as region_<attr>, VLM output as vlm_*, classifier output as classifier_*, whatever storage field names a deployment configured. A region's own data (geometry, score, state, text, cluster, thumbnail URL) is an element of the item's region_boxes list. Every item-returning endpoint emits the same item shape from one serializer, so a client parses one shape.
  • Class identity is the name. Class ids belong to one project's registry. Datasets, combined projects and promoted models are matched to classes by name, never by number.

Read endpoints fail closed: a backend error is an HTTP error, never a silently empty result. Project, config and import routes answer errors as {"detail": {"error": "<code>", "message": "..."}}.