Skip to content

HTTP API

This reference is generated at build time from FastAPI's OpenAPI document. Its endpoint inventory, parameters, request and response types, validation constraints, status codes, and component schemas come directly from route metadata, docstrings, and type hints.

Bearer authentication is required unless an operation is marked public. Write-scoped routes are also marked 🔒. See Authentication for token configuration and scope semantics. The server binds to 127.0.0.1:8000 by default.

The ingest workflow also has a task-oriented contract in the Upstream ingest API; that guide adds retry, idempotency, and integration advice around the generated wire reference below.

Endpoint map

POST   /v1/pages                            Ingest a page  🔒
POST   /v1/pages/batch                      Ingest up to 100 pages  🔒
POST   /v1/pages/status/batch               Fetch status for up to 500 pages
GET    /v1/pages/{page_id}                  Fetch a page
GET    /v1/pages/{page_id}/chunks           List a page's indexed chunks
GET    /v1/pages/{page_id}/status           Page indexing status
GET    /v1/jobs                             List jobs
POST   /v1/jobs/retry                       Retry dead jobs in bulk  🔒
POST   /v1/jobs/{job_id}/retry              Retry a dead job  🔒
POST   /v1/search                           Search the reading history
POST   /v1/search/batch                     Search many queries in one call
GET    /v1/pages/{page_id}/similar          Pages similar to a page
POST   /v1/feedback                         Record relevance feedback
POST   /v1/forget                           Purge and blacklist a URL or domain  🔒
GET    /v1/blacklist                        List blacklist rules
DELETE /v1/blacklist/{blacklist_id}         Remove a blacklist rule  🔒
GET    /v1/clusters                         List reading-topic clusters
GET    /v1/clusters/runs                    List clustering runs
GET    /v1/clusters/projection              Read a run's two-dimensional page projection
GET    /v1/clusters/{cluster_id}            A cluster's label, keywords, and member pages
POST   /v1/clusters/recompute               Trigger a cluster run  🔒
GET    /v1/entities/{ref}                   Look up an entity
GET    /v1/pages/{page_id}/entities         Entities mentioned on a page
POST   /v1/entities/merges/{merge_id}/undo  Undo an entity merge (LIFO only)  🔒
GET    /v1/models/{model_id}/backfill       Read model backfill progress
POST   /v1/models/{model_id}/backfill       Estimate or start a backfill  🔒
GET    /v1/models                           List embedding models
POST   /v1/models                           Register an embedding model  🔒
POST   /v1/models/{model_id}/activate       Make this the active search model  🔒
DELETE /v1/models/{model_id}                Retire a model and drop its vector space  🔒
POST   /v1/compare                          Compare embedding models on one query
GET    /v1/watches                          List watches
POST   /v1/watches                          Create a watch  🔒
GET    /v1/watches/{watch_id}               Get a watch
PATCH  /v1/watches/{watch_id}               Update a watch  🔒
DELETE /v1/watches/{watch_id}               Delete a watch  🔒
POST   /v1/watches/{watch_id}/run           Poll a watch now  🔒
GET    /v1/events                           Stream job status changes (SSE)
GET    /v1/admin/metrics/timeseries         Read metric history
GET    /v1/admin/metrics/summary            Typed operational canaries
GET    /v1/admin/query-log                  List query-log runs
GET    /v1/admin/query-log/{query_id}       Read one query-log run
POST   /v1/admin/eval/score                 Score logged runs
POST   /v1/admin/eval/replay                Enqueue a live eval replay  🔒
GET    /v1/admin/eval/replay/{job_id}       Poll a live eval replay
GET    /v1/admin/config                     Read effective configuration
GET    /v1/admin/mcp                        Inspect mounted MCP tools
GET    /v1/whoami                           Identify caller
GET    /metrics                             Read Prometheus metrics
GET    /healthz                             Check process liveness
GET    /readyz                              Check service readiness

Endpoint details

Pages

POST /v1/pages — Ingest a page 🔒

Add a page the user read to the index. body_extracted and body_html are mutually exclusive; when neither is given the URL is fetched asynchronously. Known canonical URLs record a revisit instead. Operation ID: add_page · Required scope: write

Request body

Required.

Media type Schema
application/json IngestPageRequest
Responses
Status Description Body
200 Successful Response application/json: IngestRevisitResponse
202 Accepted application/json: IngestAcceptedResponse
403 Forbidden application/json: BlacklistedResponse
422 Validation Error application/json: HTTPValidationError

POST /v1/pages/batch — Ingest up to 100 pages 🔒

Validate and ingest each page independently. Results preserve input order; malformed or blacklisted items do not prevent other items. Operation ID: add_pages_batch · Required scope: write

Request body

Required.

Media type Schema
application/json IngestBatchRequest
Responses
Status Description Body
200 Successful Response application/json: IngestBatchResponse
422 Validation Error application/json: HTTPValidationError

POST /v1/pages/status/batch — Fetch status for up to 500 pages

Return one status per distinct requested page id. Unknown ids are reported with found=false instead of failing the batch. Operation ID: page_status_batch · Required scope: read

Request body

Required.

Media type Schema
application/json PageStatusBatchRequest
Responses
Status Description Body
200 Successful Response application/json: PageStatusBatchResponse
422 Validation Error application/json: HTTPValidationError

GET /v1/pages/{page_id} — Fetch a page

Return the full stored body text and metadata of one page from the user's reading history. Operation ID: get_page · Required scope: read

Parameters
Name In Type Required Description and constraints
page_id path string yes
Responses
Status Description Body
200 Successful Response application/json: PageResponse
422 Validation Error application/json: HTTPValidationError

GET /v1/pages/{page_id}/chunks — List a page's indexed chunks

Return chunk text and body offsets in ordinal order. Operation ID: get_page_chunks · Required scope: read

