CortexDB Docs
Docs for CortexDB 0.12.8, the version running on CortexDB Cloud (api.cortexdb.ai).
API Reference

/v1/admin

Operational endpoints — health, readiness, metrics, version, and maintenance jobs.

GET
/v1/admin/health

Response Body

curl -X GET "https://example.com/v1/admin/health"
Empty
GET
/v1/admin/ready

Query Parameters

deep?string

Make one real embedding call (bounded at 15 s) before answering, so setup.provider is ok or failing rather than untested. Billable on a hosted provider; leave it off for load-balancer polls.

Value in

  • "1"
  • "true"

Response Body

application/json

application/json

curl -X GET "https://example.com/v1/admin/ready"
{  "setup": {    "installed": true,    "licensed": "active",    "provider": "ok",    "application": "ok",    "summary": "string",    "provider_probe": {}  },  "provider_failing": true,  "ready": true,  "checks": {    "data_dir_writable": {      "ok": true,      "detail": "string"    },    "storage": {      "ok": true,      "detail": "string"    },    "disk": {      "ok": true,      "level": "ok",      "used_percent": 0,      "refusing_writes": true,      "detail": "string"    },    "answer": {      "ok": true,      "configured": true,      "stub": true,      "model": "string",      "detail": "string"    },    "commitments": {      "ok": true,      "mounted": true,      "reason": null,      "posture": null,      "expired": true,      "detail": "string"    }  },  "embedding_provider": "string",  "degraded": true}
GET
/v1/admin/metrics
AuthorizationBearer <token>

PASETO v4 public token (or deployment gate key). Auth-disabled dev instances accept any caller. On a cluster data node a PASETO/JWT bearer (not the static key) gets 503 REVOCATION_UNAVAILABLE (retriable) on any route while the node's revocation lease has expired: the metadata group has not confirmed its revocation set for 10 s, or the node has just started. A single-node server never returns it.

In: header

Response Body

application/json

curl -X GET "https://example.com/v1/admin/metrics"
{  "disk": {    "node": 0,    "enforced": true,    "level": "ok",    "since": "2019-08-24T14:15:22Z",    "sampled_at": "2019-08-24T14:15:22Z",    "total_bytes": 0,    "available_bytes": 0,    "used_percent": 0,    "warn_percent": 0,    "critical_percent": 0,    "warn_free_bytes": 0,    "critical_free_bytes": 0,    "check_interval_secs": 0,    "background_paused": true,    "refusing_writes": true,    "refusing_node": 0,    "refused_writes": 0,    "transitions": 0,    "last_error": "string",    "voters": [      {        "node": 0,        "level": "ok",        "available_bytes": 0,        "used_percent": 0,        "age_ms": 0,        "stale": true      }    ]  },  "memory": {    "enabled": true,    "limit_bytes": 0,    "limit_source": null,    "signal": null,    "used_bytes": 0,    "ratio": 0,    "rss_anon_bytes": 0,    "vm_rss_bytes": 0,    "cgroup_current_bytes": 0,    "cgroup_inactive_file_bytes": 0,    "psi_full_avg10": 0,    "allocator": "jemalloc",    "pressure": {      "state": "normal",      "since": "2019-08-24T14:15:22Z",      "high": 0,      "low": 0,      "times_entered": 0,      "refusals": {        "bulk_write": 0,        "import": 0,        "subtree_recall": 0,        "large_recall": 0,        "cohort_read_all": 0,        "busy_recall": 0      },      "vector_sheds": 0    },    "vector_residency": {      "cap": 0,      "source": "unset",      "fraction": 0,      "reserve_bytes": 0,      "bytes_per_vector": 0,      "quantization": "string",      "dims": 0,      "pressure_cap": 0    },    "rocksdb": {      "source": "auto",      "budget_bytes": 0,      "write_buffer_bytes": 0,      "capacity_bytes": 0,      "cache_bytes": 0,      "pinned_bytes": 0,      "memtable_bytes": 0,      "pressure": true    }  },  "ice": {    "url": "string",    "target_percent": 0,    "hot_days": 0,    "segment_mb": 0,    "read_deadline_ms": 0,    "read_max_mb": 0,    "read_max_requests": 0,    "cache": {      "disk_used_bytes": 0,      "disk_cap_bytes": 0,      "disk_allowed_bytes": 0,      "memory_used_bytes": 0,      "memory_cap_bytes": 0    },    "reads": {      "fetches": 0,      "fetch_errors": 0,      "fetched_bytes": 0,      "disk_hits": 0,      "memory_hits": 0,      "budget_skips": 0    },    "segments": {      "roots": 0,      "segments": 0,      "events": 0,      "chunks": 0,      "bytes": 0,      "deleted_marks": 0,      "scrub_marks": 0,      "error": "string"    },    "owner": {      "instance": "string",      "generation": 0,      "prefix": "string",      "state": "owner",      "reason": "string",      "record": {        "instance": "string",        "generation": 0,        "fingerprint": "string",        "written_at": "2019-08-24T14:15:22Z"      },      "last_error": "string"    },    "archiver": {      "passes": 0,      "last_pass_at": "2019-08-24T14:15:22Z",      "last_seal_at": "2019-08-24T14:15:22Z",      "last_error": "string",      "totals": {        "sealed_segments": 0,        "sealed_events": 0,        "sealed_missing": 0,        "sealed_unlearned": 0,        "uploaded_bytes": 0,        "rewritten": 0,        "dropped": 0,        "objects_deleted": 0,        "orphans_deleted": 0,        "journaled": 0,        "warmed_segments": 0,        "error": "string"      },      "disk": {        "used_percent": 0,        "used_bytes": 0,        "growth_bytes_per_hour": 0,        "hours_to_critical": 0      },      "leading": true,      "foreign_key_segments": 0    }  },  "answer_cache": {    "enabled": true,    "ttl_secs": 0,    "max_entries": 0,    "entries": 0,    "hits": 0,    "misses": 0,    "stores": 0,    "skipped_stores": 0,    "clears": 0  }}
GET
/v1/admin/version

