Skip to main content
All paths are under the base URL (/api/v1) and require authentication. Reads are workspace-member; writes require the work:write scope over OAuth; purge is owner/admin only. Reads are active-only, bounded and brief by default — archived objects only surface with an explicit opt-in. See Collections for the model.

Collections

Create body (CreateCollectionDto): name (required), archetype (required, one of pipeline, analysis, content), record_schema, views, criterion_rel_slugs, distill_policy, owner_clerk_id. Update accepts name, record_schema, views, criterion_rel_slugs, distill_policy — a status of archived returns 409 and names the lifecycle route to use.

Records

Base path /workspaces/:slug/collections/:id. Record body (CreateRecordDto / UpdateRecordDto): fields, status, field_meta, entity_id, and drifted on update. A record’s status is validated against the collection’s stages. entity_id is a top-level record property, never a field inside fields (pass "" on update to clear the link). A transition to a terminal stage triggers the distill_policy and may attach a context_outcome (created · already_exists · skipped · failed) to the record — best-effort, it never blocks the write.

Entities

Upsert body (UpsertEntityDto): kind (required), name (required), dedup_key, attributes. The upsert is idempotent on (kind, dedup_key) — re-upserting the same pair merges name/attributes instead of creating a duplicate.

Pagination and errors

Collection list and search paginate by keyset cursor (limit 1–50, default 20; cursor opaque from the prior nextCursor); list is back-compat dual-shaped — bare array when no paging/filter param is present, the {items, nextCursor} envelope otherwise. Records list and retrieve paginate by keyset (limit 1–100, default 25; keyset on created_at, id). Entities list is a bounded array (no cursor). An invalid status (outside the collection’s stages) or archetype, or an unknown body field, returns 400 VALIDATION_FAILED. A malformed fields JSON on retrieve is ignored silently, never an error. See errors for the envelope.

Lifecycle: archive and purge

  • POST .../:id/archive retires an active collection. POST .../:id/restore revives an archived collection to active. Both increment version and are audited. An archived collection is immutablePATCH, record writes and retrieve return 409 PROTECTED_RESOURCE.
  • DELETE .../:id?dry_run=true previews impact (distilled_notes_preserved counts the distilled Notes that survive the delete; entity_links_removed counts broken record links). DELETE .../:id?expected_version=<n> performs the purge with compare-and-set; a mismatched expected_version returns 409 VERSION_CONFLICT. Owner/admin only, audited, idempotent — a repeat after the purge returns already_absent.
The safe record delete is the same shape: DELETE .../records/:recordId only deletes from a non-archived collection; ?dry_run=true previews the Entity link removed and the distilled Notes preserved (they survive the delete, they’re not re-evaluated). The delete is workspace-membership gated (not owner/admin), audited, idempotent — a repeat after the delete returns already_absent.