Parameters
Name In Type Required Description and constraints
page_id path string yes
Responses
Status Description Body
200 Successful Response application/json: PageChunksResponse
422 Validation Error application/json: HTTPValidationError

GET /v1/pages/{page_id}/status — Page indexing status

Return the indexing lifecycle status of a page. Operation ID: page_status · Required scope: read

Parameters
Name In Type Required Description and constraints
page_id path string yes
Responses
Status Description Body
200 Successful Response application/json: PageStatusResponse
422 Validation Error application/json: HTTPValidationError

Jobs

GET /v1/jobs — List jobs

List jobs newest first, optionally filtered by status and kind.

status_filter is a deprecated compatibility alias for status; a request that supplies both is rejected. Operation ID: list_jobs · Required scope: read

Parameters
Name In Type Required Description and constraints
status query JobStatus | null no
status_filter query JobStatus | null no Deprecated
kind query JobKind | null no
limit query integer no Default: 100; Minimum: 1; Maximum: 1000
Responses
Status Description Body
200 Successful Response application/json: JobListResponse
422 Validation Error application/json: HTTPValidationError

POST /v1/jobs/retry — Retry dead jobs in bulk 🔒

Retry dead jobs either by explicit job_ids (up to 500, deduped, results in input order) or by selector (all dead jobs, optionally filtered by kind, up to limit). Non-dead jobs are reported as skipped and unknown ids as not_found; the call is idempotent — a second selector call finds nothing left to retry. Always returns 200. Operation ID: retry_jobs_batch · Required scope: write

Request body

Required.

Media type Schema
application/json JobRetryBatchRequest
Responses
Status Description Body
200 Successful Response application/json: JobRetryBatchResponse
422 Validation Error application/json: HTTPValidationError

POST /v1/jobs/{job_id}/retry — Retry a dead job 🔒

Reset a dead job to pending and re-enqueue it; other states return 409.

Retrying an index job also resets its page from dead back to queued, so page status tracks the re-enqueued work. Operation ID: retry_job · Required scope: write

Parameters
Name In Type Required Description and constraints
job_id path string yes
Responses
Status Description Body
200 Successful Response application/json: JobResponse
422 Validation Error application/json: HTTPValidationError

POST /v1/search — Search the reading history

Hybrid semantic + keyword search over pages the user has read. Returns grounded passages from the user's own reading history. Contains no information the user has not read. Returns an empty result when nothing matches. An entity filter that resolves to no known entity returns 404 so a bad reference is distinguishable from a genuine zero. Operation ID: search · Required scope: read

Request body

Required.

Media type Schema
application/json SearchRequest
Responses
Status Description Body
200 Successful Response application/json: SearchResponse
422 Validation Error application/json: HTTPValidationError

POST /v1/search/batch — Search many queries in one call

Run up to 20 queries sequentially with shared parameters (sequential on purpose: it respects embedding provider rate limits, like model comparison does). Each item gets its own query_id and query-log row, so feedback and evaluation join per query. Per-item failures are reported inline as outcome='error' items; the call itself stays 200. Operation ID: search_batch · Required scope: read

Request body

Required.

Media type Schema
application/json SearchBatchRequest
Responses
Status Description Body
200 Successful Response application/json: SearchBatchResponse
422 Validation Error application/json: HTTPValidationError

GET /v1/pages/{page_id}/similar — Pages similar to a page

Rank other pages from the user's reading history by similarity to this one. Returns grounded results from the user's own reading history only. Operation ID: similar_to · Required scope: read

Parameters
Name In Type Required Description and constraints
page_id path string yes
mediation query Mediation no Default: "vector"
k query integer no Default: 10; Minimum: 1; Maximum: 50
Responses
Status Description Body
200 Successful Response application/json: SimilarResponse
422 Validation Error application/json: HTTPValidationError

POST /v1/feedback — Record relevance feedback

Append feedback; unknown query_ids are accepted (eval join drops them). Operation ID: record_feedback · Required scope: read

Request body

Required.

Media type Schema
application/json FeedbackRequest
Responses
Status Description Body
202 Successful Response application/json: object[string, string]
422 Validation Error application/json: HTTPValidationError

Forget

POST /v1/forget — Purge and blacklist a URL or domain 🔒

Permanently remove matching pages from the index and blacklist the pattern so future ingests are rejected. This deletes user data; removal of the blacklist rule later does not restore content. Operation ID: forget · Required scope: write

Request body

Required.

Media type Schema
application/json ForgetRequest
Responses
Status Description Body
200 Successful Response application/json: ForgetResponse
422 Validation Error application/json: HTTPValidationError

GET /v1/blacklist — List blacklist rules

All rules, newest first. Operation ID: list_blacklist · Required scope: read

Responses
Status Description Body
200 Successful Response application/json: BlacklistResponse

DELETE /v1/blacklist/{blacklist_id} — Remove a blacklist rule 🔒

Future ingests are allowed again; purged content stays purged. Operation ID: unblacklist · Required scope: write

Parameters
Name In Type Required Description and constraints
blacklist_id path string yes
Responses
Status Description Body
204 Successful Response
422 Validation Error application/json: HTTPValidationError

Clusters

GET /v1/clusters — List reading-topic clusters

Topic clusters discovered over the user's reading history. Returns grounded results derived only from pages the user has read. Operation ID: list_clusters · Required scope: read

Parameters
Name In Type Required Description and constraints
include_tombstoned query boolean no Default: false
Responses
Status Description Body
200 Successful Response application/json: ClusterListResponse
422 Validation Error application/json: HTTPValidationError

GET /v1/clusters/runs — List clustering runs

Return persisted clustering history newest first. Operation ID: list_cluster_runs · Required scope: read

Parameters
Name In Type Required Description and constraints
limit query integer no Default: 100; Minimum: 1; Maximum: 1000
Responses
Status Description Body
200 Successful Response application/json: ClusterRunsResponse
422 Validation Error application/json: HTTPValidationError

