/v1/admin
Operational endpoints — health, readiness, metrics, version, and maintenance jobs.
Response Body
application/json
curl -X GET "https://example.com/v1/admin/health"{ "status": "healthy", "version": "string"}Query Parameters
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. true is read in any letter case; any other value makes no call.
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" }, "enrichment": { "ok": true, "enabled": true, "mode": "string", "detail": "string" }, "content_processors": { "ok": true, "configured": [ "string" ], "detail": "string" }, "embeddings": { "ok": true, "detail": "string", "mock": true, "unproven": true, "ok_count": 0, "fail_count": 0, "last_success_ms": 0, "last_failure_ms": 0, "last_error": "string" } }, "embedding_provider": "string", "degraded": true, "native_memory": "current"}Authorization
bearer Authorization: Bearer . Four credentials authenticate: (1) the operator key, the static CORTEX_API_KEY: the trusted operator, acting as the principal X-Cortex-Actor names (default user:local), in billing tenant default; (2) a PASETO v4 public token this server's minter signed (POST /v1/auth/tokens, POST /v1/auth/signup, the mint_offline tool), of audience CORTEX_V1_AUDIENCE or, with CORTEX_V1_TENANT_AUDIENCES=1, a billing tenant's own cortexdb:tenant:; (3) since 0.14.0 (principal_chain_v1), a JWT (RS256 or ES256) or PASETO v4 public token of an issuers-file entry (CORTEX_AUTH_ISSUERS_FILE; [auth] issuers_file on a cluster data node), whose subject, audience and token id (:) the entry maps; (4) a commitment transport token, on /v1/memory/commitments/* only. HS256, none and every symmetric JWT algorithm are refused. With auth disabled (no operator key: CORTEX_API_KEY unset on a loopback bind, or CORTEX_INSECURE_NO_AUTH) no token is verified and every request is served as the trusted operator, as the X-Cortex-Actor it names or user:local. On the operator key, with auth disabled and in the unsigned development mode, an X-Cortex-Actor that is not a principal id is 400 INVALID_HEADER, details {part: header, field: X-Cortex-Actor, reason: the parse error} (since 0.14.0; before, 401 INVALID_BODY). X-Cortex-Actor must equal the token's sub; a token of an issuers-file entry in prefix, hash or directory mode and a call of /mcp may leave it out (when present it must match). Any other token sent without it is verified first: a bearer of no known token form (a mistyped operator key) is 401 MISSING_TOKEN, any other bad token gets its own refusal, as with the header, and only a token that verifies answers 401 ACTOR_MISMATCH, the header is required. Every authenticated response carries X-Cortex-Token-Expires-In and X-Cortex-Token-Expires-At (and a Warning header within 72 h of expiry); every error of the auth layer carries X-Cortex-Request-ID.
Authentication refusals, the same on every authenticated route, none retriable unless marked. A token that carries none of the principal-chain claims and was not signed by an issuers-file entry gets each code with no details, as in 0.13.x; an entry's token, and a token that carries a chain claim other than minter, gets details.issuer (the entry's id, minter, commitment or unknown) and the details named here. 401 MISSING_TOKEN: no Authorization: Bearer header, or a bearer that is neither the operator key nor a PASETO v4 public or JWT token (a wrong operator key), with or without X-Cortex-Actor. 401 INVALID_TOKEN: a PASETO token no registered key verifies, a token signed by a key other than the issuer its iss names included. 401 INVALID_TOKEN_SIGNATURE: a bad signature, an iss no issuer is registered under, a JWT algorithm other than RS256 or ES256, a malformed token, or, on a token without chain claims, an iat ahead of the clock; for an issuers-file entry's token details.reason issuer_disabled, token_type, critical_header, algorithm, kid_revoked or unknown_kid (none for a bad signature). 401 EXPIRED_TOKEN. 401 REVOKED_TOKEN: the token's id is revoked (an entry's token goes by :). 401 WRONG_TENANT: an aud the token's issuer may not name, or one that names no tenant. 401 INVALID_TOKEN_CLAIM (since 0.14.0), details.claim and details.reason: a principal-chain claim malformed (malformed, not_an_agent, same_as_sub, not_a_service, person_with_agent, run_start_without_run, after_iat, run_too_old, out_of_range; unknown_critical_claim, with details.critical); an issuers-file entry's token without exp, iat or jti (missing), with iat or nbf ahead of the clock (ahead), or living longer than the entry's max_lifetime_s (claim exp, lifetime_over_maximum, details.lifetime_s, details.ceiling_s); a token this server's minter signed whose exp - iat is above the ceiling for its subject, 604,800 s for a user: or agent:, 2,592,000 s for a service: or system: (claim exp, lifetime_over_ceiling, details.issuer minter, details.lifetime_s, details.ceiling_s; checked before the revocation, and counted instead while CORTEX_MINT_LIFETIME_ENFORCE=off); and, whatever issuer signed it, a token whose act names an AI employee of its tenant, the self of an employee record that is not delegable (claim act, act_not_delegable). 401 CLAIM_NOT_PERMITTED (since 0.14.0), details.claim: a chain claim the token's issuer is not bound to assert (an issuers-file entry's [issuer.claims]; minter from any issuer but this server's minter; the minter's own chain claims without the chain assertion), or an issuers-file entry's scopes entry outside its token's tenant root org: (claim scopes, details.entry). 401 SUBJECT_UNMAPPED (since 0.14.0), details.reason: an issuers-file entry's subject mapping produced no principal (missing, not_a_principal, kind, service, foreign_namespace, personal_data_in_id, malformed, too_long, not_in_directory, directory_not_here); and, on every route whatever issuer signed the credential, a principal whose entry in its tenant's directory (org:) is deactivated (deactivated; never the trusted operator). 410 TENANT_DELETED (since 0.14.0): the credential's tenant root, org:, is being deleted or was (POST /v1/erasures/tenant); never the trusted operator. 503 REVOCATION_UNAVAILABLE (retriable): the revocation list cannot answer, so every token is refused as an outage, never 401 REVOKED_TOKEN, and the list still fails closed: on a cluster data node 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), and on a single node once the list's journal failed (a refused write latches the list). 503 ISSUER_KEYS_UNAVAILABLE (retriable): an issuers-file entry's JWKS cannot be fetched and no pinned or cached key verifies the token; Retry-After is the whole seconds until the key cache's next fetch (its backoff, 3 s doubling to 60 s; 3 s before the first fetch), at least 1, also in details.retry_after_seconds. 503 WAL_UNAVAILABLE (retriable): the directory did not read, so a directory-mode subject's principal, or a principal's deactivation, is unknown.
Authorization refusals any route gives where it checks a capability or a roster, none retriable. 403 POLICY_DENIED, details.capability and details.denied_by_tier (deployment, tenant, actor or scope): the deployment's or the tenant's policy denies the capability, the token's caps leave it out, a scopes entry names it but not at this scope, or the scope's roster does not admit the caller. Since 0.14.0 a token that carries scopes holds only the capabilities an entry names (: its subtree, ! that one scope, :* every scope and the checks made without one); any other is refused with details.link confinement, and counted instead while CORTEX_TOKEN_CONFINEMENT_ENFORCE=off. An agent chain token (one that carries act or run, or app or client resolving to agent) is confined so whatever the switch, and without scopes holds no capability. A tenant-audience token's :* entry at a scope outside its own org: root is details.link tenant. A token another principal minted (minter a principal other than its sub) also needs that minter to hold the capability under the deployment's and the tenant's policy and the role on the scope's roster: details.link minter, denied_by_tier scope, counted instead while CORTEX_MINTER_LINK_ENFORCE=off. A read whose credential stops being current while it runs (expired or revoked, or its issuers-file entry disabled or reloaded without its audience) is refused 403 POLICY_DENIED before it delivers. 403 APP_NOT_ADMITTED (since 0.14.0): in a tenant whose root sets apps_enforced, a chain whose application (app, or unlisted) is not admitted at the scope. 403 RUN_REQUIRED (since 0.14.0), details.scope: on the write routes (POST /v1/experience, each item of /v1/experience/bulk and /v1/experiences, each JSONL import line, POST /v1/beliefs), a token with act and no run.
The operator key under CORTEX_OPERATOR_DATA_READS (since 0.14.0, operator_break_glass_v1): allow, audit or deny; unset, deny on the cloud_shared_saas preset and allow on the other presets and with auth disabled; a tenant's own mode (PUT /v1/admin/access/tenant-settings) overrides it in that tenant. 403 BREAK_GLASS_REQUIRED, details {mode, class, route}: under deny, without a well-formed X-Cortex-Break-Glass, an operator-key action of class stored_rows (a route that returns stored rows, or a forget or erasure preview whose selector names about_subject, about_entity or predicate), admin_content (a content-returning admin route), private_region (a read whose scope or extra_scopes lie in a private region) or in_band (a mint of a token that carries a data capability, can mint, carries a registry-write capability or is for an Owner of a private region; a registration, an auto-provisioning first write included; a roster or tree edit that widens a private region or adds a service: principal; a write into a private region or a shared org-tree scope; a migration out of a private region; lowering a tenant's mode from deny; a deployment minter's mint of a token that carries a data capability). 400 INVALID_HEADER, in every mode: that header malformed, sent twice or longer than 512 bytes, details {part: header, field: X-Cortex-Break-Glass, reason: what is wrong with it}. Both refusals write the request's audit row as decision deny, each with its denial (check operator_access, path request#x-cortex-break-glass, decided_by_tier request; reason the 403's class, or break_glass_invalid for the 400, which names no scope), within the operator key's bound (its billing tenant and X-Cortex-Actor) (AuditRow). Its form: reason=incident|legal|support|security|migration|restore|other; ticket=1 to 64 characters of [A-Za-z0-9._:-]; optionally note=percent-encoded UTF-8 of at most 256 bytes; no other key, separated by semicolons. It is read only on the operator key's requests and on a deployment minter's POST /v1/auth/tokens, and ignored on every other credential; a cross-origin browser page cannot send it. 503 NOTICE_UNAVAILABLE: under deny, a header-bearing read of a private region whose notice to the region's person did not commit, so nothing was delivered: details.reason moving, limit, members_behind (Retry-After: 30) or refused (details.refusal), each retriable; arming_disabled and reserved_path_in_use, not retriable. A by-id read's notice is its row's region's, committed once the row is read (since 0.14.0; scope_unnamed is gone).
Before authentication, a build that enforces licenses answers every data route 402 LICENSE_EXPIRED, or 402 LICENSE_REQUIRED when it never held a license, not retriable; health, version, ready, /metrics, the published contract (the OpenAPI document, GET /v1/schemas and /v1/schemas/{name}), the console, every GET /v1/admin/* route, GET /v1/auth/whoami, POST /v1/export and, since 0.14.0, deleting data (POST /v1/forget and its preview, POST /v1/erasures and its preview, /v1/erasures/person, /v1/erasures/tenant, /v1/erasures/{id}/cancel, and GET /v1/erasures/{id}, /verify and /receipt) pass. Not an authentication refusal, and answered only where a write needs a newer shard apply format than the node's shard is armed for (change sets, application records, directory and SCIM writes, a person's erasure and a tenant's deletion, writes that carry atoms or provenance, rehomes and watermarks, a declared belief with provenance): 503 FORMAT_UPGRADE_PENDING, details {reason, format, node, paths, count} (reason, format and node alone on the directory, SCIM, rehome and watermark writes; reason alone on an application record or a reference root), retriable with Retry-After: 30 for members_behind only (arming_disabled and reserved_path_in_use wait for the operator). A plain node arms for format 10 at its first boot of 0.14.0 under the default ceiling, one way, and then never answers it.
In: header
Header Parameters
operator_break_glass_v1 (API reference §8.15.3): read only on a request the operator key (CORTEX_API_KEY) authenticates, and on a deployment minter's POST /v1/auth/tokens; ignored on every other credential. reason=<incident|legal|support|security|migration|restore|other>; ticket=<1 to 64 of [A-Za-z0-9._:-]>[; note=<percent-encoded UTF-8, at most 256 bytes>], exactly one header of at most 512 bytes; any other form is 400 INVALID_HEADER in every mode (details {part: header, field: X-Cortex-Break-Glass, reason}), and the request's audit row a deny. Under CORTEX_OPERATOR_DATA_READS=deny (the deployment's, or the tenant's own) an operator-key request that reads stored rows, a content-returning admin route, a private region's read, or an in-band path needs it, else 403 BREAK_GLASS_REQUIRED (details {mode, class, route}). A well-formed header is written to the audit row as {reason, ticket}, never the note.
length <= 512subject_token_shadow_v1: on a request the operator key authenticates, the token of the principal the operator acts for, verified by the same verifier (without waiting on the network and without the X-Cortex-Actor step) and evaluated in shadow by the access report (GET /v1/admin/access/report). It never changes the response: a repeated, oversized (over 8,192 bytes) or invalid header, or the operator key itself, is recorded as the shadow's invalid_token and the request runs as sent.
length <= 8192Response Body
application/json
application/json
application/json
curl -X GET "https://example.com/v1/admin/metrics"{ "requests_total": 0, "requests_active": 0, "errors_total": 0, "rate_limited_total": 0, "not_configured_total": 0, "uptime_seconds": 0, "version": "string", "endpoints": [ { "route": "string", "count": 0, "errors": 0, "p50_ms": 0, "p95_ms": 0, "p99_ms": 0, "max_ms": 0 } ], "errors_by_code": [ { "error_code": "string", "count": 0 } ], "writes": { "indexing_failures_total": 0 }, "enrichment_multilingual": {}, "indexer": {}, "answer_cache": { "enabled": true, "ttl_secs": 0, "max_entries": 0, "entries": 0, "hits": 0, "misses": 0, "stores": 0, "skipped_stores": 0, "clears": 0 }, "retention": { "ticks": 0, "scanned": 0, "expired": 0, "erased": 0, "detached": 0, "held": 0, "errors": 0, "last_tick_unix": 0 }, "unified_wal": { "live_count": 0, "next_offset": 0, "head_offset": 0, "captured_head_offset": 0, "partitions": 0, "consumer_offsets": { "property1": 0, "property2": 0 }, "retired_marker_count": 0, "native_profile_blocked": true }, "erasure_residual": {}, "erasure_index": {}, "recall_empty_content_drops": { "absent_during_erasure": 0, "absent_orphan": 0, "foreign_tenant": 0 }, "storage": { "data_dir_bytes_approx": 0, "breakdown": [ { "name": "string", "bytes": 0, "kind": "dir" } ] }, "vector_indexes": [ { "name": "string", "quantization": "string", "hot_count": 0, "cold_count": 0, "memory_bytes": 0 } ], "vectors": { "embed_inputs": 0, "proposed_entries": 0, "proposed_bytes": 0, "kept_slots": 0, "rejected_inputs": 0, "erase_order_wait_seconds": 0, "vectors_erased": 0, "unresolved_locators": 0 }, "artifact_plane_enabled": true, "artifact": {}, "process": { "rss_bytes": 0 }, "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 } }, "access_view": { "keeps": true, "leader": true, "bytes": 0, "max_bytes": 0, "root_max_bytes": 0, "roots_kept": 0, "roots_building": 0, "roots_over_cap": 0, "evictions": 0 }, "wal": { "events": 0 }, "code": {}, "auth": { "enforce": { "property1": "on", "property2": "on" }, "deployment_minters": [ "string" ], "issuers_file": true, "auth_issuers_sha256": "string", "reloaded_at": "2019-08-24T14:15:22Z", "issuers": [ { "id": "string", "iss": "string", "format": "jwt", "keys": 0, "pinned_keys": 0, "fetched_at": "2019-08-24T14:15:22Z", "persisted_at": "2019-08-24T14:15:22Z", "last_error": "string", "stale": true, "disabled": true, "revoked_kids": [ "string" ] } ] }, "edge": {}, "role": "ingress", "responses": [ { "shard": "string", "code": "string", "count": 0 } ], "resends": 0, "reroutes": 0, "leader_redirects": 0, "replica_rotations": 0}Response Body
application/json
curl -X GET "https://example.com/v1/admin/version"{ "version": "0.14.0", "crate_version": "0.14.0", "git_sha": "", "built_at": "", "capabilities": [ "access_changesets_v1", "access_check_v1", "access_limits_v1", "access_lookup_v1", "access_notices_v1", "access_report_v1", "access_watch_v1", "app_records_v1", "authzen_v1", "beliefs_merged_v1", "blob_text_decode_v1", "budget_excerpt_v1", "capture_language_v1", "confinement_wildcard_v1", "consistency_tokens_v1", "directory_v1", "enrich_backfill_v1", "erasure_blob_cascade_v1", "erasure_early_answer_v1", "event_scope_filter_v1", "extra_scopes_40_v1", "extra_scopes_v1", "forget_by_key_v1", "matched_fragments_v1", "mcp_http_v1", "memory_kinds_v1", "mint_chain_v1", "operator_break_glass_v1", "org_tree_v1", "party_erasure_v1", "person_erasure_v1", "principal_chain_v1", "publish_v1", "query_variants_v1", "read_purpose_v1", "recall_coverage_v1", "recall_diagnostics_v1", "recall_language_v1", "recall_mounts_v1", "refers_to_v1", "rollups_v1", "scim_v1", "source_containers_v1", "subject_token_shadow_v1", "temporal_lenient_v1", "tenant_access_settings_v1", "tenant_erasure_v1", "write_provenance_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:
| Capability | What it adds |
|---|---|
blob_text_decode_v1 | Charset-safe decoding of text blobs; undecodable text is 422 BLOB_TEXT_ENCODING_UNKNOWN |
budget_excerpt_v1 | No recall context entry exceeds the budget; oversized events are served as excerpts |
capture_language_v1 | language, scripts[] and context.timezone on captures |
erasure_blob_cascade_v1 | Erasure also deletes the source blob; purge counts on /v1/forget |
event_scope_filter_v1 | GET /v1/events/{id}?scope= returns the event only in exactly that scope (see Events) |
matched_fragments_v1 | matched_fragments[] on recalled long events |
query_variants_v1 | query_variants[] on recall |
recall_diagnostics_v1 | Per-channel recall diagnostics with diagnostics: "full" |
recall_language_v1 | language and scripts[] hints on recall; pack warnings[] |
refers_to_v1 | context.refers_to[] on captures; temporal.refers_during on recall and answer |
temporal_lenient_v1 | temporal.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 keyedby_scopewith build counters, plusbitemporal,global, and ahealthblock (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: truerefills missing vectors, the step used after an embedding model change; response includesmissing_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.
| Route | Page |
|---|---|
GET /v1/admin/retention · PUT/GET/DELETE /v1/admin/retention/policies · POST /v1/admin/retention/sweep | Retention & WAL |
GET /v1/admin/wal · POST /v1/admin/wal/compact | Retention & WAL |
GET /v1/admin/usage | Token & Cost Ledger |
POST /v1/admin/index-audit reports all four vector families and a vector_rebuild_pending record | Embeddings |
Chunk, blob and repair routes
| Route | What it does |
|---|---|
GET /v1/admin/events/{id}/chunks | How 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-blobs | Live blobs no surviving event links or references: { live_blobs, referenced_blobs, orphans_total, orphans[], truncated } |
POST /v1/admin/index-audit/source-blob-links | Backfills 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/unmerge | Split 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.
rederivetakes ascopeand targets (event_ids,fact_ids, supersessionedges, or an audit report), plus optionalactions(revert_supersessions,purge_derived,reenrich,reconsolidate,rebuild_beliefs; default all),max_events(default 200, at most 2,000) andjob_id. The dry run returns the full plan with a cost estimate and changes nothing; an apply answers202with a job. An apply that purges also needscold_backup_ack: true(else422 COLD_BACKUP_ACK_REQUIRED);enrichment_wait_secs(default 600, at most 3,600) bounds the wait for re-extraction; reposting the same body with itsjob_idresumes a job; andcontinue_fromtakes the events amax_eventscut left (once per job, else409 JOB_ALREADY_CONTINUED).scopeis required; with no targets it answers422 NO_TARGETS; on a server with enrichment off it answers409 ENRICHMENT_OFF(nothing to re-derive with).unmergealso requires ascopeand 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→404on 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 thePOSTform 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).
/v1/plan and /v1/usage
An account's plan, limits and usage on CortexDB Cloud (GET /v1/plan, operator plan routes), and per-scope storage and activity on any server (GET /v1/usage).
Error codes
The error body, how to tell a retriable error, and every error code the v1 API returns, with the page that explains each.