Skip to content

Local TypeScript API

The local Fastify API is the browser-facing boundary for JobCtrl. It serves projection-backed reads, accepts explicit commands, starts Temporal workflows, and streams domain-event invalidations to the web app.

Read this first for the shape of the API. Follow a route-family link for the common contracts, or open the complete field-level contract when implementing or debugging a specific endpoint.

In 30 Seconds

  • Reads come from SQLite-backed projections; clients do not reconstruct domain state from raw events.
  • Workflow-starting commands normally return 202 Accepted. That means queued, not completed.
  • Synchronous reads and commands return 200 OK; invalid input, unavailable workers, and start failures return an error instead of a misleading 202.
  • Browser updates arrive through GET /v1/events/stream; TanStack Query exact- patches truthful detail payloads and boundedly refetches affected projections when membership, aggregation, or missing event data requires reconciliation.
  • Credentials are handled through the local credential boundary, never through ordinary settings payloads.

Choose A Route Family

You need to…Start here
Read or update the candidate profile, preferences, discovery settings, or credentialsProfile & Settings
List jobs, inspect score/material evidence, review applications, or work with contactsJobs & Materials
Start/cancel workflows, inspect current pipeline operations, inspect health, or consume realtime eventsOperations & Events
Check every field, status code, precedence rule, and cadenceComplete Contract

API At A Glance

BoundaryRepresentative routesResponse model
Profile and configuration/v1/profile, /v1/settings, /v1/credentials, /v1/providers/models, /v1/discovery/settings, /v1/browser-capabilities, /v1/extension/pairing-tokenSynchronous reads and validated patches
Jobs and evidence/v1/jobs, /v1/jobs/:jobKey, /v1/evidence-map, /v1/artifactsProjection-backed reads
Scoring keywords and feedback learning/v1/scoring/keywords, /v1/learning/recommendations, /v1/learning/policies/materialsCurrent score-version aggregation plus explicit review and versioned policy history
Review and outcomes/v1/apply/review-queue, /v1/jobs/:jobKey/apply-review/decision, /v1/jobs/:jobKey/repeat-application/override, /v1/outcomesExplicit review commands plus read models
Workflow operations/v1/pipeline/actions/run-stage, /v1/pipeline/operations, /v1/workflow-runs, /v1/health202 for accepted asynchronous work; 200 for projection-backed and runtime-backed reads/sync commands
Realtime/v1/events/streamServer-Sent Events with replay and reconnect support

Profile And Preferences

Profile data, preferences, discovery controls, settings, and credentials have different owners even though the UI presents them together. See Profile & Settings for that ownership map and the routes used by autosave, resume preview, source administration, and Keychain-backed credentials.

Artifacts And Tailoring Audit

Artifact list/detail/preview routes and canonical tailoring evidence are grouped with job reads in Jobs & Materials. The complete contract documents every generation, template, provenance, layout, and audit field.

Jobs Read Model And Lifecycle

The jobs list and detail routes read stable projections. Lifecycle changes—hide, restore, delete, score correction, stage retry, and per-job actions—are explicit commands. See Jobs & Materials.

On job detail, Enrich stage summaries can carry a separate, allow-listed applyUrlOutcome (code, message, retryable, and method). It reports application-target readiness without changing posting-content trust or stage success; raw browser/resolver errors are not exposed.

Employer-analysis requirements on job detail expose coverage_scope as resume, eligibility, logistics, employer_condition, or null for historical analyses. Both projection builders normalize missing historical values to null, so the TypeScript and Python read models remain identical.

Internal list/detail identity is the tenant-scoped stable JobId; posting URLs are locators resolved only at explicit import or API boundaries. Employer and Source remain independent facts. GET /v1/scoring/keywords aggregates indexed, normalized keywords from the current score version, and GET /v1/jobs accepts an exact normalizedScoreKeyword filter using those returned keys.

Feedback Learning And Policy History

GET /v1/learning/recommendations and its evidence route expose only bounded, privacy-safe derived evidence. The review route requires an explicit accepted or rejected decision. Acceptance creates a versioned Materials policy revision; rejection changes no policy. GET /v1/learning/policies/materials exposes the allowlisted current/superseded history, while the rollback route appends a new user-requested revision that restores an earlier version. These routes never start scoring, tailoring, Apply, or artifact work. See Jobs & Materials.

Dashboard, Analytics, And Operational Metrics

GET /v1/dashboard/summary, GET /v1/analytics/outcomes, GET /v1/digest, and GET /v1/debug/activity expose read-side summaries. Their exact filters and shapes live in the complete contract.

GET /v1/pipeline/operations is the dedicated current-operations read model. It selects the active or draining Discover execution, falling back to the latest terminal execution, and combines immutable execution membership, pipeline-step and per-job stage projections, fresh worker capacity, approximate Temporal task-queue statistics, bounded active-work detail, and a conservative typed ETA. It is a current snapshot, not a historical reconstruction endpoint. See Operations & Events.

Discovery Controls

Source registry, quarantine, locator candidates, manual capture, schedules, and role-match feedback are covered in Profile & Settings.

POST /v1/jobs/import-url accepts { "url": "https://…" } and awaits the worker-owned JobUrlImportWorkflow. It returns either the canonical imported jobKey or a typed manual_capture_required outcome with the pending queue item and reason. The API never fetches the remote page itself.