GET /v1/clusters/projection — Read a run's two-dimensional page projection

Return page coordinates for a persisted clustering run. Operation ID: cluster_projection · Required scope: read

Parameters
Name In Type Required Description and constraints
run_id query string yes Minimum Length: 1
Responses
Status Description Body
200 Successful Response application/json: ClusterProjectionResponse
422 Validation Error application/json: HTTPValidationError

GET /v1/clusters/{cluster_id} — A cluster's label, keywords, and member pages

Member pages of one topic cluster from the user's reading history. Tombstoned clusters remain resolvable by id. Operation ID: cluster_pages · Required scope: read

Parameters
Name In Type Required Description and constraints
cluster_id path string yes
Responses
Status Description Body
200 Successful Response application/json: ClusterDetailResponse
422 Validation Error application/json: HTTPValidationError

POST /v1/clusters/recompute — Trigger a cluster run 🔒

Enqueue a manual run; 409 when one is already in flight. Operation ID: recompute_clusters · Required scope: write

Responses
Status Description Body
202 Successful Response application/json: RecomputeResponse

Entities

GET /v1/entities/{ref} — Look up an entity

Resolve an entity by id, canonical form, or alias (references survive merges). Returns aliases and the user's pages mentioning it — grounded in the reading history only. Operation ID: entities · Required scope: read

Parameters
Name In Type Required Description and constraints
ref path string yes
Responses
Status Description Body
200 Successful Response application/json: EntityDetailResponse
422 Validation Error application/json: HTTPValidationError

GET /v1/pages/{page_id}/entities — Entities mentioned on a page

Entities linked to one page. Operation ID: page_entities · Required scope: read

Parameters
Name In Type Required Description and constraints
page_id path string yes
Responses
Status Description Body
200 Successful Response application/json: PageEntitiesResponse
422 Validation Error application/json: HTTPValidationError

POST /v1/entities/merges/{merge_id}/undo — Undo an entity merge (LIFO only) 🔒

Restore a merged entity from its snapshot. Operation ID: undo_entity_merge · Required scope: write

Parameters
Name In Type Required Description and constraints
merge_id path string yes
Responses
Status Description Body
200 Successful Response application/json: UndoMergeResponse
422 Validation Error application/json: HTTPValidationError

Models

GET /v1/models/{model_id}/backfill — Read model backfill progress

Return durable progress for a model's corpus backfill. Operation ID: get_model_backfill · Required scope: read

Parameters
Name In Type Required Description and constraints
model_id path string yes
Responses
Status Description Body
200 Successful Response application/json: ModelBackfillResponse
422 Validation Error application/json: HTTPValidationError

POST /v1/models/{model_id}/backfill — Estimate or start a backfill 🔒

confirm=false returns an exact dry-run estimate (chunk token counts are stored); confirm=true starts the durable, resumable backfill. Operation ID: backfill_model · Required scope: write

Parameters
Name In Type Required Description and constraints
model_id path string yes
Request body

Required.

Media type Schema
application/json BackfillRequest
Responses
Status Description Body
200 Successful Response application/json: BackfillEstimateResponse
422 Validation Error application/json: HTTPValidationError

GET /v1/models — List embedding models

All registered models. Operation ID: list_models_api · Required scope: read

Responses
Status Description Body
200 Successful Response application/json: ModelListResponse

POST /v1/models — Register an embedding model 🔒

Register (not active, no vectors yet). Rejected below the chunk budget. Operation ID: register_model · Required scope: write

Request body

Required.

Media type Schema
application/json RegisterModelRequest
Responses
Status Description Body
201 Successful Response application/json: ModelInfo
422 Validation Error application/json: HTTPValidationError

POST /v1/models/{model_id}/activate — Make this the active search model 🔒

Atomic flip; only ready models can be activated. Operation ID: activate_model · Required scope: write

Parameters
Name In Type Required Description and constraints
model_id path string yes
Responses
Status Description Body
200 Successful Response application/json: ModelInfo
422 Validation Error application/json: HTTPValidationError

DELETE /v1/models/{model_id} — Retire a model and drop its vector space 🔒

409 when active; drops the vector space and page vectors. Operation ID: retire_model · Required scope: write

Parameters
Name In Type Required Description and constraints
model_id path string yes
Responses
Status Description Body
204 Successful Response
422 Validation Error application/json: HTTPValidationError

Compare

POST /v1/compare — Compare embedding models on one query

Runs the full retrieval pipeline once per model; the sparse arm and reranker are identical across arms, so the delta isolates the embedder. Returns per-model rankings over the user's reading history plus agreement statistics (Jaccard@k, RBO, Kendall's tau). Operation ID: compare · Required scope: read

Request body

Required.

Media type Schema
application/json CompareRequest
Responses
Status Description Body
200 Successful Response application/json: CompareResponse
422 Validation Error application/json: HTTPValidationError

Watches

GET /v1/watches — List watches

All watches with their schedules and last-poll health (status, error, item count). Operation ID: list_watches · Required scope: read

Responses
Status Description Body
200 Successful Response application/json: WatchListResponse

POST /v1/watches — Create a watch 🔒

Register a URL to poll on a schedule (default every 24 hours). Each poll discovers the source's current item URLs and ingests the new ones, so they become searchable. Kind 'rss' polls an RSS/Atom feed; kind 'youtube' polls a YouTube playlist or channel and ingests each video's transcript (single videos go to add_page instead); kind 'podcast' polls a podcast RSS feed and ingests each episode's audio enclosure as a local Whisper transcript (requires a transcription extra). Already-indexed and blacklisted items are skipped automatically. The first poll runs within a minute of creation. Operation ID: create_watch · Required scope: write

Request body

Required.

Media type Schema
application/json CreateWatchRequest
Responses
Status Description Body
201 Successful Response application/json: WatchResponse
422 Validation Error application/json: HTTPValidationError

GET /v1/watches/{watch_id} — Get a watch

