API Specification

Two surfaces:

  1. REST endpoints — for the SvelteKit app and external clients (curl, Postman, agents calling REST)
  2. MCP tools — for AI agents using Model Context Protocol

Both wrap the same underlying operations. MCP is just a translation layer over REST.


REST Endpoints

All under /api/*. Hosted by the SvelteKit app or a dedicated Worker (TBD by rewrite).

Workspaces

POST /api/workspaces

Create a new workspace.

Request: { name?: string }

Response: { id, slug, name, created_at }

GET /api/workspaces/:id

Get current workspace state (snapshot from Y.Doc).

Response includes:

  • workspace metadata (id, slug, name, created_at, version)
  • settings (Y.Map serialized) — keys masked
  • layout, order_state, cv_profile, cl_profile, cloud_keys (with masked keys)

Used for fresh client load before WebSocket sync establishes.

PATCH /api/workspaces/:id

Direct workspace PATCH applies the provided patch through the broker. Prefer resource-specific endpoints or /ops for new writes.

Behavior: applies provided patch to Y.Doc via the broker.

Jobs

GET /api/workspaces/:id/jobs

List all jobs in the workspace.

Response: Job[]

POST /api/workspaces/:id/jobs

Create a new job.

Request: partial Job (at minimum: { url? | title? })

Response: created Job. Pipeline auto-advances if URL provided.

PATCH /api/workspaces/:id/jobs/:jobId

Update job fields.

Request: partial Job. Special flags:

  • _statusOnly — fast path for status changes (steward auto-advance)
  • _salaryOnly — fast path for salary + origin (enrichment stage)
  • _tailoredClOnly — fast path for CL section edits
  • _formatOnly — format_overrides only

Response: updated Job.

DELETE /api/workspaces/:id/jobs/:jobId

Delete a workspace job by committing job.delete to the jobs sub-doc.

Workspace Intelligence

Workspace Intelligence is the process-state surface. Browser UI, MCP, and future schedulers should read and command pipeline state here instead of building parallel job/activity interpretations.

GET /api/workspaces/:id/activity

List durable process ledger events.

Query filters: limit, jobId, stage, status, severity, actorClass, actorId.

Response: { activity, filters, nowMs }.

GET /api/workspaces/:id/intelligence

Return the workspace process projection.

Query filters: activityLimit, jobLimit, jobId.

Response includes:

  • summary — job count, stage quality, queue pressure, warning count
  • stageSummaries — RADAR / EXTRACT / ENRICH / EVALUATE / TAILOR counts
  • jobStates — per-job stage state, warnings, latest activity, available commands
  • jobs — resolved workspace job projections for the dashboard intelligence panel
  • activity — recent process ledger rows

POST /api/workspaces/:id/intelligence/commands/rerun-extract

Queue extraction repair for a job. Clears AI-owned extracted fields, preserves human-origin fields by default, stamps a process command marker on the job, and commits a job.update op so open browser tabs can re-run EXTRACT through the same pipeline actor model.

Request: { jobId, reason?, actorClass?, actorId?, clearHumanFields? }

Response: { command, workspaceId, jobId, requestId, clearedFields, preservedHumanFields }

GET /api/workspaces/:id/process-requests

List durable Workspace Intelligence process requests.

Query filters: limit, jobId, stage, status.

Response: { requests, nowMs }.

POST /api/workspaces/:id/process-requests/claim

Claim queued process requests for an executor. Browser tabs are the preferred worker class; future local/server runners use the same contract.

Request: { workerId, workerClass, stages?, jobIds?, max?, leaseMs? }

Response: { requests, nowMs, unavailable? }, where each claimed request includes claimToken. Missing queue migrations return an empty unavailable response rather than a failing response so browser workers stay quiet during staged deploys.

PATCH /api/workspaces/:id/process-requests/:requestId

Report lifecycle status for a claimed process request. Terminal statuses clear the process marker in jobs.chain_overrides.__process so stale browser commands do not re-run after reload.

Request: { status, workerId?, claimToken?, summary?, error?, result? }

Response: { request, nowMs }.

CV / CL operations

POST /api/cv/generate?workspace=:wsid

Persist a tailored CV. The LLM call runs on the client; this endpoint just writes the result.

Request: { job_id, profile: Node, providerId, model, usage, wasFallback }profile is a full Node-tree snapshot.

Response: { success: true, jobId }. Broadcasts job_update via realtime.

POST /api/cl/generate?workspace=:wsid

Same shape, for cover letter.

Settings (via Yjs ops)

Settings updates flow through the typed op catalog (lib/mutations/src/opsCatalog.ts): settings.update, layout.update, order_state.update. Clients dispatch through the workspace SDK; the REST endpoints accept ops and forward them to the broker.

Chain / cascade

GET /api/chain?workspace=:wsid

Returns ChainConfig: { providerKeys, chains }. This is the execution path — keys are real (unmasked). Mode-gated:

  • dev mode: env vars merged in
  • production mode: only workspace BYOK keys

Files (R2)

GET /api/files?id=:fileId&workspace=:wsid

Stream a file from R2. Workspace ownership check.

POST /api/files?workspace=:wsid&job_id=:jobId&type=cv|cl|misc

Multipart upload. Stores in R2, updates jobs.files in Y.Doc.

DELETE /api/files?id=:fileId&workspace=:wsid

Delete from R2 + Y.Doc reference.

Health / telemetry

GET /api/health

Returns: { ok: true, ts }.

POST /api/telemetry

Append-only telemetry event log (D1 table). Rate-limited.


MCP Tools

Each REST endpoint becomes an MCP tool. Tool definitions follow the Model Context Protocol Tool Spec.

The MCP server is a separate Cloudflare Worker. It connects to the same D1 + DO + R2 as the SvelteKit app.

Connection

Clients (ChatGPT, Claude, Claude Code, Cursor, etc.) configure the remote MCP server URL:

{
  "mcpServers": {
    "careervector": {
      "url": "https://mcp.careervector.corbet.ch/mcp"
    }
  }
}

Tool calls still include wsId; workspace ID remains the access credential. See wiki/content/architecture/MCP-CONNECTORS.md for current ChatGPT/Claude setup notes and CVL editing workflow guidance.

Tools available

Tool Wraps Description
add_job POST /jobs Add a job by URL or manual fields. Pipeline auto-advances.
list_jobs GET /jobs Returns all jobs in the workspace
get_job GET /jobs/:id Returns a single job
update_job PATCH /jobs/:id Update fields (idempotent for same id+fields)
delete_job DELETE /jobs/:id Delete
tailor (Yjs op) Persist a tailored CV/CL snapshot for a job
get_cv_typst Typst render source Return CV Typst source for agents
compile_cv_pdf Koyeb Typst service Compile CV Typst source to PDF
get_cvl_tree workspace snapshot Return exact CVL node paths and editable node data
patch_node_data (Yjs op) Shallow-merge node data for safe chatbot edits
update_node (Yjs op) Update a CV/CL quarry node
add_node (Yjs op) Add a node to the quarry
move_node (Yjs op) Reorder or reparent nodes
delete_node (Yjs op) Delete a node
set_selection (Yjs op) Choose active child variants for rendering
set_score_criterion (settings update) Update a scoring criterion config
evaluate_job POST /jobs/:id/evaluate (action) Run scoring evaluation
list_views (settings read) Returns view definitions
set_active_view (Yjs op on settings) Switch dashboard view
get_workspace_summary GET /workspaces/:id Concise summary for context-loading
list_activity GET /activity Read filtered Workspace Intelligence activity
get_workspace_intelligence GET /intelligence Read process quality, job states, and queue pressure
get_job_process_state GET /intelligence?jobId=... Read one job's pipeline state and warnings
rerun_extract POST /intelligence/commands/rerun-extract Queue extraction repair for a job

Note: where MCP tools translate to Yjs ops (not REST), the MCP server applies the op via the realtime broker on the client's behalf.

Tool semantics

Per principle 7 (agent API parity):

  • Every tool is idempotent (retries don't double-apply)
  • Every tool returns a clear success/failure result with explanation
  • Every tool emits realtime ops so connected GUI clients see the change

Streaming responses

For long-running operations (tailor, evaluate), MCP supports streaming. The agent sees progress (e.g., "evaluating criterion 3 of 7") rather than waiting for completion.

Error handling

Standard errors:

  • WorkspaceNotFound — invalid workspace ID
  • JobNotFound — job doesn't exist in this workspace
  • ChainExhaustion — all LLM providers in cascade failed
  • Locked — singleflight lock active (retry after notification)
  • RateLimit — provider 429, retry after delay
  • InvalidShape — Zod validation failed (with detail)

Schema source of truth

All request/response shapes derive from Zod schemas in lib/schemas/:

  • JobSchema, NodeSchema / LangTreeSchema (CV/CL document trees), WorkspaceSettingsSchema, CloudKeysSchema, etc.

Zod schemas → OpenAPI spec (auto-generated via hono-openapi or similar) → MCP tool definitions.

This means: change a schema, the API spec updates everywhere automatically. No drift.

Source: wiki/content/architecture/API-SPEC.md