Response Body

application/json

curl -X GET "https://example.com/v1/admin/version"
{  "version": "0.10.5",  "crate_version": "0.10.5",  "git_sha": "",  "built_at": "",  "capabilities": [    "blob_text_decode_v1",    "budget_excerpt_v1",    "capture_language_v1",    "erasure_blob_cascade_v1",    "erasure_early_answer_v1",    "event_scope_filter_v1",    "extra_scopes_v1",    "matched_fragments_v1",    "memory_kinds_v1",    "query_variants_v1",    "recall_diagnostics_v1",    "recall_language_v1",    "refers_to_v1",    "temporal_lenient_v1"  ]}

Health and readiness

curl https://api-v1.cortexdb.ai/v1/admin/health
# {"status":"healthy","version":"vX.Y.Z"}

Health is a minimal liveness check

GET /v1/admin/health returns only { status, version } (version has a v prefix) — no storage, no uptime_secs. It always returns 200 while the process is up, so orchestrators should probe readiness instead. Health and ready are unauthenticated.

GET /v1/admin/ready → an outcome-based readiness report:

{
  "ready": true,
  "degraded": true,
  "embedding_provider": "mock::1536",
  "checks": {
    "data_dir_writable": { "ok": true, "detail": "ok" },
    "storage":           { "ok": true, "detail": "wal_events=0, vectors=0" },
    "embeddings":        { "ok": false, "mock": true, "unproven": true,
                           "ok_count": 0, "fail_count": 0,
                           "last_success_ms": null, "last_failure_ms": null, "last_error": null,
                           "detail": "pinned to mock embeddings; recall quality is not representative" },
    "enrichment":        { "ok": true, "enabled": false, "mode": "off",
                           "detail": "enrichment OFF: facts, beliefs and concepts will stay empty" },
    "content_processors":{ "ok": false, "configured": [],
                           "detail": "NO content processors: uploaded blobs are stored but nothing is extracted" }
  }
}

Readiness reports what real provider calls actually did