One watch with its schedule and last-poll health. Operation ID: get_watch · Required scope: read

Parameters
Name In Type Required Description and constraints
watch_id path string yes
Responses
Status Description Body
200 Successful Response application/json: WatchResponse
422 Validation Error application/json: HTTPValidationError

PATCH /v1/watches/{watch_id} — Update a watch 🔒

Partially update a watch: pause/resume with 'enabled', change the poll interval, title, or per-kind config. Omitted fields are left unchanged; the URL is immutable (delete and recreate instead). Changing the interval reschedules the next poll from the last run. Operation ID: update_watch · Required scope: write

Parameters
Name In Type Required Description and constraints
watch_id path string yes
Request body

Required.

Media type Schema
application/json UpdateWatchRequest
Responses
Status Description Body
200 Successful Response application/json: WatchResponse
422 Validation Error application/json: HTTPValidationError

DELETE /v1/watches/{watch_id} — Delete a watch 🔒

Stop polling this source. Pages already ingested by the watch stay indexed; use forget to remove content. Operation ID: delete_watch · Required scope: write

Parameters
Name In Type Required Description and constraints
watch_id path string yes
Responses
Status Description Body
204 Successful Response
422 Validation Error application/json: HTTPValidationError

POST /v1/watches/{watch_id}/run — Poll a watch now 🔒

Enqueue an immediate poll instead of waiting for the schedule; the next scheduled poll moves to one interval from now. Returns the poll job id; track it via the jobs API. Operation ID: run_watch · Required scope: write

Parameters
Name In Type Required Description and constraints
watch_id path string yes
Responses
Status Description Body
202 Successful Response application/json: WatchRunAcceptedResponse
422 Validation Error application/json: HTTPValidationError

Events

GET /v1/events — Stream job status changes (SSE)

A text/event-stream of job ledger transitions. Opens with a snapshot event carrying the current job list (same shape as GET /v1/jobs), then one job event per status change (pending, running, done, failed, dead), with : keep-alive comments between events. Returns 503 when the subscriber limit is reached. Authenticate with the normal bearer header (fetch-based SSE); query-string tokens are not supported. Operation ID: stream_events · Required scope: read

Responses
Status Description Body
200 Successful Response application/json: object

Admin

GET /v1/admin/metrics/timeseries — Read metric history

Read historical rollups plus current gauge values for one metric. Operation ID: admin_metrics_timeseries · Required scope: read

Parameters
Name In Type Required Description and constraints
metric query string yes Minimum Length: 1
since query string (date-time) | null no
step query number no Default: 60.0; Exclusive Minimum: 0
Responses
Status Description Body
200 Successful Response application/json: MetricsTimeseriesResponse
422 Validation Error application/json: HTTPValidationError

GET /v1/admin/metrics/summary — Typed operational canaries

One JSON snapshot of operational health: job-ledger counts, vector tombstone backlog, dropped observability rows, provider error counters, search latency quantiles, and circuit breaker states. Counter-derived fields are process-lifetime totals that reset on restart; since bounds the latency window only. Operation ID: admin_metrics_summary · Required scope: read

Parameters
Name In Type Required Description and constraints
since query string (date-time) | null no
Responses
Status Description Body
200 Successful Response application/json: MetricsSummaryResponse
422 Validation Error application/json: HTTPValidationError

GET /v1/admin/query-log — List query-log runs

List retrieval traces newest first, optionally filtered by time and kind.

Each trace includes candidate, dense, sparse, and final hit sets, per-stage timing, and the latest relevance feedback. The default limit is 100 and the maximum is 1,000. Operation ID: admin_query_log · Required scope: read

Parameters
Name In Type Required Description and constraints
since query string (date-time) | null no
limit query integer no Default: 100; Minimum: 1; Maximum: 1000
kind query "search" | "compare_arm" | null no
Responses
Status Description Body
200 Successful Response application/json: QueryLogListResponse
422 Validation Error application/json: HTTPValidationError

GET /v1/admin/query-log/{query_id} — Read one query-log run

Return one full retrieval trace with rankings, timing, and feedback. Operation ID: admin_query_log_detail · Required scope: read

Parameters
Name In Type Required Description and constraints
query_id path string yes
Responses
Status Description Body
200 Successful Response application/json: QueryLogRunResponse
422 Validation Error application/json: HTTPValidationError

POST /v1/admin/eval/score — Score logged runs

Score logged rankings synchronously without calling external providers. Operation ID: admin_eval_score · Required scope: read

Request body

Required.

Media type Schema
application/json EvalScoreRequest
Responses
Status Description Body
200 Successful Response application/json: ScoreReport
422 Validation Error application/json: HTTPValidationError

POST /v1/admin/eval/replay — Enqueue a live eval replay 🔒

Enqueue a durable two-arm replay that may call paid model providers.

The response is a 202 with a job ID and result URL. Polling the result needs only read scope; the report or failure survives process restarts. Operation ID: admin_eval_replay · Required scope: write

Request body

Required.

Media type Schema
application/json EvalReplayRequest
Responses
Status Description Body
202 Successful Response application/json: EvalReplayAcceptedResponse
422 Validation Error application/json: HTTPValidationError

GET /v1/admin/eval/replay/{job_id} — Poll a live eval replay

Read durable replay status, its completed report, or terminal failure. Operation ID: admin_eval_replay_result · Required scope: read

Parameters
Name In Type Required Description and constraints
job_id path string yes
Responses
Status Description Body
200 Successful Response application/json: EvalReplayResultResponse
422 Validation Error application/json: HTTPValidationError

GET /v1/admin/config — Read effective configuration

Return effective settings with secrets redacted recursively.

Every setting is marked boot_only because runtime mutation is not supported. Operation ID: admin_config · Required scope: read

Responses
Status Description Body
200 Successful Response application/json: AdminConfigResponse

GET /v1/admin/mcp — Inspect mounted MCP tools

