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

POST /v1/answer

Recall a pack and answer a natural-language question over it, with citations.

POST
/v1/answer
AuthorizationBearer <token>

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 as this server runs it (CORTEX_MINT_MAX_LIFETIME_PERSON, _AGENT and _SERVICE since 0.14.1; by default 7,776,000 s for a user:, 604,800 s for an 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

Cache-Control?string

Answer cache (opt-in, CORTEX_ANSWER_CACHE=1; ignored while it is off). Comma-separated directives, matched case-insensitively: no-cache answers now and replaces the stored answer; no-store neither reads nor writes the cache. Other directives are ignored. See API_REFERENCE_V1.md §8.4.1.

X-Cortex-Break-Glass?string

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.

Lengthlength <= 512
X-Cortex-Subject-Token?string

subject_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.

Lengthlength <= 8192

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Unknown fields are ignored: the fields listed here are the ones the route reads (query_variants, language, scripts, diagnostics, citation_mode, exclude_content and subtree among the ones it does not).

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/v1/answer" \  -H "Content-Type: application/json" \  -d '{    "scope": "string",    "question": "string"  }'
{  "pack_id": "string",  "answer": "string",  "citations": [    {      "marker": "string",      "layer": "event",      "id": "string",      "support_strength": 0,      "scope": "string",      "subject": "string"    }  ],  "provenance": {},  "diagnostics": {    "recall_ms": 0,    "llm_ms": 0,    "total_ms": 0,    "answer_model": "string",    "answer_llm_skipped_by_request": true,    "tokens_input": 0,    "tokens_output": 0,    "time_ms": {      "property1": 0,      "property2": 0    },    "inferred_question_shape": "string",    "question_shape_applied": true,    "artifact_route": "string",    "artifact_hits": 0,    "raw_focus_route": "string",    "raw_focus_hits": 0,    "reducer_refusal": "string",    "coverage_certificate": {},    "abstain_retry_used": true,    "abstain_retry_outcome": "string",    "degraded_sources": [      "string"    ],    "premise_unsupported_terms": [      "string"    ],    "extractive_retry_outcome": "answered",    "cost_usd": 0,    "answer_cache": {      "hit": true,      "age_ms": 0    }  },  "as_of": "2019-08-24T14:15:22Z",  "context_block": "string",  "cohort": {    "version": 1,    "status": "complete",    "lane": "exact",    "plan": {      "version": 1,      "op": "list",      "where": {        "intent": [          "string"        ],        "intent_prefix": [          "string"        ],        "labels_all": [          "string"        ],        "labels_any": [          "string"        ],        "modality": [          "string"        ],        "observed": {          "from": "2019-08-24T14:15:22Z",          "to": "2019-08-24T14:15:22Z"        },        "via": {          "family": "string",          "match": {}        }      },      "all_of": [        {          "intent": [            "string"          ],          "intent_prefix": [            "string"          ],          "labels_all": [            "string"          ],          "labels_any": [            "string"          ],          "modality": [            "string"          ],          "observed": {            "from": "2019-08-24T14:15:22Z",            "to": "2019-08-24T14:15:22Z"          },          "via": {            "family": "string",            "match": {}          }        }      ],      "none_of": [        {          "intent": [            "string"          ],          "intent_prefix": [            "string"          ],          "labels_all": [            "string"          ],          "labels_any": [            "string"          ],          "modality": [            "string"          ],          "observed": {            "from": "2019-08-24T14:15:22Z",            "to": "2019-08-24T14:15:22Z"          },          "via": {            "family": "string",            "match": {}          }        }      ],      "sequence": {        "first": {          "intent": [            "string"          ],          "intent_prefix": [            "string"          ],          "labels_all": [            "string"          ],          "labels_any": [            "string"          ],          "modality": [            "string"          ],          "observed": {            "from": "2019-08-24T14:15:22Z",            "to": "2019-08-24T14:15:22Z"          },          "via": {            "family": "string",            "match": {}          }        },        "then": {          "intent": [            "string"          ],          "intent_prefix": [            "string"          ],          "labels_all": [            "string"          ],          "labels_any": [            "string"          ],          "modality": [            "string"          ],          "observed": {            "from": "2019-08-24T14:15:22Z",            "to": "2019-08-24T14:15:22Z"          },          "via": {            "family": "string",            "match": {}          }        },        "within_days": 0      },      "metric": "subjects",      "group_by": "string",      "n": 1,      "per_subject": true,      "steps": [        {          "intent": [            "string"          ],          "intent_prefix": [            "string"          ],          "labels_all": [            "string"          ],          "labels_any": [            "string"          ],          "modality": [            "string"          ],          "observed": {            "from": "2019-08-24T14:15:22Z",            "to": "2019-08-24T14:15:22Z"          },          "via": {            "family": "string",            "match": {}          }        },        {          "intent": [            "string"          ],          "intent_prefix": [            "string"          ],          "labels_all": [            "string"          ],          "labels_any": [            "string"          ],          "modality": [            "string"          ],          "observed": {            "from": "2019-08-24T14:15:22Z",            "to": "2019-08-24T14:15:22Z"          },          "via": {            "family": "string",            "match": {}          }        }      ],      "ordered": true,      "numerator": {        "intent": [          "string"        ],        "intent_prefix": [          "string"        ],        "labels_all": [          "string"        ],        "labels_any": [          "string"        ],        "modality": [          "string"        ],        "observed": {          "from": "2019-08-24T14:15:22Z",          "to": "2019-08-24T14:15:22Z"        },        "via": {          "family": "string",          "match": {}        }      },      "denominator": {        "intent": [          "string"        ],        "intent_prefix": [          "string"        ],        "labels_all": [          "string"        ],        "labels_any": [          "string"        ],        "modality": [          "string"        ],        "observed": {          "from": "2019-08-24T14:15:22Z",          "to": "2019-08-24T14:15:22Z"        },        "via": {          "family": "string",          "match": {}        }      },      "subject": "string",      "window": {        "from": "2019-08-24T14:15:22Z",        "to": "2019-08-24T14:15:22Z",        "natural": "string"      },      "anchor": "string",      "lane": "exact",      "query": "string",      "synthesis": false,      "chain": [        {}      ]    },    "notes": [      "string"    ],    "as_of": {      "captured_offset": 0,      "visibility_revision": 0,      "fence_generation": 0,      "built_at": "2019-08-24T14:15:22Z"    },    "coverage": {      "subjects_considered": 0,      "events_considered": 0,      "data_span": {        "min": "2019-08-24T14:15:22Z",        "max": "2019-08-24T14:15:22Z"      },      "window": {        "from": "2019-08-24T14:15:22Z",        "to": "2019-08-24T14:15:22Z"      },      "anchor": "string",      "anchor_at": "2019-08-24T14:15:22Z",      "root_events_ignored": 0,      "complete": true,      "conversations_read": 0,      "conversations_in_cohort": 0,      "subjects_read": 0,      "subjects_in_cohort": 0    },    "result": {      "count": 0,      "subjects": [        {          "alias": "string",          "scope": "string",          "events": 0,          "first_at": "2019-08-24T14:15:22Z",          "last_at": "2019-08-24T14:15:22Z",          "evidence": [            "string"          ],          "evidence_scopes": [            "string"          ],          "cited": true        }      ],      "groups": [        {          "key": "string",          "subjects": 0,          "events": 0,          "tied": true        }      ],      "ties_at_cutoff": true,      "truncated": {        "listed": 0,        "total": 0      },      "next_cursor": "string",      "share": 0,      "numerator": 0,      "denominator": 0    },    "unsupported": {      "reason": "needs_text_attribute",      "detail": "string",      "catalog_hint": "string"    },    "narration_unverified_numbers": [      "string"    ],    "read": {      "map_model": "string",      "conversations_in_cohort": 0,      "conversations_read": 0,      "conversations_unread": 0,      "conversations_truncated": 0,      "conversations_dropped": 0,      "conversations_matching": 0,      "batches": 0,      "batches_failed": 0,      "map_calls": 0,      "theme_calls": 0,      "check_calls": 0,      "narration_calls": 0,      "labels": 0,      "labels_ungrouped": 0,      "themes": [        {          "key": "string",          "subjects": 0,          "conversations": 0,          "evidence": [            "string"          ],          "evidence_scopes": [            "string"          ],          "labels": [            "string"          ]        }      ],      "tokens_input": 0,      "tokens_output": 0,      "cost_usd": 0,      "elapsed_ms": 0,      "reused": true,      "computed_at": "2019-08-24T14:15:22Z",      "plan_fingerprint": "string",      "cohort_fingerprint": "string",      "topic": "string",      "reader_question": "string"    }  },  "coverage": {    "searched": {      "scopes": [        "string"      ],      "scopes_total": 0,      "partitions": 0,      "truncated": true    },    "authorized": {      "scopes": [        "string"      ],      "scopes_total": 0,      "truncated": true    },    "gaps": [      {        "scope": "string",        "reason": "bound",        "left_out": 0      }    ],    "gaps_truncated": true,    "gaps_total": 0,    "watermarks": [      {        "source": "string",        "at": "2019-08-24T14:15:22Z",        "through": "string"      }    ]  },  "warnings": [    "string"  ],  "consistency_token": "string",  "consistency_reset": true,  "derived_through": 0,  "access_restored_at": "2019-08-24T14:15:22Z"}

503 ANSWER_UNAVAILABLE without an answer lane

On a self-hosted server with no CORTEX_ANSWER_* lane configured, POST /v1/answer returns 503 ANSWER_UNAVAILABLE ("no answer LLM is configured on this deployment; set CORTEX_ANSWER_API_KEY …"). It is deployment-wide and scope-independent. See LLM & Answer.

The question field

The field is question, not query

/v1/answer requires question (not query). Sending { "query": … } → 422 "missing field question". It shares view, include, temporal, budgets, and citation_mode semantics with recall.

question_type is ignored

The server picks its retrieval route and question shape from the question itself. A question_type field is accepted and discarded so older clients keep working; the spec marks it deprecated: "multi-session", "temporal-reasoning", "single-session-user" and no question_type all give the same inferred_question_shape for the same question.

Retrieval without the LLM

skip_answer_llm returns an empty answer with citations

skip_answer_llm: true returns 200 with answer: "" and diagnostics.answer_llm_skipped_by_request: true — it runs recall + citation shaping but skips the LLM. Use it when you want the answer endpoint's citation shaping without paying for generation. cite_sources (top-level) is accepted but has no observable effect on a content-only instance.

Response diagnostics

The response is { pack_id, answer, citations[{ marker, layer, id, support_strength }], provenance, diagnostics, as_of }. as_of is a top-level field here (unlike recall, where it does not exist).

diagnostics fields

diagnostics carries recall_ms, llm_ms, total_ms, answer_model, answer_llm_skipped_by_request, tokens_input, tokens_output, and a time_ms phase-map (with a long list of artifact_* and wal_* counters, and inferred_question_shape / question_shape_applied when the server infers a question shape from the question). Token counts are flat tokens_input / tokens_output — there is no pack_used field and no answer_tokens: { prompt, completion }. The model used is at diagnostics.answer_model. The token keys are omitted (absent, not null) when the answer came from the multi-session executor lane (question_type: "multi-session") and on any path that does not call the answer model. The SSE stream's diagnostics event carries both tokens_input and tokens_output, and omits a count it does not know instead of sending null.

Degraded runs and abstentions

  • diagnostics.degraded_sources appears when the answer ran without part of its evidence, as stable codes only (never error text): coordinator_recall, wal_supplement, derived_<lane> (e.g. derived_facts), artifact_search, index_divergence, derived_backfill, facet_plan_unavailable, facet_recall_failed, ms_executor_fallback, else unknown. The answer is still returned; read a non-empty list as "this answer saw less evidence than a healthy run would" and check the server log. On a healthy server it is absent.
  • Abstentions ("The memories contain no such information.") are detected as such. When the question names things the context never mentions, diagnostics.premise_unsupported_terms lists them (for example, a question about a Singapore office's wifi password over IT tickets lists Singapore, office, wifi, password, branch). General nouns such as "issue" or "problem" don't count as unsupported premises, so "How was the VPN issue fixed?" is answered. When an extractive retry runs, diagnostics also carry extractive_retry_outcome (answered, still_abstained or premise_ungrounded).
  • A request carrying the evaluation-only facet_plan_override is refused with 422 EVAL_OVERRIDE_DISABLED unless the server runs with CORTEX_EVAL_FACET_PLAN_OVERRIDE=1. Never set a CORTEX_EVAL_ variable on a production server.
  • /v1/answer merges several recall passes; an event served whole by one pass and as a budget excerpt by another appears once, whole.

Model and streaming

answer_model default is env-driven; streaming is not token-SSE self-hosted

The claude-opus-5-5 default is a managed-cloud value; self-hosted, answer_model resolves to whatever the answer lane (CORTEX_ANSWER_*) is configured with, and /v1/answer is disabled until that lane is set. Streaming is selected by the body field "stream": true, which returns text/event-stream with the events token ({ text }), citations, provenance, diagnostics (a reduced set: answer_model, recall_ms, llm_ms, total_ms, tokens_input, tokens_output) and done ({ pack_id, as_of }). The query parameter ?stream=true is ignored and returns the normal JSON object. See Self-hosting defaults.

Notes

  • session (capability derivation_use_rules_v1) is the read's conversation session, read only where a read-time use rule is in force. A server without that capability ignores it here (recall refuses it with 422 INVALID_BODY), so send it only to a server that lists derivation_use_rules_v1.
  • use_pack_id reuses a recall pack (same-scope only; a pack from a different scope → 404).
  • Temporal questions trigger internal "question shaping" — diagnostics.inferred_question_shape / question_shape_applied reflect it; no request field controls it.
  • order (relevance | recency) is forwarded to recall. When you omit it, recency-shaped questions ("recent…", "latest…", "currently…", "next best step…") auto-infer recency; an explicit value always wins. As with recall, the recency timeline tail is observable in the default holistic view (see POST /v1/recall). Kill-switches: CORTEX_RECENCY_INFERENCE=0 (disable the inference), CORTEX_RECENCY_ORDER=0 (disable the mode) — see Recall Tuning.

On this page