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 —