Return actual mounted tool metadata and mutating-tool visibility. Operation ID: admin_mcp · Required scope: read

Responses
Status Description Body
200 Successful Response application/json: McpAdminResponse

Auth

GET /v1/whoami — Identify caller

Return the authenticated token name and its effective scopes. Operation ID: whoami · Required scope: read

Responses
Status Description Body
200 Successful Response application/json: WhoAmIResponse

Health

GET /metrics — Read Prometheus metrics

Return the Prometheus text exposition for authenticated scrapers. Operation ID: metrics · Required scope: read

Responses
Status Description Body
200 Prometheus text exposition. text/plain: string

GET /healthz — Check process liveness

Return success when the API process is running. No authentication. Operation ID: healthz · Required scope: public

Responses
Status Description Body
200 Successful Response application/json: object[string, string]

GET /readyz — Check service readiness

Check that the metadata store is reachable and an embedding model is active. Returns 503 until both conditions hold. No authentication. Operation ID: readyz · Required scope: public

Responses
Status Description Body
200 Successful Response application/json: object
503 A required dependency is unavailable.

Schemas

AdminConfigResponse

Field Type Required Description and constraints
settings object yes
mutability object[string, "boot_only"] yes

BackfillEstimateResponse

Dry-run estimate (confirm=false).

Field Type Required Description and constraints
model_id string yes
n_chunks integer yes
total_tokens integer yes
est_cost_usd number | null yes
est_duration_s number | null yes
confirm_required true no Default: true

BackfillRequest

POST /v1/models/{id}/backfill body.

Field Type Required Description and constraints
confirm boolean no Default: false

BackfillStartedResponse

202-style acknowledgement (confirm=true).

Field Type Required Description and constraints
model_id string yes
status "backfilling" no Default: "backfilling"

BlacklistEntry

One blacklist rule.

Field Type Required Description and constraints
id string yes
pattern string yes
kind BlacklistKind yes
reason string | null yes
created_at string (date-time) yes

BlacklistKind

Whether a blacklist rule matches a URL exactly or a domain suffix.

Type: "url" | "domain"

BlacklistResponse

GET /v1/blacklist response.

Field Type Required Description and constraints
entries array<BlacklistEntry> yes

BlacklistedResponse

403: URL matches a blacklist rule.

Field Type Required Description and constraints
error "blacklisted" no Default: "blacklisted"
pattern string yes

BreakerStateSummary

Field Type Required Description and constraints
name string yes
state "closed" | "half_open" | "open" yes

ChunkResult

One matched chunk, whole text.

Field Type Required Description and constraints
chunk_id string yes
ordinal integer yes
text string yes
score number yes

ClusterDetailResponse

GET /v1/clusters/{id} response.

Field Type Required Description and constraints
cluster ClusterSummary yes
pages array<ClusterMemberPage> yes

ClusterListResponse

GET /v1/clusters response.

Field Type Required Description and constraints
clusters array<ClusterSummary> yes

ClusterMemberPage

A page inside a cluster.

Field Type Required Description and constraints
page_id string yes
canonical_url string yes
title string | null yes
probability number | null yes

ClusterProjectionPointResponse

A page point in a clustering run's display projection.

Field Type Required Description and constraints
page_id string yes
x number yes
y number yes
cluster_id string | null yes

ClusterProjectionResponse

GET /v1/clusters/projection.

Field Type Required Description and constraints
run_id string yes
points array<ClusterProjectionPointResponse> yes

ClusterRef

Cluster membership shown on a result (populated from M4).

Field Type Required Description and constraints
id string yes
label string | null yes

ClusterRunResponse

One persisted clustering run.

Field Type Required Description and constraints
id string yes
trigger string yes
algorithm string yes
params object[string, JsonValue] yes
started_at string (date-time) yes
finished_at string (date-time) | null yes
duration_ms integer | null yes
n_pages integer | null yes
n_clusters integer | null yes
n_noise integer | null yes

ClusterRunsResponse

GET /v1/clusters/runs.

Field Type Required Description and constraints
runs array<ClusterRunResponse> yes

ClusterSummary

One cluster.

Field Type Required Description and constraints
id string yes
label string | null yes
keywords array<string> yes
size integer yes
tombstoned boolean no Default: false
projection_x number | null no
projection_y number | null no

CompareAgreement

Agreement stats for one model pair.

Field Type Required Description and constraints
model_a string yes
model_b string yes
jaccard_at_k number yes
rbo number yes
kendall_tau number | null yes
intersection_size integer yes

CompareModelRun

One model's ranking.

Field Type Required Description and constraints
model string yes
results array<ComparedPage> yes

CompareRequest

POST /v1/compare body.

Field Type Required Description and constraints
query string yes Minimum Length: 1; Maximum Length: 4000
models array<string> yes Minimum Items: 2; Maximum Items: 5
k integer no Default: 10; Minimum: 1.0; Maximum: 100.0
candidates integer no Default: 100; Minimum: 1.0; Maximum: 1000.0
rerank boolean no Default: true

CompareResponse

POST /v1/compare response.

Field Type Required Description and constraints
compare_id string yes
query string yes
runs array<CompareModelRun> yes
agreement array<CompareAgreement> yes

ComparedPage

One ranked page in a compare arm.

Field Type Required Description and constraints
page_id string yes
canonical_url string yes
title string | null yes
score number yes

CreateWatchRequest

POST /v1/watches body.

Field Type Required Description and constraints
url string yes
kind WatchKind no Default: "rss"
title string | null no
interval_hours integer | null no
enabled boolean no Default: true
config object[string, string] | null no

EntityDetailResponse

GET /v1/entities/{ref} response.

Field Type Required Description and constraints
entity EntitySummary yes
aliases array<string> yes
page_ids array<string> yes

EntitySummary

One canonical entity.

Field Type Required Description and constraints
id string yes
canonical_form string yes
type string yes
mention_count integer yes
page_count integer yes
idf number | null yes

