Skip to main content
POST

Authorizations

Authorization
string
header
required

Bearer token used by every /v1/* endpoint. Two kinds of key work here:

  • Long-lived partner key (cxh_pk_live_*) — issued by CollectiveX Health (contact partners@collectivex.health). Keep it server-side only — never expose it in client bundles or mobile app code. This rule is absolute and does not change for any endpoint.
  • Short-lived session key — minted server-side via POST /v1/session-tokens from a long-lived key. Safe to hand to a native mobile client only — not a browser or other JavaScript context, see "API keys" in the description above. It's scoped to /v1/query and /v1/chat only, expires in at most 60 minutes, and can't mint further session keys. POST /v1/session-tokens itself only ever accepts the long-lived key.

Body

application/json

Request body for POST /v1/query. Uses additionalProperties: false (mirrors Zod .strict() on the server, matching ChatRequest's convention). Exactly one of question or messages is required — not both. Retrieval tuning knobs are intentionally not accepted here either, for the same reason as /v1/chat: we curate retrieval, partners don't tune.

question
string
required

A single question, plain string. The preferred shape for /v1/query callers — no message envelope needed. Internally wrapped into a single-element UIMessage before being passed to the same pipeline that /v1/chat uses, so behavior is identical to sending that text as the sole message on /v1/chat.

Minimum string length: 1
messages
object[]

Single-element AI SDK v6 UIMessage array, provided for continuity with callers already building /v1/chat request bodies who want to switch endpoints with a minimal diff. Unlike /v1/chat, this array MUST contain exactly one message with role "user" — /v1/query has no multi-turn concept. Sending more than one message, or a message with a different role, is a 400 invalid_request (use /v1/chat with stream:false instead if you need conversation history).

Required array length: 1 element
response_format
enum<string>
default:text

BETA. When omitted or "text", the response is byte-for-byte the SyncResponse this endpoint has always returned — no behavior change. When "structured", the response ADDS a structured_answer field (machine-readable sections/claims/citations) alongside the unchanged answer/citations fields; nothing is removed or replaced. This field, and the shape it produces, may change in a future minor version while in beta.

Available options:
text,
structured

Response

OK. Always application/json — the SyncResponse flat envelope. No stream negotiation; this endpoint has exactly one response shape.

/v1/query's response — the shared SyncResponse shape plus the BETA structured_answer field. Defined as its own schema (rather than putting structured_answer on SyncResponse directly) so clients generated from this spec do not advertise structured_answer on /v1/chat, which has no response_format field and can never produce it.

request_id
string<uuid>
required

UUID minted per request. Use for support correlation across PostHog / Langfuse / logs.

answer
string
required

The model's answer string. Empty string ("") on deferral or crisis short-circuit (the pipeline refuses to generate an answer without grounded evidence).

citations
object[]
required

Retrieved citations from the peer-reviewed corpus. Empty array on deferral or crisis. Never fabricate or paraphrase a citation that is not in this array.

deferral
null | object

Present when the corpus lacks strong evidence for the query. Present the originalQuery back to the user and do not synthesize an answer from training data.

crisis
null | object

Present when a safety classifier fires before retrieval (self-harm, acute symptoms, IPV). Show the resources verbatim and route the user away from research framing.

error
object

Present when the generation pipeline emitted an error (v1.1, optional). The message is always a generic sanitized string — internal error details are never exposed. With no usable content the response status is 502; alongside a partial answer or a deferral/crisis short-circuit the status stays 200 and this field flags the failure.

structured_answer
object

BETA. Present only when the request set response_format: "structured" (see QueryRequest.response_format). For structured-mode consumers, structured_answer.status is the authoritative outcome — do not also branch on the legacy top-level answer/error fields, which keep text-mode semantics unchanged and can disagree with status on truncated generations. Absent — not null — on every request that omits it or sends response_format: "text", which is every request today: this field is purely additive and never changes the meaning of answer or citations.