The embeddings check is outcome-based, distinguishing three states: unproven (booted, nothing embedded yet — not degraded), failing (fail_count rising because the most recent real call failed — e.g. an expired/rate-limited key returning 401, with last_error carrying the provider's own text), and healthy. degraded is true for mock or failing embeddings, so a broken key surfaces on the wire instead of silently returning empty recall. enrichment reports ok: true when off (a valid choice), and content_processors flags that blob uploads won't be extracted when none are configured. Point container healthchecks and monitoring at ready, not health.

Ready is not the same as working

A 200 from ready means the process and its storage answer: it comes before the embedding provider has ever worked and before any write was indexed. From 0.12.2 the body says which stages are proven, and says at top level when embeddings fail:

{
  "ready": true,
  "provider_failing": false,
  "setup": {
    "installed": true,
    "licensed": "active",
    "provider": "untested",
    "application": "untested",
    "summary": "installed; the embedding provider is not proven yet (write once, or ask GET /v1/admin/ready?deep=1)"
  }
}

provider is ok, failing, untested or mock; application is ok once a write was indexed and a recall answered since start. ?deep=1 makes one real embedding call (at most 15 s) first, so provider is ok or failing; it costs a provider call, so leave it off for a load balancer. The HTTP status keeps its meaning, so existing health checks are unaffected. The installer on customer.cortexdb.ai finishes with a real write and recall, and a server reports provider and application with its license renewal, so your Overview shows them.

Metrics

The default metrics format is JSON, not Prometheus text

GET /v1/admin/metrics (no ?format) returns a JSON snapshot: { requests_total, requests_active, errors_total, not_configured_total, rate_limited_total, uptime_seconds, version, endpoints[…], errors_by_code, writes, indexer, storage{ breakdown }, vector_indexes, process, wal, unified_wal, retention, deferred_vectors, code, artifact, artifact_plane_enabled, erasure_index, erasure_residual, enrichment_multilingual, recall_empty_content_drops }. A 503 from a lane that isn't configured (for example ANSWER_UNAVAILABLE) counts in not_configured_total and errors_by_code, not in errors_total. The v1 docs' Prometheus cortex_* text counters are not emitted by default. (?format=json is also accepted.) vector_indexes confirms the HNSW quantization default is TQ2.

recall_empty_content_drops counts, since the process started, search hits that recall dropped because the content store had no text for them — nothing is ever served from such a hit: absent_during_erasure (a forget or erasure removed the content during or just before the recall; it climbs when recalls run beside forgets, and is not logged as a warning), absent_orphan (an index document with no content and no erasure behind it) and foreign_tenant (content that belongs to another tenant). Watch the last two.

Version and capabilities

GET /v1/admin/version → { version, crate_version, git_sha, built_at, capabilities }. capabilities is a sorted, append-only list of API-shape names; gate new request fields on it, never on the version string. A server that doesn't list a capability refuses that capability's fields with 422 INVALID_BODY; a server with no capabilities key supports none of them. The current release lists 11:

CapabilityWhat it adds
blob_text_decode_v1Charset-safe decoding of text blobs; undecodable text is 422 BLOB_TEXT_ENCODING_UNKNOWN
budget_excerpt_v1No recall context entry exceeds the budget; oversized events are served as excerpts
capture_language_v1language, scripts[] and context.timezone on captures
erasure_blob_cascade_v1Erasure also deletes the source blob; purge counts on /v1/forget
event_scope_filter_v1GET /v1/events/{id}?scope= returns the event only in exactly that scope (see Events)
matched_fragments_v1matched_fragments[] on recalled long events
query_variants_v1query_variants[] on recall
recall_diagnostics_v1Per-channel recall diagnostics with diagnostics: "full"
recall_language_v1language and scripts[] hints on recall; pack warnings[]
refers_to_v1context.refers_to[] on captures; temporal.refers_during on recall and answer
temporal_lenient_v1temporal.natural_mode and temporal.timezone on recall and answer

budget_excerpt_v1 is the one name an operator switch withdraws: with CORTEX_RECALL_BUDGET_EXCERPTS set to 0, off or false the list has ten names and no budget_excerpt_v1, because recall then renders the old way. Every other name is listed whatever the switches say.

Maintenance and stats

  • GET /v1/admin/layers/stats → a rich operator snapshot keyed by_scope with build counters, plus bitemporal, global, and a health block (enrichment_llm_usage, layer_sources, llm_roles, provider_health).
  • POST /v1/admin/index-audit { repair, vectors, scope } → 200 — a WAL↔index self-audit (all fields optional; repair: true, vectors: true refills missing vectors, the step used after an embedding model change; response includes missing_vectors, vectors_repaired, repaired_from_wal).
  • POST /v1/admin/scopes/migrate { from, to, mode, dry_run } → 202 { job_id, status, status_url } — the copy/move migration workflow.

Compaction and pruning

To compact the write-ahead log, use POST /v1/admin/wal/compact (below); to prune scopes, use POST /v1/scopes/prune (see Scopes). There is no /v1/admin/compact or /v1/admin/flush-views route.

Retention, WAL and usage routes

Each has its own page.

RoutePage
GET /v1/admin/retention · PUT/GET/DELETE /v1/admin/retention/policies · POST /v1/admin/retention/sweepRetention & WAL
GET /v1/admin/wal · POST /v1/admin/wal/compactRetention & WAL
GET /v1/admin/usageToken & Cost Ledger
POST /v1/admin/index-audit reports all four vector families and a vector_rebuild_pending recordEmbeddings

Chunk, blob and repair routes

RouteWhat it does
GET /v1/admin/events/{id}/chunksHow one event was chunked: { event_id, scope, content_kind, stored_text_bytes, chunker_version, chunk_count, chunks[], language, scripts, source_blob_ids }, each chunk with its byte_range, text and locator.section_path
GET /v1/admin/index-audit/orphan-blobsLive blobs no surviving event links or references: { live_blobs, referenced_blobs, orphans_total, orphans[], truncated }
POST /v1/admin/index-audit/source-blob-linksBackfills source_blob_ids on events that have none, for example events written before an upgrade (dry run by default): { applied, events_scanned, events_linked, links_added, unverifiable_total, … }
POST /v1/admin/derived/rederive · GET /v1/admin/derived/rederive/{job_id}Re-derive facts and beliefs from the event log (dry run by default)
POST /v1/admin/derived/resolver/unmergeSplit names the entity resolver merged (dry run by default)

Repairing derived data (enrichment-on stores only)

A store that ran with enrichment on under an earlier release can hold facts with a lost negation, different people merged into one entity, and beliefs counting facts of the opposite polarity (see Upgrading). rederive repairs what's stored, from the event log, without wiping or re-ingesting.

  • rederive takes a scope and targets (event_ids, fact_ids, supersession edges, or an audit report), plus optional actions (revert_supersessions, purge_derived, reenrich, reconsolidate, rebuild_beliefs; default all), max_events (default 200, at most 2,000) and job_id. The dry run returns the full plan with a cost estimate and changes nothing; an apply answers 202 with a job. An apply that purges also needs cold_backup_ack: true (else 422 COLD_BACKUP_ACK_REQUIRED); enrichment_wait_secs (default 600, at most 3,600) bounds the wait for re-extraction; reposting the same body with its job_id resumes a job; and continue_from takes the events a max_events cut left (once per job, else 409 JOB_ALREADY_CONTINUED).
  • scope is required; with no targets it answers 422 NO_TARGETS; on a server with enrichment off it answers 409 ENRICHMENT_OFF (nothing to re-derive with). unmerge also requires a scope and names or an audit report (422 NO_TARGETS). Reading a job that doesn't exist, or that another caller created, → 404 JOB_NOT_FOUND.
  • Take a cold backup before an apply. A dry run changes nothing; after an apply, rolling back to an earlier release means restoring that backup.
  • Ask CortexDB support for the step-by-step repair runbook and the read-only audit script before repairing a production store.

Native memory host

There is a second serving profile, the native memory host, started with --native-memory-host-dir instead of the default single-node router. Its routes are in the OpenAPI spec but are not served by the default router:

  • GET /v1/admin/native-memory → 404 on the default router (auth-free host status probe on the host).
  • POST /v1/memory/commitments/{commands,current,evidence,status} → 503 COMMITMENT_UNAVAILABLE ("native commitment request unavailable", retriable: true).
  • POST /v1/memory/source-erasure/{commands,discovered-commands,discovery,progress,refusals,status}, POST /v1/experience/resume, and the POST form of /v1/experience/status → 404.

These docs cover the default router only. The native host has no public setup guide yet; the shapes are in the pinned spec (commitment_* and source_erasure_* schemas).

On this page