EvalReplayAcceptedResponse

Field Type Required Description and constraints
job_id string yes
result_url string yes

EvalReplayRequest

Field Type Required Description and constraints
model_a string | null no
model_b string | null no
rerank_a boolean no Default: true
rerank_b boolean no Default: true
k integer no Default: 10; Minimum: 1.0; Maximum: 1000.0
candidates integer no Default: 100; Minimum: 1.0; Maximum: 10000.0
limit integer | null no

EvalReplayResultResponse

Field Type Required Description and constraints
job_id string yes
status JobStatus yes
report object | null no
error string | null no

EvalScoreRequest

Field Type Required Description and constraints
k integer no Default: 10; Minimum: 1.0; Maximum: 1000.0
since string (date-time) | null no
model string | null no

FeatureStatus

Status of an asynchronous enrichment feature for a page.

Field Type Required Description and constraints
status JobStatus | "not_queued" | null no
last_error string | null no

FeedbackRequest

POST /v1/feedback body.

Field Type Required Description and constraints
query_id string yes
page_id string yes
relevant boolean yes

ForgetRequest

POST /v1/forget body: exactly one of url/domain.

Field Type Required Description and constraints
url string | null no
domain string | null no
reason string | null no

ForgetResponse

POST /v1/forget response.

Field Type Required Description and constraints
blacklist_id string yes
pattern string yes
kind BlacklistKind yes
pages_purged integer yes
vector_deletes_queued integer yes

HTTPValidationError

Field Type Required Description and constraints
detail array<ValidationError> no

IngestAcceptedResponse

202: new page queued.

Field Type Required Description and constraints
page_id string yes
status "queued" no Default: "queued"

IngestBatchAcceptedResult

A newly queued item in an ingest batch.

Field Type Required Description and constraints
page_id string yes
status "queued" no Default: "queued"
index integer yes
outcome "accepted" no Default: "accepted"

IngestBatchBlacklistedResult

A blacklisted item in an ingest batch.

Field Type Required Description and constraints
error "blacklisted" no Default: "blacklisted"
pattern string yes
index integer yes
outcome "blacklisted" no Default: "blacklisted"

IngestBatchRejectedResult

An independently invalid item in an ingest batch.

Field Type Required Description and constraints
index integer yes
outcome "rejected" no Default: "rejected"
detail string yes

IngestBatchRequest

POST /v1/pages/batch envelope.

Items remain unvalidated here so one malformed page does not reject the otherwise valid envelope. The route validates each item independently.

Field Type Required Description and constraints
pages array<object> yes Minimum Items: 1; Maximum Items: 100

IngestBatchResponse

POST /v1/pages/batch response in input order.

Field Type Required Description and constraints
results array<IngestBatchAcceptedResult | IngestBatchRevisitResult | IngestBatchBlacklistedResult | IngestBatchRejectedResult> yes

IngestBatchRevisitResult

A revisit item in an ingest batch.

Field Type Required Description and constraints
page_id string yes
status PageStatus yes
revisit true no Default: true
content_hash_differs boolean no Default: false
index integer yes
outcome "revisit" no Default: "revisit"

IngestPageRequest

POST /v1/pages body.

Field Type Required Description and constraints
url string yes
title string | null no
body_extracted string | null no
body_html string | null no
fetched_at string (date-time) | null no
source string | null no
metadata object[string, JsonValue] | null no

IngestRevisitResponse

200: canonical URL already known.

Field Type Required Description and constraints
page_id string yes
status PageStatus yes
revisit true no Default: true
content_hash_differs boolean no Default: false

JobKind

Kinds of durable jobs the queue executes.

Type: "index_page" | "fetch_and_index" | "extract_entities" | "backfill_model" | "cluster" | "label_clusters" | "canonicalize_entities" | "purge_vectors" | "eval_replay" | "poll_watch"

JobListResponse

GET /v1/jobs.

Field Type Required Description and constraints
jobs array<JobResponse> yes

JobResponse

Job ledger row (admin surface).

Field Type Required Description and constraints
job_id string yes
kind string yes
status JobStatus yes
attempts integer yes
max_attempts integer yes
last_error string | null yes
created_at string (date-time) yes
updated_at string (date-time) yes

JobRetryBatchMissingResult

An unknown job id.

Field Type Required Description and constraints
job_id string yes
outcome "not_found" no Default: "not_found"

JobRetryBatchRequest

POST /v1/jobs/retry body: explicit ids or a dead-job selector.

Field Type Required Description and constraints
status "dead" no Default: "dead"
job_ids array<string> | null no
kind JobKind | null no
limit integer no Default: 100; Minimum: 1.0; Maximum: 500.0

JobRetryBatchResponse

POST /v1/jobs/retry response in input/selection order.

Field Type Required Description and constraints
requested integer yes
retried integer yes
results array<JobRetryBatchRetriedResult | JobRetryBatchSkippedResult | JobRetryBatchMissingResult> yes

JobRetryBatchRetriedResult

A job that was reset to pending and re-enqueued.

Field Type Required Description and constraints
job_id string yes
kind string yes
status JobStatus yes
attempts integer yes
max_attempts integer yes
last_error string | null yes
created_at string (date-time) yes
updated_at string (date-time) yes
outcome "retried" no Default: "retried"

JobRetryBatchSkippedResult

A job skipped because it is not dead.

Field Type Required Description and constraints
job_id string yes
status JobStatus yes
outcome "skipped" no Default: "skipped"
detail string yes

JobStatus

Ledger status of a durable job.

Type: "pending" | "running" | "done" | "failed" | "dead"

JobsSummary

Field Type Required Description and constraints
by_status object[string, integer] yes
queue_depth integer yes
dead integer yes
failures_by_kind object[string, integer] yes

JsonValue

Type: object

LoggedHitResponse