Compensation

Posted compensation, market estimates, source controls, and refresh actions are job evidence. Start with Jobs & Materials.

Workflow Runs

List, detail, cancel, and workflow-start semantics are in Operations & Events.

GET /v1/workflow-runs accepts the normal pagination and sort parameters plus an exact workflowType match, inclusive startedSince, and exclusive startedBefore. Timestamp bounds must be UTC ISO-8601 timestamps; malformed optional filter values are ignored. The list response echoes the effective filters in its filter object (null when an optional filter is inactive).

GET /v1/workflow-runs/:runId returns failureDiagnostics when available. automaticRetryable describes Temporal retry behavior; manualRecoveryAvailable describes whether the canonical Tailor stage is failed/retryable or exhausted for a failed JobPreparation Tailor run, so a new recovery can be started. Its optional providerFailures summary is limited to safe provider/model/operation failure dimensions, counts, and the latest correlated attempt; it excludes all prompt, generated-content, SDK-message, path, URL, and secret data. For matching pre-instrumentation Codex records only, the exact legacy parse_error: "builder error" shape is returned with source: "legacy_inferred"; per-call retryability and structured provider fields remain unavailable.

Discover, job preparation, and Apply share the same statuses, lifecycle timeline, terminal-state rules, and cancellation command. Cancellation is cooperative and idempotent: repeated requests do not replace terminal results or emit duplicate cancellation transitions.

Profile Resume Preview

GET /v1/profile/preview.html and GET /v1/profile/preview.pdf render the baseline profile resume. Per-job generated artifacts use the artifact preview routes described in Jobs & Materials.

Apply Review And Outcomes

Apply review is a user decision boundary, not a background side effect. The review queue, editable resume-review drafts, approval decisions, outcomes, and bounded Gmail suggestion flow are summarized in Jobs & Materials.

The queue and job-detail read models include repeatApplication: its current status, summary, evaluation time, evidence fingerprint, related confirmed prior applications, matching reason and identity evidence, matching one-attempt override, and bounded audit trail. POST /v1/jobs/:jobKey/repeat-application/override accepts evidenceFingerprint, priorJobKey, a 10–400 character reason, and confirmedBy; it records intent but does not dispatch Apply. Stale evidence, prior mismatch, already-consumed confirmation, and live apply without required confirmation return 409 with stable repeat-application error codes. Dry-run dispatch does not require this confirmation because it cannot submit.

Contacts

Contact facts, supervised research, candidate confirmation, outreach drafts, send logs, and follow-ups share one route family. See Jobs & Materials.

Pipeline And Preparation Actions

Global and per-job stage runs, rescore/re-tailor, retry, mark-applied, and mark-skipped commands are summarized in Operations & Events.

Discovery reads the saved profile target, location, and work-model preferences. The precedence and filter details remain in the complete contract.

Worker Runtime And Health

Use GET /v1/health before starting work that needs Temporal and the Python worker. See Operations & Events for the readiness boundary.

Settings And Credentials

Ordinary settings and secrets deliberately use separate routes and storage. See Profile & Settings. GET /v1/providers/models returns sanitized ready-provider catalogs through the Python JSON-RPC boundary. PATCH /v1/settings accepts preferredModels, validates every non-null choice against that current catalog, and persists only the canonical preferred_models provider/model mapping; null clears one provider without requiring it to be ready.

GET/PATCH /v1/settings also carries effective-source and activation metadata for launch controls. Environment-owned fields are read-only. Discovery runtime and schedule controls use GET/PATCH /v1/discovery/settings; browser adoption uses GET /v1/browser-capabilities plus capability-specific POST enable, disable, and profile-copy routes. Standard default profiles can be selected by opaque detected-browser ID; write-only filesystem paths are an advanced fallback. Extension pairing uses GET /v1/extension/pairing-token and POST /v1/extension/pairing-token/rotate.

The contracts package owns shared API types, the API-client package owns typed browser calls, and apps/api owns HTTP/JSON-RPC/SSE transport. Source pointers are collected in Operations & Events.

Commands

Use corepack pnpm api:dev for the API, corepack pnpm web:dev for the web app, and corepack pnpm dev for the complete attached local stack. The full setup and verification sequence is in Local Development.

Server-Sent Events — GET /v1/events/stream

The endpoint tails tenant-scoped job_events, emits typed SSE frames, and uses event IDs for replay. The browser invalidation router maps each event type to the smallest safe query-key set. See Operations & Events and the frontend realtime architecture.

Request

The browser opens one long-lived GET request. A reconnect may include Last-Event-ID; the server resumes after that event when it is still available.

Response Framing

Frames include an id, typed event, JSON data, and a reconnect cadence. Clients validate the payload before dispatching it to the invalidation router.

Tenant Filtering (COALESCE On The Row, Not The Request)

Tenant compatibility is resolved against each stored row. The request tenant is never broadened to turn a tenant-scoped stream into an unscoped one.

Resume-Position Precedence

Resume and replay positions follow the explicit request/header contract before falling back to the current tail. See the complete contract for the exact order.

Cadences

The server polls for durable events and sends keepalive traffic on separate, bounded cadences so quiet connections remain observable.

Reconnect Backstop

Native EventSource reconnect plus durable event IDs is the normal recovery path. Projection refetches remain the correctness backstop if a client misses an invalidation.