Field Type Required Description and constraints
chunk_id string yes
page_id string yes
score number yes

LoggedPageResponse

Field Type Required Description and constraints
page_id string yes
score number yes
rank integer yes

McpAdminResponse

Field Type Required Description and constraints
tools array<McpToolResponse> yes
enable_mutating_tools boolean yes

McpToolResponse

Field Type Required Description and constraints
name string yes
description string | null no
inputSchema object no

Mediation

What mediates 'similar': vectors, cluster co-membership, or entities.

Type: "vector" | "cluster" | "entity"

MetricPointResponse

Field Type Required Description and constraints
ts string (date-time) yes
value number yes

MetricSeriesResponse

Field Type Required Description and constraints
sample string yes
labels object[string, string] yes
metric_type string yes
points array<MetricPointResponse> yes

MetricsSummaryResponse

Field Type Required Description and constraints
generated_at string (date-time) yes
tombstones TombstoneBacklogSummary yes
jobs JobsSummary yes
query_log_dropped_rows integer yes
embedding_errors_by_provider object[string, integer] yes
rerank_degraded_total integer yes
search_latency SearchLatencySummary | null yes
breakers array<BreakerStateSummary> yes

MetricsTimeseriesResponse

Field Type Required Description and constraints
metric string yes
series array<MetricSeriesResponse> yes
current array<MetricSeriesResponse> yes

ModelBackfillResponse

GET /v1/models/{id}/backfill.

Field Type Required Description and constraints
model_id string yes
status "not_started" | "running" | "complete" | "failed" yes
total_chunks integer yes
embedded_chunks integer yes
total_tokens integer yes
cursor_page_id string | null yes
started_at string (date-time) | null yes
updated_at string (date-time) | null yes
finished_at string (date-time) | null yes
last_error string | null yes

ModelInfo

One registered embedding model.

Field Type Required Description and constraints
id string yes
provider string yes
model_name string yes
dim integer yes
max_input_tokens integer yes
is_active boolean yes
status string yes

ModelListResponse

GET /v1/models response.

Field Type Required Description and constraints
models array<ModelInfo> yes

ModelReport

Mean metrics over one model's scored runs.

Field Type Required Description and constraints
model string yes
queries integer yes
ndcg number yes
reciprocal_rank number yes
recall number yes
recall_candidates number yes
rerank_lift number | null yes

PageChunkResponse

One stored chunk with its position in the page body.

Field Type Required Description and constraints
chunk_id string yes
ordinal integer yes
text string yes
token_count integer yes
char_start integer yes
char_end integer yes
section_title string | null no
section_start_s number | null no

PageChunksResponse

GET /v1/pages/{id}/chunks.

Field Type Required Description and constraints
page_id string yes
chunks array<PageChunkResponse> yes

PageEntitiesResponse

GET /v1/pages/{id}/entities response.

Field Type Required Description and constraints
page_id string yes
entities array<EntitySummary> yes

PageResponse

GET /v1/pages/{id}.

Field Type Required Description and constraints
page_id string yes
canonical_url string yes
original_url string yes
domain string yes
title string | null yes
body_text string | null yes
source string | null yes
metadata object[string, JsonValue] | null yes
first_seen_at string (date-time) yes
last_seen_at string (date-time) yes
visit_count integer yes
indexed_at string (date-time) | null yes
status PageStatus yes

PageResult

One ranked page.

Field Type Required Description and constraints
page_id string yes
canonical_url string yes
title string | null yes
domain string yes
first_seen_at string (date-time) yes
visit_count integer yes
score number yes
cluster ClusterRef | null no
chunks array<ChunkResult> yes
exact_match boolean no Default: false

PageStatus

Lifecycle of a page through the indexing pipeline.

Type: "queued" | "indexing" | "indexed" | "failed" | "dead"

PageStatusBatchFoundResult

Status for a page known to the server.

Field Type Required Description and constraints
page_id string yes
status PageStatus yes
last_error string | null no
features PageStatusFeatures | null no
found true no Default: true

PageStatusBatchMissingResult

Status result for an unknown or expired page id.

Field Type Required Description and constraints
page_id string yes
found false no Default: false

PageStatusBatchRequest

POST /v1/pages/status/batch envelope.

Field Type Required Description and constraints
page_ids array<string> yes Minimum Items: 1; Maximum Items: 500

PageStatusBatchResponse

POST /v1/pages/status/batch response.

Field Type Required Description and constraints
results array<PageStatusBatchFoundResult | PageStatusBatchMissingResult> yes

PageStatusFeatures

Nested enrichment feature state.

Field Type Required Description and constraints
entities FeatureStatus | null no

PageStatusResponse

GET /v1/pages/{id}/status.

Field Type Required Description and constraints
page_id string yes
status PageStatus yes
last_error string | null no
features PageStatusFeatures | null no

QueryLogListResponse

Field Type Required Description and constraints
runs array<QueryLogRunResponse> yes

QueryLogRunResponse

Field Type Required Description and constraints
query_id string yes
ts string (date-time) yes
kind string yes
compare_id string | null yes
query_text string yes
params object yes
active_model string yes
reranker_model string | null yes
candidate_set array<LoggedHitResponse> yes
dense_hits array<LoggedHitResponse> yes
sparse_hits array<LoggedHitResponse> yes
final_pages array<LoggedPageResponse> yes
timing_ms object[string, number] yes
feedback object[string, boolean] yes

QueryScore

Metrics for one logged run.

Field Type Required Description and constraints
query_id string yes
query_text string yes
model string yes
ndcg number yes
reciprocal_rank number yes
recall number yes
recall_candidates number yes
rerank_lift number | null yes

RecomputeResponse

POST /v1/clusters/recompute response.

Field Type Required Description and constraints
accepted boolean yes
detail string | null no

RegisterModelRequest

POST /v1/models body.

Field Type Required Description and constraints
provider string yes
model_name string yes
dim integer yes Minimum: 1.0; Maximum: 8192.0
max_input_tokens integer yes Minimum: 1.0
id string | null no

RollupStrategy

How chunk scores pool into a page score.

No magic-number bonuses in v1: rerankers are calibrated, and arbitrary boosts would destroy their ranking. A bonus variant may be added later only if eval proves lift.

Type: "max" | "mean_top_m" | "sum_rrf"

ScoreReport

Everything eval score emits.

Field Type Required Description and constraints
k integer yes
logged integer yes
labeled integer yes
scored integer yes
skipped_no_positive integer yes
models array<ModelReport> yes
queries array<QueryScore> yes

SearchBatchErrorResult

One batch query that failed with a per-item (non-fatal) error.

Field Type Required Description and constraints
index integer yes
query string yes
outcome "error" no Default: "error"
error "feature_unavailable" | "no_active_model" yes
detail string yes

SearchBatchOkResult

One successful batch query with its full search response.

Field Type Required Description and constraints
query_id string yes
results array<PageResult> yes
offset integer yes
has_more boolean yes
suggestions array<Suggestion> yes
timing_ms object[string, number] yes
index integer yes
query string yes
outcome "ok" no Default: "ok"

SearchBatchRequest

POST /v1/search/batch body: shared params + up to 20 query strings.

Field Type Required Description and constraints
k integer no Default: 10; Minimum: 1.0; Maximum: 100.0
offset integer no Default: 0; Minimum: 0.0
candidates integer no Default: 100; Minimum: 1.0; Maximum: 1000.0
rerank boolean no Default: true
chunks_per_page integer no Default: 2; Minimum: 0.0; Maximum: 10.0
rollup RollupStrategy no Default: "max"
rollup_m integer no Default: 3; Minimum: 1.0; Maximum: 20.0
rrf_k integer no Default: 60; Minimum: 1.0
suggest integer no Default: 3; Minimum: 0.0; Maximum: 10.0
mediation Mediation no Default: "vector"
recency_half_life_days number | null no
filters SearchFiltersBody | null no
queries array<string> yes Minimum Items: 1; Maximum Items: 20

SearchBatchResponse

POST /v1/search/batch response in input order.

Field Type Required Description and constraints
results array<SearchBatchOkResult | SearchBatchErrorResult> yes

SearchFiltersBody

First-class search filters.

Field Type Required Description and constraints
domain string | null no
after string (date-time) | null no
before string (date-time) | null no
cluster_id string | null no
entity string | null no

SearchLatencySummary

Field Type Required Description and constraints
since string (date-time) | null yes
runs integer yes
p50_ms number yes
p95_ms number yes

SearchRequest

POST /v1/search body.

Field Type Required Description and constraints
k integer no Default: 10; Minimum: 1.0; Maximum: 100.0
offset integer no Default: 0; Minimum: 0.0
candidates integer no Default: 100; Minimum: 1.0; Maximum: 1000.0
rerank boolean no Default: true
chunks_per_page integer no Default: 2; Minimum: 0.0; Maximum: 10.0
rollup RollupStrategy no Default: "max"
rollup_m integer no Default: 3; Minimum: 1.0; Maximum: 20.0
rrf_k integer no Default: 60; Minimum: 1.0
suggest integer no Default: 3; Minimum: 0.0; Maximum: 10.0
mediation Mediation no Default: "vector"
recency_half_life_days number | null no
filters SearchFiltersBody | null no
query string yes Minimum Length: 1; Maximum Length: 4000

SearchResponse

POST /v1/search response.

Field Type Required Description and constraints
query_id string yes
results array<PageResult> yes
offset integer yes
has_more boolean yes
suggestions array<Suggestion> yes
timing_ms object[string, number] yes

SimilarResponse

GET /v1/pages/{id}/similar response.

Field Type Required Description and constraints
page_id string yes
mediation Mediation yes
results array<SimilarResult> yes

SimilarResult

One similar page.

Field Type Required Description and constraints
page_id string yes
canonical_url string yes
title string | null yes
score number yes
reason string yes

Suggestion

A related page appended to the response.

Field Type Required Description and constraints
page_id string yes
title string | null yes
reason string yes

TombstoneBacklogSummary

Field Type Required Description and constraints
pending integer yes
deleted integer yes
verified integer yes

UndoMergeResponse

POST /v1/entities/merges/{id}/undo response.

Field Type Required Description and constraints
restored_entity_id string yes

UpdateWatchRequest

PATCH /v1/watches/{watch_id} body; omitted fields stay unchanged.

url is immutable: it is the watch's dedup identity — delete and recreate to point a watch elsewhere.

Field Type Required Description and constraints
enabled boolean | null no
interval_hours integer | null no
title string | null no
config object[string, string] | null no

ValidationError

Field Type Required Description and constraints
loc array<string | integer> yes
msg string yes
type string yes
input object no
ctx object no

WatchKind

Kinds of pull sources a watch can poll.

Type: "rss" | "youtube" | "podcast"

WatchListResponse

GET /v1/watches response.

Field Type Required Description and constraints
watches array<WatchResponse> yes

WatchResponse

One watch with its schedule and last-poll health.

Field Type Required Description and constraints
id string yes
kind WatchKind yes
url string yes
title string | null yes
enabled boolean yes
interval_hours integer yes
config object[string, string] | null yes
next_run_at string (date-time) yes
last_run_at string (date-time) | null yes
last_status WatchStatus yes
last_error string | null yes
last_item_count integer | null yes
created_at string (date-time) yes

WatchRunAcceptedResponse

POST /v1/watches/{watch_id}/run response (202).

Field Type Required Description and constraints
watch_id string yes
job_id string | null yes

WatchStatus

Outcome of a watch's most recent poll.

Type: "pending" | "ok" | "error"

WhoAmIResponse

Authenticated token identity and effective scopes.

Field Type Required Description and constraints
name string yes
scopes array<string> yes