curl --request POST \ --url https://api.collectivex.health/v1/jobs \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "question": "What does the research say about magnesium for perimenopause sleep?"}'
Submit a question as an async job (asynchronous /v1/query)
Submit exactly the request body you would send to POST /v1/query
and get a job id back immediately, instead of waiting for the
grounded answer on an open connection. The answer is produced in
the background and read with GET /v1/jobs/{id} — see the “Async
jobs” tag description for the lifecycle, retention, and a polling
recipe.
Validation is identical to /v1/query, and happens now. The
body must contain exactly one of question or messages (one
user message), may set response_format, and may not contain
anything else. Every 400 this endpoint returns — unknown field,
both/neither shape, empty or whitespace-only question, a message
with a file part, a non-user role — is one /v1/query would
have returned for the same body. Nothing is accepted here only to
fail an hour of polling later. One extra rule applies only here:
the body is persisted and echoed back on every poll, so it is
capped at 32 KiB of JSON.
What runs, and when. On 202 the job is queued and starts
running immediately, in the background. It is the same
retrieval + safety + generation pipeline /v1/query uses, with the
same published tone and the same deferral/crisis behaviour — so
the eventual result is exactly the QueryResponse you would have
received synchronously. Expect the job to finish in the same
10–40 s a synchronous call takes; a job is never faster than the
sync call, it just doesn’t make you wait on the socket.
Metering. Submitting is one metered request against your
per-key budget (the same pool as /v1/query, /v1/chat, and
polling). The background execution is not metered again.
Server-to-server only, with your long-lived key. A session
token gets 403 forbidden here, exactly as on GET /v1/jobs/{id}.
Never embed your long-lived key in a client.
POST
/
v1
/
jobs
curl --request POST \ --url https://api.collectivex.health/v1/jobs \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "question": "What does the research say about magnesium for perimenopause sleep?"}'
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
Option 1
Option 2
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.
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.
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).
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
Accepted. The job exists and is running in the background. Poll
poll_url (a path relative to the API base URL you called)
until status is terminal. Nothing about the answer is known
yet — this response deliberately carries no result.
202 body from POST /v1/jobs. A receipt, not a result: it tells
you where to poll and how long the job will be readable.
Path to poll, relative to the API base URL you called (e.g.
/v1/jobs/8f14e45f-…). Deliberately not an absolute URL: the
host you called is the host to poll.
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"Collective X API","colors":{"primary":"#1a3f52","light":"#6aade4","dark":"#101828"},"logo":{"light":"https://mintcdn.com/collective-x/XmdndrtGclqPo6DR/logo/light.svg?fit=max\u0026auto=format\u0026n=XmdndrtGclqPo6DR\u0026q=85\u0026s=a7e95b40e54fc2a5bb8a468486937833","dark":"https://mintcdn.com/collective-x/XmdndrtGclqPo6DR/logo/dark.svg?fit=max\u0026auto=format\u0026n=XmdndrtGclqPo6DR\u0026q=85\u0026s=e49aacf5db1549f425bb1876eb3006ad"},"favicon":"https://mintcdn.com/collective-x/XmdndrtGclqPo6DR/favicon.png?fit=max\u0026auto=format\u0026n=XmdndrtGclqPo6DR\u0026q=85\u0026s=78206f470882a5ed0733d80506b8c90a","navbar":{"links":[{"href":"https://api.collectivex.health","label":"Status"}]},"footer":{},"contextual":{"options":["copy","view","chatgpt","claude","perplexity","mcp","cursor","vscode"],"display":"header"},"styling":{"latex":false},"navigation":{"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"collective-x","hasAudioNarration":false,"actualSubdomain":"collective-x","customDomains":["docs.collectivex.health"],"gitSource":{"type":"github","owner":"collective-x","repo":"docs","deployBranch":"main","contentDirectory":"","isPrivate":false,"mountPath":"","label":""},"chroma":{"latestBuiltDeploymentHistoryId":"6aa635006cb71c753f702f70","collectionId":"75e7c7d0-413f-4f2e-8fac-bcec7f6e2d60","liveDeploymentHistoryId":"6aa635006cb71c753f702f70","schemaVersion":"v3"},"entitlements":{"AI_CHAT":{"status":"DISABLED"}},"buildId":"6aa635006cb71c753f702f70","clientVersion":"0.0.4006","searchFilterCounts":[{"language":"default","tag":"Guides","count":6},{"language":"default","tag":"API Reference","count":5},{"language":"default","tag":"Getting Started","count":3},{"language":"default","tag":"Features","count":2},{"language":"default","tag":"Async jobs","count":2},{"language":"default","tag":"Reference","count":1},{"language":"default","tag":"Ask a question","count":1},{"language":"default","tag":"Have a conversation","count":1},{"language":"default","tag":"Session tokens","count":1}]},"assistantConfig":{"enableAskAiButton":false,"enableStarterQuestions":false,"enableContextualStarterQuestions":false,"enableAskAiSearchBar":true},"basePath":"","hasChatPermissions":false,"hasWebMcpPermissions":false,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"rbac-expansion","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"full-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"vite-origin","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-sso-scim","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-signup","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-dual-write","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-enabled","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"approve-review-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"sso-setup-deadline-banner","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-serve","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"googleFontsInlined":false},"customCss":[],"page":{"pagePath":"api-reference/async-jobs/submit-a-question-as-an-async-job-asynchronous-v1query","slug":"/api-reference/async-jobs/submit-a-question-as-an-async-job-asynchronous-v1query","pageMetadata":{"title":"Submit a question as an async job (asynchronous /v1/query)","description":"Submit exactly the request body you would send to `POST /v1/query`\nand get a job id back immediately, instead of waiting for the\ngrounded answer on an open connection. The answer is produced in\nthe background and read with `GET /v1/jobs/{id}` — see the \"Async\njobs\" tag description for the lifecycle, retention, and a polling\nrecipe.\n\n**Validation is identical to `/v1/query`, and happens now.** The\nbody must contain exactly one of `question` or `messages` (one\nuser message), may set `response_format`, and may not contain\nanything else. Every `400` this endpoint returns — unknown field,\nboth/neither shape, empty or whitespace-only question, a message\nwith a `file` part, a non-`user` role — is one `/v1/query` would\nhave returned for the same body. Nothing is accepted here only to\nfail an hour of polling later. One extra rule applies only here:\nthe body is persisted and echoed back on every poll, so it is\ncapped at 32 KiB of JSON.\n\n**What runs, and when.** On `202` the job is `queued` and starts\nrunning immediately, in the background. It is the same\nretrieval + safety + generation pipeline `/v1/query` uses, with the\nsame published tone and the same deferral/crisis behaviour — so\nthe eventual `result` is exactly the `QueryResponse` you would have\nreceived synchronously. Expect the job to finish in the same\n10–40 s a synchronous call takes; a job is never faster than the\nsync call, it just doesn't make you wait on the socket.\n\n**Metering.** Submitting is one metered request against your\nper-key budget (the same pool as `/v1/query`, `/v1/chat`, and\npolling). The background execution is not metered again.\n\n**Server-to-server only, with your long-lived key.** A session\ntoken gets `403 forbidden` here, exactly as on `GET /v1/jobs/{id}`.\nNever embed your long-lived key in a client.\n","deprecated":null,"version":null,"openapi":"/openapi.yaml post /v1/jobs","href":"/api-reference/async-jobs/submit-a-question-as-an-async-job-asynchronous-v1query","autogeneratedByOpenApi":true},"description":{"compiledSource":"\"use strict\";\nconst {Fragment: _Fragment, jsx: _jsx, jsxs: _jsxs} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n code: \"code\",\n p: \"p\",\n strong: \"strong\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsxs(_Fragment, {\n children: [_jsxs(_components.p, {\n children: [\"Submit exactly the request body you would send to \", _jsx(_components.code, {\n children: \"POST /v1/query\"\n }), \"\\nand get a job id back immediately, instead of waiting for the\\ngrounded answer on an open connection. The answer is produced in\\nthe background and read with \", _jsx(_components.code, {\n children: \"GET /v1/jobs/{id}\"\n }), \" — see the “Async\\njobs” tag description for the lifecycle, retention, and a polling\\nrecipe.\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [_jsxs(_components.strong, {\n children: [\"Validation is identical to \", _jsx(_components.code, {\n children: \"/v1/query\"\n }), \", and happens now.\"]\n }), \" The\\nbody must contain exactly one of \", _jsx(_components.code, {\n children: \"question\"\n }), \" or \", _jsx(_components.code, {\n children: \"messages\"\n }), \" (one\\nuser message), may set \", _jsx(_components.code, {\n children: \"response_format\"\n }), \", and may not contain\\nanything else. Every \", _jsx(_components.code, {\n children: \"400\"\n }), \" this endpoint returns — unknown field,\\nboth/neither shape, empty or whitespace-only question, a message\\nwith a \", _jsx(_components.code, {\n children: \"file\"\n }), \" part, a non-\", _jsx(_components.code, {\n children: \"user\"\n }), \" role — is one \", _jsx(_components.code, {\n children: \"/v1/query\"\n }), \" would\\nhave returned for the same body. Nothing is accepted here only to\\nfail an hour of polling later. One extra rule applies only here:\\nthe body is persisted and echoed back on every poll, so it is\\ncapped at 32 KiB of JSON.\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [_jsx(_components.strong, {\n children: \"What runs, and when.\"\n }), \" On \", _jsx(_components.code, {\n children: \"202\"\n }), \" the job is \", _jsx(_components.code, {\n children: \"queued\"\n }), \" and starts\\nrunning immediately, in the background. It is the same\\nretrieval + safety + generation pipeline \", _jsx(_components.code, {\n children: \"/v1/query\"\n }), \" uses, with the\\nsame published tone and the same deferral/crisis behaviour — so\\nthe eventual \", _jsx(_components.code, {\n children: \"result\"\n }), \" is exactly the \", _jsx(_components.code, {\n children: \"QueryResponse\"\n }), \" you would have\\nreceived synchronously. Expect the job to finish in the same\\n10–40 s a synchronous call takes; a job is never faster than the\\nsync call, it just doesn’t make you wait on the socket.\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [_jsx(_components.strong, {\n children: \"Metering.\"\n }), \" Submitting is one metered request against your\\nper-key budget (the same pool as \", _jsx(_components.code, {\n children: \"/v1/query\"\n }), \", \", _jsx(_components.code, {\n children: \"/v1/chat\"\n }), \", and\\npolling). The background execution is not metered again.\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [_jsx(_components.strong, {\n children: \"Server-to-server only, with your long-lived key.\"\n }), \" A session\\ntoken gets \", _jsx(_components.code, {\n children: \"403 forbidden\"\n }), \" here, exactly as on \", _jsx(_components.code, {\n children: \"GET /v1/jobs/{id}\"\n }), \".\\nNever embed your long-lived key in a client.\"]\n })]\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){return _jsx(_Fragment,{})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"title":"Submit a question as an async job (asynchronous /v1/query)","description":"Submit exactly the request body you would send to `POST /v1/query`\nand get a job id back immediately, instead of waiting for the\ngrounded answer on an open connection. The answer is produced in\nthe background and read with `GET /v1/jobs/{id}` — see the \"Async\njobs\" tag description for the lifecycle, retention, and a polling\nrecipe.\n\n**Validation is identical to `/v1/query`, and happens now.** The\nbody must contain exactly one of `question` or `messages` (one\nuser message), may set `response_format`, and may not contain\nanything else. Every `400` this endpoint returns — unknown field,\nboth/neither shape, empty or whitespace-only question, a message\nwith a `file` part, a non-`user` role — is one `/v1/query` would\nhave returned for the same body. Nothing is accepted here only to\nfail an hour of polling later. One extra rule applies only here:\nthe body is persisted and echoed back on every poll, so it is\ncapped at 32 KiB of JSON.\n\n**What runs, and when.** On `202` the job is `queued` and starts\nrunning immediately, in the background. It is the same\nretrieval + safety + generation pipeline `/v1/query` uses, with the\nsame published tone and the same deferral/crisis behaviour — so\nthe eventual `result` is exactly the `QueryResponse` you would have\nreceived synchronously. Expect the job to finish in the same\n10–40 s a synchronous call takes; a job is never faster than the\nsync call, it just doesn't make you wait on the socket.\n\n**Metering.** Submitting is one metered request against your\nper-key budget (the same pool as `/v1/query`, `/v1/chat`, and\npolling). The background execution is not metered again.\n\n**Server-to-server only, with your long-lived key.** A session\ntoken gets `403 forbidden` here, exactly as on `GET /v1/jobs/{id}`.\nNever embed your long-lived key in a client.\n","deprecated":null,"version":null,"openapi":"/openapi.yaml post /v1/jobs","href":"/api-reference/async-jobs/submit-a-question-as-an-async-job-asynchronous-v1query","autogeneratedByOpenApi":true}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[],"codeExamples":{}},"jsLinks":[],"openApiReferenceData":{"operation":{"operationId":"postJob","summary":"Submit a question as an async job (asynchronous /v1/query)","tags":["Async jobs"],"description":"Submit exactly the request body you would send to `POST /v1/query`\nand get a job id back immediately, instead of waiting for the\ngrounded answer on an open connection. The answer is produced in\nthe background and read with `GET /v1/jobs/{id}` — see the \"Async\njobs\" tag description for the lifecycle, retention, and a polling\nrecipe.\n\n**Validation is identical to `/v1/query`, and happens now.** The\nbody must contain exactly one of `question` or `messages` (one\nuser message), may set `response_format`, and may not contain\nanything else. Every `400` this endpoint returns — unknown field,\nboth/neither shape, empty or whitespace-only question, a message\nwith a `file` part, a non-`user` role — is one `/v1/query` would\nhave returned for the same body. Nothing is accepted here only to\nfail an hour of polling later. One extra rule applies only here:\nthe body is persisted and echoed back on every poll, so it is\ncapped at 32 KiB of JSON.\n\n**What runs, and when.** On `202` the job is `queued` and starts\nrunning immediately, in the background. It is the same\nretrieval + safety + generation pipeline `/v1/query` uses, with the\nsame published tone and the same deferral/crisis behaviour — so\nthe eventual `result` is exactly the `QueryResponse` you would have\nreceived synchronously. Expect the job to finish in the same\n10–40 s a synchronous call takes; a job is never faster than the\nsync call, it just doesn't make you wait on the socket.\n\n**Metering.** Submitting is one metered request against your\nper-key budget (the same pool as `/v1/query`, `/v1/chat`, and\npolling). The background execution is not metered again.\n\n**Server-to-server only, with your long-lived key.** A session\ntoken gets `403 forbidden` here, exactly as on `GET /v1/jobs/{id}`.\nNever embed your long-lived key in a client.\n","security":["51e9b639-99d6-4ba9-93b9-3cd9f4b0d28a"],"requestBody":"e303b314-107a-4683-871f-6ae2662f1cd2","responses":{"202":"0905b2d7-fabb-491c-814a-56bb2b30da2d","400":"d865d6cb-9b7f-43af-a220-e11f3b23ea9f","401":"96483c24-6d47-4975-a025-f0fb3c65cce6","403":"5c25a7f8-ca3a-460d-b57a-7b82c060c9c1","404":"e0fee4d2-14a4-45c9-a9d1-97de0926493c","429":"d7115272-ac06-414b-838f-70275e34c20a","500":"5886d00b-a5aa-409b-acd4-597bf814c0b2","503":"472ee944-8d5f-4f63-a080-5b7f0c7031a1"},"method":"post","type":"path","servers":["3aec66e8-ea38-412b-92a2-04eeb1a50e2f"],"path":"/v1/jobs","baseUrlOptions":["https://api.collectivex.health"],"title":"Submit a question as an async job (asynchronous /v1/query)","codeSamples":[],"requiredOnlyExamples":false,"requestExampleType":"generated","responseExampleType":"generated"},"dependencies":{"parameters":{},"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","description":"Request body for POST /v1/query. Uses additionalProperties: false (mirrors\nZod .strict() on the server, matching ChatRequest's convention). Exactly\none of `question` or `messages` is required — not both. Retrieval tuning\nknobs are intentionally not accepted here either, for the same reason as\n/v1/chat: we curate retrieval, partners don't tune.\n","required":["question"],"properties":{"question":{"type":"string","minLength":1,"description":"A single question, plain string. The preferred shape for /v1/query\ncallers — no message envelope needed. Internally wrapped into a\nsingle-element UIMessage before being passed to the same pipeline\nthat /v1/chat uses, so behavior is identical to sending that text as\nthe sole message on /v1/chat.\n","isRequired":true,"uniqueKey":"oneOf.0.question","typeLabel":"string"},"messages":{"type":"array","description":"Single-element AI SDK v6 UIMessage array, provided for continuity\nwith callers already building /v1/chat request bodies who want to\nswitch endpoints with a minimal diff. Unlike /v1/chat, this array\nMUST contain exactly one message with role \"user\" — /v1/query has\nno multi-turn concept. Sending more than one message, or a message\nwith a different role, is a 400 invalid_request (use /v1/chat with\nstream:false instead if you need conversation history).\n","minItems":1,"maxItems":1,"items":{"type":"object","description":"AI SDK v6 UIMessage shape. id + role + parts are the load-bearing fields.\n`file` parts are rejected with `400 invalid_request` on every endpoint —\nthis API's contract is text questions in, grounded text answers out, and\nfile URLs are never forwarded upstream. Text parts (and, on replayed\nassistant history, the `data-*` parts our own stream emits) are accepted.\n","required":["id","role","parts"],"properties":{"id":{"type":"string","description":"Unique message identifier. AI SDK clients (e.g. useChat) auto-generate\nthis; raw HTTP callers MUST provide it (any opaque string — e.g. UUID,\npartner-side conversation row id). validateUIMessages rejects messages\nwithout an id.\n","isRequired":true,"uniqueKey":"oneOf.0.messages.items.[INDEX].id","typeLabel":"string"},"role":{"type":"string","enum":["user","assistant","system"],"description":"Message sender role.","isRequired":true,"uniqueKey":"oneOf.0.messages.items.[INDEX].role","typeLabel":"enum\u003cstring>"},"parts":{"type":"array","description":"Message content as an array of parts. Each part has a `type` field.\nFor user messages: use `{ \"type\": \"text\", \"text\": \"...\" }`.\nFor assistant messages: the API returns text-delta parts assembled into parts.\n","items":{"type":"object","description":"A single content part within a UIMessage.","required":["type"],"properties":{"type":{"type":"string","description":"Part type. Common values: \"text\", \"tool-invocation\", \"tool-result\".","isRequired":true,"uniqueKey":"oneOf.0.messages.items.[INDEX].parts.items.[INDEX].type","typeLabel":"string"},"text":{"type":"string","description":"Text content (present when type is \"text\").","uniqueKey":"oneOf.0.messages.items.[INDEX].parts.items.[INDEX].text","typeLabel":"string"}},"uniqueKey":"oneOf.0.messages.items.[INDEX].parts.items.[INDEX]","typeLabel":"object"},"isRequired":true,"uniqueKey":"oneOf.0.messages.items.[INDEX].parts","typeLabel":"object[]"}},"uniqueKey":"oneOf.0.messages.items.[INDEX]","typeLabel":"object"},"uniqueKey":"oneOf.0.messages","typeLabel":"object[]"},"response_format":{"type":"string","enum":["text","structured"],"default":"text","description":"**BETA.** When omitted or `\"text\"`, the response is byte-for-byte\nthe `SyncResponse` this endpoint has always returned — no behavior\nchange. When `\"structured\"`, the response ADDS a `structured_answer`\nfield (machine-readable sections/claims/citations) alongside the\nunchanged `answer`/`citations` fields; nothing is removed or\nreplaced. This field, and the shape it produces, may change in a\nfuture minor version while in beta.\n","uniqueKey":"oneOf.0.response_format","typeLabel":"enum\u003cstring>"}},"additionalProperties":false,"uniqueKey":"oneOf.0","typeLabel":"object"},{"type":"object","description":"Request body for POST /v1/query. Uses additionalProperties: false (mirrors\nZod .strict() on the server, matching ChatRequest's convention). Exactly\none of `question` or `messages` is required — not both. Retrieval tuning\nknobs are intentionally not accepted here either, for the same reason as\n/v1/chat: we curate retrieval, partners don't tune.\n","required":["messages"],"properties":{"messages":{"type":"array","description":"Single-element AI SDK v6 UIMessage array, provided for continuity\nwith callers already building /v1/chat request bodies who want to\nswitch endpoints with a minimal diff. Unlike /v1/chat, this array\nMUST contain exactly one message with role \"user\" — /v1/query has\nno multi-turn concept. Sending more than one message, or a message\nwith a different role, is a 400 invalid_request (use /v1/chat with\nstream:false instead if you need conversation history).\n","minItems":1,"maxItems":1,"items":{"type":"object","description":"AI SDK v6 UIMessage shape. id + role + parts are the load-bearing fields.\n`file` parts are rejected with `400 invalid_request` on every endpoint —\nthis API's contract is text questions in, grounded text answers out, and\nfile URLs are never forwarded upstream. Text parts (and, on replayed\nassistant history, the `data-*` parts our own stream emits) are accepted.\n","required":["id","role","parts"],"properties":{"id":{"type":"string","description":"Unique message identifier. AI SDK clients (e.g. useChat) auto-generate\nthis; raw HTTP callers MUST provide it (any opaque string — e.g. UUID,\npartner-side conversation row id). validateUIMessages rejects messages\nwithout an id.\n","isRequired":true,"uniqueKey":"oneOf.1.messages.items.[INDEX].id","typeLabel":"string"},"role":{"type":"string","enum":["user","assistant","system"],"description":"Message sender role.","isRequired":true,"uniqueKey":"oneOf.1.messages.items.[INDEX].role","typeLabel":"enum\u003cstring>"},"parts":{"type":"array","description":"Message content as an array of parts. Each part has a `type` field.\nFor user messages: use `{ \"type\": \"text\", \"text\": \"...\" }`.\nFor assistant messages: the API returns text-delta parts assembled into parts.\n","items":{"type":"object","description":"A single content part within a UIMessage.","required":["type"],"properties":{"type":{"type":"string","description":"Part type. Common values: \"text\", \"tool-invocation\", \"tool-result\".","isRequired":true,"uniqueKey":"oneOf.1.messages.items.[INDEX].parts.items.[INDEX].type","typeLabel":"string"},"text":{"type":"string","description":"Text content (present when type is \"text\").","uniqueKey":"oneOf.1.messages.items.[INDEX].parts.items.[INDEX].text","typeLabel":"string"}},"uniqueKey":"oneOf.1.messages.items.[INDEX].parts.items.[INDEX]","typeLabel":"object"},"isRequired":true,"uniqueKey":"oneOf.1.messages.items.[INDEX].parts","typeLabel":"object[]"}},"uniqueKey":"oneOf.1.messages.items.[INDEX]","typeLabel":"object"},"isRequired":true,"uniqueKey":"oneOf.1.messages","typeLabel":"object[]"},"question":{"type":"string","minLength":1,"description":"A single question, plain string. The preferred shape for /v1/query\ncallers — no message envelope needed. Internally wrapped into a\nsingle-element UIMessage before being passed to the same pipeline\nthat /v1/chat uses, so behavior is identical to sending that text as\nthe sole message on /v1/chat.\n","uniqueKey":"oneOf.1.question","typeLabel":"string"},"response_format":{"type":"string","enum":["text","structured"],"default":"text","description":"**BETA.** When omitted or `\"text\"`, the response is byte-for-byte\nthe `SyncResponse` this endpoint has always returned — no behavior\nchange. When `\"structured\"`, the response ADDS a `structured_answer`\nfield (machine-readable sections/claims/citations) alongside the\nunchanged `answer`/`citations` fields; nothing is removed or\nreplaced. This field, and the shape it produces, may change in a\nfuture minor version while in beta.\n","uniqueKey":"oneOf.1.response_format","typeLabel":"enum\u003cstring>"}},"additionalProperties":false,"uniqueKey":"oneOf.1","typeLabel":"object"}],"uniqueKey":"","typeLabel":"object"},"examples":{"question":{"title":"question","value":{"question":"What does the research say about magnesium for perimenopause sleep?"}},"structured":{"title":"structured","value":{"question":"What does the research say about magnesium for perimenopause sleep?","response_format":"structured"}}},"exampleType":"examples"}},"exampleType":"examples"},"responses":{"0905b2d7-fabb-491c-814a-56bb2b30da2d":{"description":"Accepted. The job exists and is running in the background. Poll\n`poll_url` (a path relative to the API base URL you called)\nuntil `status` is terminal. Nothing about the answer is known\nyet — this response deliberately carries no `result`.\n","headers":{"X-Request-Id":{"description":"UUID correlation key for this request. Also stamped on the job's background execution, so quoting it to support finds both.","required":true,"schema":{"type":"string","format":"uuid","example":"550e8400-e29b-41d4-a716-446655440000"}},"X-RateLimit-Limit":{"description":"Maximum requests per minute for this key (shared budget with /v1/query, /v1/chat, /v1/jobs/{id}, and /v1/session-tokens; per-key configurable, default 60).","required":true,"schema":{"type":"integer","example":60}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current per-minute rate-limit window.","required":true,"schema":{"type":"integer","example":59}},"X-RateLimit-Reset":{"description":"Unix timestamp (seconds) when the per-minute rate-limit window resets.","required":true,"schema":{"type":"integer","example":1717000060}}},"content":{"application/json":{"schema":{"type":"object","description":"`202` body from `POST /v1/jobs`. A receipt, not a result: it tells\nyou where to poll and how long the job will be readable.\n","required":["request_id","id","job_type","status","poll_url","expires_at","created_at"],"properties":{"request_id":{"type":"string","format":"uuid","description":"UUID minted for this submission. The job's background execution reuses it for tracing, so quote it to support.","isRequired":true,"uniqueKey":"request_id","typeLabel":"string\u003cuuid>"},"id":{"type":"string","format":"uuid","description":"The job's id. Use it with `GET /v1/jobs/{id}`.","isRequired":true,"uniqueKey":"id","typeLabel":"string\u003cuuid>"},"job_type":{"type":"string","description":"Always `query` today — see `JobResponse.job_type`.","example":"query","isRequired":true,"uniqueKey":"job_type","typeLabel":"string"},"status":{"type":"string","enum":["queued"],"description":"Always `queued` on the 202 — execution begins in the background immediately after this response is sent.","isRequired":true,"uniqueKey":"status","typeLabel":"enum\u003cstring>"},"poll_url":{"type":"string","description":"Path to poll, relative to the API base URL you called (e.g.\n`/v1/jobs/8f14e45f-…`). Deliberately not an absolute URL: the\nhost you called is the host to poll.\n","example":"/v1/jobs/8f14e45f-ceea-467e-adc0-7a1a6b5f6b28","isRequired":true,"uniqueKey":"poll_url","typeLabel":"string"},"expires_at":{"type":"string","format":"date-time","description":"When the job and its result stop being readable — one hour after submission. Fetch your result before then.","isRequired":true,"uniqueKey":"expires_at","typeLabel":"string\u003cdate-time>"},"created_at":{"type":"string","format":"date-time","description":"When the job was created.","isRequired":true,"uniqueKey":"created_at","typeLabel":"string\u003cdate-time>"}},"uniqueKey":"","typeLabel":"object"},"example":{"request_id":"550e8400-e29b-41d4-a716-446655440000","id":"8f14e45f-ceea-467e-adc0-7a1a6b5f6b28","job_type":"query","status":"queued","poll_url":"/v1/jobs/8f14e45f-ceea-467e-adc0-7a1a6b5f6b28","expires_at":"2026-09-11T13:00:00.000Z","created_at":"2026-09-11T12:00:00.000Z"},"examples":{"Example":{"title":"Example","value":{"request_id":"550e8400-e29b-41d4-a716-446655440000","id":"8f14e45f-ceea-467e-adc0-7a1a6b5f6b28","job_type":"query","status":"queued","poll_url":"/v1/jobs/8f14e45f-ceea-467e-adc0-7a1a6b5f6b28","expires_at":"2026-09-11T13:00:00.000Z","created_at":"2026-09-11T12:00:00.000Z"}}},"exampleType":"examples"}},"exampleType":"examples"},"d865d6cb-9b7f-43af-a220-e11f3b23ea9f":{"description":"400 Bad Request. The body failed the `/v1/query` request rules\n(the message names the problem — same texts as `/v1/query`), or\nexceeds the 32 KiB async-job body cap. Fix the request; nothing\nwas created.\n","headers":{"X-Request-Id":{"description":"Request UUID.","schema":{"type":"string","format":"uuid"}}},"content":{"application/json":{"schema":{"type":"object","description":"Standard partner API error envelope (D-09). Mirrors Anthropic/OpenAI error shape.\nParse `error.code` for machine-readable error classification.\n","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Machine-readable error code (D-10 locked enum). Use this for branching logic.\nAdding new codes is a minor version bump per the versioning policy.\n","enum":["unauthorized","forbidden","rate_limited","invalid_request","corpus_unavailable","grounding_deferral","crisis_resources","not_found","internal_error"],"isRequired":true,"uniqueKey":"error.code","typeLabel":"enum\u003cstring>"},"message":{"type":"string","description":"Human-readable error description. Use for logging; do not parse programmatically.","isRequired":true,"uniqueKey":"error.message","typeLabel":"string"},"request_id":{"type":"string","format":"uuid","description":"UUID for this request. Include when contacting support.","isRequired":true,"uniqueKey":"error.request_id","typeLabel":"string\u003cuuid>"}},"isRequired":true,"uniqueKey":"error","typeLabel":"object"}},"uniqueKey":"","typeLabel":"object"},"examples":{"empty_question":{"title":"empty_question","value":{"error":{"code":"invalid_request","message":"Latest user message must not be empty.","request_id":"550e8400-e29b-41d4-a716-446655440000"}}},"unknown_field":{"title":"unknown_field","value":{"error":{"code":"invalid_request","message":"Unknown field: topK. The /v1/query endpoint does not accept retrieval tuning parameters.","request_id":"550e8400-e29b-41d4-a716-446655440000"}}},"too_large":{"title":"too_large","value":{"error":{"code":"invalid_request","message":"Request body too large for an async job (limit 32768 bytes of JSON).","request_id":"550e8400-e29b-41d4-a716-446655440000"}}}},"exampleType":"examples"}},"exampleType":"examples"},"96483c24-6d47-4975-a025-f0fb3c65cce6":{"description":"401 Unauthorized. Missing, malformed, or invalid API key. Check that the Authorization\nheader is present and has the form `Bearer \u003ckey>`. An EXPIRED session token also\nreturns 401, with an \"API key expired\" message — session-token clients should treat\nthat specific message as \"request a fresh token from your backend\", not as an alert.\nSession tokens additionally return 401 whenever the session-token feature is disabled\n(the feature flag is a kill switch for outstanding keys).\n","headers":{"X-Request-Id":{"description":"Request UUID.","schema":{"type":"string","format":"uuid"}}},"content":{"application/json":{"schema":{"type":"object","description":"Standard partner API error envelope (D-09). Mirrors Anthropic/OpenAI error shape.\nParse `error.code` for machine-readable error classification.\n","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Machine-readable error code (D-10 locked enum). Use this for branching logic.\nAdding new codes is a minor version bump per the versioning policy.\n","enum":["unauthorized","forbidden","rate_limited","invalid_request","corpus_unavailable","grounding_deferral","crisis_resources","not_found","internal_error"],"isRequired":true,"uniqueKey":"error.code","typeLabel":"enum\u003cstring>"},"message":{"type":"string","description":"Human-readable error description. Use for logging; do not parse programmatically.","isRequired":true,"uniqueKey":"error.message","typeLabel":"string"},"request_id":{"type":"string","format":"uuid","description":"UUID for this request. Include when contacting support.","isRequired":true,"uniqueKey":"error.request_id","typeLabel":"string\u003cuuid>"}},"isRequired":true,"uniqueKey":"error","typeLabel":"object"}},"uniqueKey":"","typeLabel":"object"},"example":{"error":{"code":"unauthorized","message":"Missing or malformed Authorization header. Expected: Bearer \u003ckey>","request_id":"550e8400-e29b-41d4-a716-446655440000"}},"examples":{"Example":{"title":"Example","value":{"error":{"code":"unauthorized","message":"Missing or malformed Authorization header. Expected: Bearer \u003ckey>","request_id":"550e8400-e29b-41d4-a716-446655440000"}}}},"exampleType":"examples"}},"exampleType":"examples"},"5c25a7f8-ca3a-460d-b57a-7b82c060c9c1":{"description":"403 Forbidden. Key is valid but does not have permission for this operation. Four\ncases, distinguishable by `error.message`: (1) the key's scope excludes this\nendpoint — e.g. a session token calling anything other than /v1/query or /v1/chat,\nincluding /v1/session-tokens itself; (2) the key's total request allowance is\nexhausted (\"…allowance is exhausted. Mint a new session token.\" — permanent for\nthat key, do not retry; mint a new token); (3) \"This session token is not usable.\nMint a new one.\" — a SERVER-SIDE provisioning fault, not a client mistake: do NOT\nre-mint in a loop (each mint is a metered request and the replacement will fail the\nsame way) — surface the error and contact partners@collectivex.health; (4) an\naccount-level restriction — contact partners@collectivex.health.\n","headers":{"X-Request-Id":{"description":"Request UUID.","schema":{"type":"string","format":"uuid"}}},"content":{"application/json":{"schema":{"type":"object","description":"Standard partner API error envelope (D-09). Mirrors Anthropic/OpenAI error shape.\nParse `error.code` for machine-readable error classification.\n","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Machine-readable error code (D-10 locked enum). Use this for branching logic.\nAdding new codes is a minor version bump per the versioning policy.\n","enum":["unauthorized","forbidden","rate_limited","invalid_request","corpus_unavailable","grounding_deferral","crisis_resources","not_found","internal_error"],"isRequired":true,"uniqueKey":"error.code","typeLabel":"enum\u003cstring>"},"message":{"type":"string","description":"Human-readable error description. Use for logging; do not parse programmatically.","isRequired":true,"uniqueKey":"error.message","typeLabel":"string"},"request_id":{"type":"string","format":"uuid","description":"UUID for this request. Include when contacting support.","isRequired":true,"uniqueKey":"error.request_id","typeLabel":"string\u003cuuid>"}},"isRequired":true,"uniqueKey":"error","typeLabel":"object"}},"uniqueKey":"","typeLabel":"object"},"example":{"error":{"code":"forbidden","message":"This key does not have permission to access /v1/chat.","request_id":"550e8400-e29b-41d4-a716-446655440000"}},"examples":{"Example":{"title":"Example","value":{"error":{"code":"forbidden","message":"This key does not have permission to access /v1/chat.","request_id":"550e8400-e29b-41d4-a716-446655440000"}}}},"exampleType":"examples"}},"exampleType":"examples"},"e0fee4d2-14a4-45c9-a9d1-97de0926493c":{"description":"404 Not Found — **endpoint switched off**. Not expected in\nnormal operation: you only see it if the operator kill switch\nfor async jobs has been turned off, in which case every caller\nreceives this bare response: status 404, body `{}`, no error\nenvelope and no `X-Request-Id` — indistinguishable from an\nunknown route by design. There is nothing to retry; contact\npartners@collectivex.health.\n","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"uniqueKey":"","typeLabel":"object"},"example":{},"examples":{"Example":{"title":"Example","value":{}}},"exampleType":"examples"}},"exampleType":"examples"},"d7115272-ac06-414b-838f-70275e34c20a":{"description":"429 Too Many Requests. Rate limit exceeded. Read the Retry-After header (seconds)\nbefore retrying. Also check X-RateLimit-Remaining on successful responses to\nself-throttle before hitting 429. Limits are per-key configurable (a partner may\nhave a non-default requests-per-minute ceiling and/or a requests-per-day ceiling);\nX-RateLimit-* on this response always reflects whichever budget was exceeded.\n","headers":{"Retry-After":{"description":"Seconds until the rate-limit window resets. Wait this long before retrying.","required":true,"schema":{"type":"integer","example":37}},"X-Request-Id":{"description":"Request UUID.","schema":{"type":"string","format":"uuid"}},"X-RateLimit-Limit":{"description":"Maximum requests for the window that was exceeded (per-minute by default, or per-day if this key has a daily budget and that's what was hit).","schema":{"type":"integer","example":60}},"X-RateLimit-Remaining":{"description":"Requests remaining (0 when rate-limited).","schema":{"type":"integer","example":0}},"X-RateLimit-Reset":{"description":"Unix timestamp (seconds) when the window resets.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","description":"Standard partner API error envelope (D-09). Mirrors Anthropic/OpenAI error shape.\nParse `error.code` for machine-readable error classification.\n","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Machine-readable error code (D-10 locked enum). Use this for branching logic.\nAdding new codes is a minor version bump per the versioning policy.\n","enum":["unauthorized","forbidden","rate_limited","invalid_request","corpus_unavailable","grounding_deferral","crisis_resources","not_found","internal_error"],"isRequired":true,"uniqueKey":"error.code","typeLabel":"enum\u003cstring>"},"message":{"type":"string","description":"Human-readable error description. Use for logging; do not parse programmatically.","isRequired":true,"uniqueKey":"error.message","typeLabel":"string"},"request_id":{"type":"string","format":"uuid","description":"UUID for this request. Include when contacting support.","isRequired":true,"uniqueKey":"error.request_id","typeLabel":"string\u003cuuid>"}},"isRequired":true,"uniqueKey":"error","typeLabel":"object"}},"uniqueKey":"","typeLabel":"object"},"example":{"error":{"code":"rate_limited","message":"Rate limit exceeded. Retry after 37s.","request_id":"550e8400-e29b-41d4-a716-446655440000"}},"examples":{"Example":{"title":"Example","value":{"error":{"code":"rate_limited","message":"Rate limit exceeded. Retry after 37s.","request_id":"550e8400-e29b-41d4-a716-446655440000"}}}},"exampleType":"examples"}},"exampleType":"examples"},"5886d00b-a5aa-409b-acd4-597bf814c0b2":{"description":"500 Internal Server Error. Unexpected server-side failure. Retry with exponential\nbackoff. If persistent, contact partners@collectivex.health with the request_id.\n","headers":{"X-Request-Id":{"description":"Request UUID.","schema":{"type":"string","format":"uuid"}}},"content":{"application/json":{"schema":{"type":"object","description":"Standard partner API error envelope (D-09). Mirrors Anthropic/OpenAI error shape.\nParse `error.code` for machine-readable error classification.\n","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Machine-readable error code (D-10 locked enum). Use this for branching logic.\nAdding new codes is a minor version bump per the versioning policy.\n","enum":["unauthorized","forbidden","rate_limited","invalid_request","corpus_unavailable","grounding_deferral","crisis_resources","not_found","internal_error"],"isRequired":true,"uniqueKey":"error.code","typeLabel":"enum\u003cstring>"},"message":{"type":"string","description":"Human-readable error description. Use for logging; do not parse programmatically.","isRequired":true,"uniqueKey":"error.message","typeLabel":"string"},"request_id":{"type":"string","format":"uuid","description":"UUID for this request. Include when contacting support.","isRequired":true,"uniqueKey":"error.request_id","typeLabel":"string\u003cuuid>"}},"isRequired":true,"uniqueKey":"error","typeLabel":"object"}},"uniqueKey":"","typeLabel":"object"},"example":{"error":{"code":"internal_error","message":"An unexpected error occurred. Please retry.","request_id":"550e8400-e29b-41d4-a716-446655440000"}},"examples":{"Example":{"title":"Example","value":{"error":{"code":"internal_error","message":"An unexpected error occurred. Please retry.","request_id":"550e8400-e29b-41d4-a716-446655440000"}}}},"exampleType":"examples"}},"exampleType":"examples"},"472ee944-8d5f-4f63-a080-5b7f0c7031a1":{"description":"503 Service Unavailable. A TRANSIENT failure in the auth/rate-limit\ndependency every partner endpoint shares (Unkey), so the job could\nnot be accepted. Always carries `error.code: \"internal_error\"` and\na `Retry-After` header (jittered); retry the submission with\nexponential backoff — nothing was created. Unlike `/v1/query`,\nthis endpoint never returns `corpus_unavailable` at submission:\nthe research corpus is not touched until the job runs in the\nbackground, and a corpus outage during execution surfaces as a\n`failed` job with `error_code: \"corpus_unavailable\"` on\n`GET /v1/jobs/{id}`, not as a 503 here.\n","headers":{"X-Request-Id":{"description":"Request UUID.","schema":{"type":"string","format":"uuid"}},"Retry-After":{"description":"Seconds to wait before retrying. Always present on this response.","required":true,"schema":{"type":"integer","example":5}}},"content":{"application/json":{"schema":{"type":"object","description":"Standard partner API error envelope (D-09). Mirrors Anthropic/OpenAI error shape.\nParse `error.code` for machine-readable error classification.\n","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Machine-readable error code (D-10 locked enum). Use this for branching logic.\nAdding new codes is a minor version bump per the versioning policy.\n","enum":["unauthorized","forbidden","rate_limited","invalid_request","corpus_unavailable","grounding_deferral","crisis_resources","not_found","internal_error"],"isRequired":true,"uniqueKey":"error.code","typeLabel":"enum\u003cstring>"},"message":{"type":"string","description":"Human-readable error description. Use for logging; do not parse programmatically.","isRequired":true,"uniqueKey":"error.message","typeLabel":"string"},"request_id":{"type":"string","format":"uuid","description":"UUID for this request. Include when contacting support.","isRequired":true,"uniqueKey":"error.request_id","typeLabel":"string\u003cuuid>"}},"isRequired":true,"uniqueKey":"error","typeLabel":"object"}},"uniqueKey":"","typeLabel":"object"},"example":{"error":{"code":"internal_error","message":"Rate limit service temporarily unavailable. Please retry.","request_id":"550e8400-e29b-41d4-a716-446655440000"}},"examples":{"Example":{"title":"Example","value":{"error":{"code":"internal_error","message":"Rate limit service temporarily unavailable. Please retry.","request_id":"550e8400-e29b-41d4-a716-446655440000"}}}},"exampleType":"examples"}},"exampleType":"examples"}},"security":{"51e9b639-99d6-4ba9-93b9-3cd9f4b0d28a":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"cxh_pk_live_*","description":"Bearer token used by every `/v1/*` endpoint. Two kinds of key work here:\n - **Long-lived partner key** (`cxh_pk_live_*`) — issued by\n CollectiveX Health (contact partners@collectivex.health). Keep\n it server-side only — never expose it in client bundles or\n mobile app code. This rule is absolute and does not change for\n any endpoint.\n - **Short-lived session key** — minted server-side via\n `POST /v1/session-tokens` from a long-lived key. Safe to hand\n to a native mobile client only — not a browser or other\n JavaScript context, see \"API keys\" in the description above.\n It's scoped to `/v1/query` and `/v1/chat` only, expires in at\n most 60 minutes, and can't mint further session keys.\n `POST /v1/session-tokens` itself only ever accepts the\n long-lived key.\n","scopes":[]}}},"servers":{"3aec66e8-ea38-412b-92a2-04eeb1a50e2f":{"url":"https://api.collectivex.health","description":"Production partner API"}},"processedSecurityOptions":[{"schemes":[{"scheme":{"type":"http","scheme":"bearer","bearerFormat":"cxh_pk_live_*","description":"Bearer token used by every `/v1/*` endpoint. Two kinds of key work here:\n - **Long-lived partner key** (`cxh_pk_live_*`) — issued by\n CollectiveX Health (contact partners@collectivex.health). Keep\n it server-side only — never expose it in client bundles or\n mobile app code. This rule is absolute and does not change for\n any endpoint.\n - **Short-lived session key** — minted server-side via\n `POST /v1/session-tokens` from a long-lived key. Safe to hand\n to a native mobile client only — not a browser or other\n JavaScript context, see \"API keys\" in the description above.\n It's scoped to `/v1/query` and `/v1/chat` only, expires in at\n most 60 minutes, and can't mint further session keys.\n `POST /v1/session-tokens` itself only ever accepts the\n long-lived key.\n","scopes":[]},"schema":{"title":"Bearer Auth","type":"string","isRequired":true,"description":"Bearer token used by every `/v1/*` endpoint. Two kinds of key work here:\n - **Long-lived partner key** (`cxh_pk_live_*`) — issued by\n CollectiveX Health (contact partners@collectivex.health). Keep\n it server-side only — never expose it in client bundles or\n mobile app code. This rule is absolute and does not change for\n any endpoint.\n - **Short-lived session key** — minted server-side via\n `POST /v1/session-tokens` from a long-lived key. Safe to hand\n to a native mobile client only — not a browser or other\n JavaScript context, see \"API keys\" in the description above.\n It's scoped to `/v1/query` and `/v1/chat` only, expires in at\n most 60 minutes, and can't mint further session keys.\n `POST /v1/session-tokens` itself only ever accepts the\n long-lived key.\n","format":"bearer","typeLabel":"string\u003cbearer>","deprecated":false},"schemeName":"bearerAuth","name":"Authorization"}],"title":"bearerAuth"}]},"originalFileLocation":"openapi.yaml"},"subdomain":"collective-x","siteName":"Collective X API","title":"Submit a question as an async job (asynchronous /v1/query) - Collective X API","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-09-13T05:30:51.784Z","originalFileLocation":"api-reference/async-jobs/submit-a-question-as-an-async-job-asynchronous-v1query.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"Submit exactly the request body you would send to POST /v1/query and get a job id back immediately, instead of waiting for the grounded answer on an open connection. The answer is produced in the background and read with GET /v1/jobs/{id} — see the \u0026quot;Async jobs\u0026quot; tag description for the lifecycle, retention, and a polling recipe. Validation is identical to /v1/query, and happens now. The body must contain exactly one of question or messages (one user message), may set response_format, and may not contain anything else. Every 400 this endpoint returns — unknown field, both/neither shape, empty or whitespace-only question, a message with a file part, a non-user role — is one /v1/query would have returned for the same body. Nothing is accepted here only to fail an hour of polling later. One extra rule applies only here: the body is persisted and echoed back on every poll, so it is capped at 32 KiB of JSON. What runs, and when. On 202 the job is queued and starts running immediately, in the background. It is the same retrieval + safety + generation pipeline /v1/query uses, with the same published tone and the same deferral/crisis behaviour — so the eventual result is exactly the QueryResponse you would have received synchronously. Expect the job to finish in the same 10–40 s a synchronous call takes; a job is never faster than the sync call, it just doesn\u0026#x27;t make you wait on the socket. Metering. Submitting is one metered request against your per-key budget (the same pool as /v1/query, /v1/chat, and polling). The background execution is not metered again. Server-to-server only, with your long-lived key. A session token gets 403 forbidden here, exactly as on GET /v1/jobs/{id}. Never embed your long-lived key in a client.\">\u003cmeta data-mint-head name=\"application-name\" content=\"Collective X API\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/collective-x/0_4ikjEQ7vib8PG9/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/collective-x/0_4ikjEQ7vib8PG9/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/collective-x/0_4ikjEQ7vib8PG9/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/collective-x/0_4ikjEQ7vib8PG9/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/collective-x/0_4ikjEQ7vib8PG9/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/collective-x/0_4ikjEQ7vib8PG9/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/collective-x/0_4ikjEQ7vib8PG9/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/collective-x/0_4ikjEQ7vib8PG9/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/collective-x/0_4ikjEQ7vib8PG9/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Submit a question as an async job (asynchronous /v1/query) - Collective X API\">\u003cmeta data-mint-head property=\"og:description\" content=\"Submit exactly the request body you would send to POST /v1/query and get a job id back immediately, instead of waiting for the grounded answer on an open connection. The answer is produced in the background and read with GET /v1/jobs/{id} — see the \u0026quot;Async jobs\u0026quot; tag description for the lifecycle, retention, and a polling recipe. Validation is identical to /v1/query, and happens now. The body must contain exactly one of question or messages (one user message), may set response_format, and may not contain anything else. Every 400 this endpoint returns — unknown field, both/neither shape, empty or whitespace-only question, a message with a file part, a non-user role — is one /v1/query would have returned for the same body. Nothing is accepted here only to fail an hour of polling later. One extra rule applies only here: the body is persisted and echoed back on every poll, so it is capped at 32 KiB of JSON. What runs, and when. On 202 the job is queued and starts running immediately, in the background. It is the same retrieval + safety + generation pipeline /v1/query uses, with the same published tone and the same deferral/crisis behaviour — so the eventual result is exactly the QueryResponse you would have received synchronously. Expect the job to finish in the same 10–40 s a synchronous call takes; a job is never faster than the sync call, it just doesn\u0026#x27;t make you wait on the socket. Metering. Submitting is one metered request against your per-key budget (the same pool as /v1/query, /v1/chat, and polling). The background execution is not metered again. Server-to-server only, with your long-lived key. A session token gets 403 forbidden here, exactly as on GET /v1/jobs/{id}. Never embed your long-lived key in a client.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"Collective X API\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.collectivex.health/api-reference/async-jobs/submit-a-question-as-an-async-job-asynchronous-v1query\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://collective-x.mintlify.app/_mintlify/api/og/v2?division=Async+jobs\u0026amp;title=Submit+a+question+as+an+async+job+%28asynchronous+%2Fv1%2Fquery%29\u0026amp;description=Submit+exactly+the+request+body+you+would+send+to+%60POST+%2Fv1%2Fquery%60%0Aand+get+a+job+id+back+immediately%2C+instead+of+waiting+for+the%0Agrounded+answer+on+an+open+conn\u0026amp;theme=bb4e877d2136d284fc6ff9a0\u0026amp;signature=584972423e6ed57e924bd1ba677c95d44cc9f8f3ce1715b4b71e7d1382f1f609\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Submit a question as an async job (asynchronous /v1/query) - Collective X API\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"Submit exactly the request body you would send to POST /v1/query and get a job id back immediately, instead of waiting for the grounded answer on an open connection. The answer is produced in the background and read with GET /v1/jobs/{id} — see the \u0026quot;Async jobs\u0026quot; tag description for the lifecycle, retention, and a polling recipe. Validation is identical to /v1/query, and happens now. The body must contain exactly one of question or messages (one user message), may set response_format, and may not contain anything else. Every 400 this endpoint returns — unknown field, both/neither shape, empty or whitespace-only question, a message with a file part, a non-user role — is one /v1/query would have returned for the same body. Nothing is accepted here only to fail an hour of polling later. One extra rule applies only here: the body is persisted and echoed back on every poll, so it is capped at 32 KiB of JSON. What runs, and when. On 202 the job is queued and starts running immediately, in the background. It is the same retrieval + safety + generation pipeline /v1/query uses, with the same published tone and the same deferral/crisis behaviour — so the eventual result is exactly the QueryResponse you would have received synchronously. Expect the job to finish in the same 10–40 s a synchronous call takes; a job is never faster than the sync call, it just doesn\u0026#x27;t make you wait on the socket. Metering. Submitting is one metered request against your per-key budget (the same pool as /v1/query, /v1/chat, and polling). The background execution is not metered again. Server-to-server only, with your long-lived key. A session token gets 403 forbidden here, exactly as on GET /v1/jobs/{id}. Never embed your long-lived key in a client.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://collective-x.mintlify.app/_mintlify/api/og/v2?division=Async+jobs\u0026amp;title=Submit+a+question+as+an+async+job+%28asynchronous+%2Fv1%2Fquery%29\u0026amp;description=Submit+exactly+the+request+body+you+would+send+to+%60POST+%2Fv1%2Fquery%60%0Aand+get+a+job+id+back+immediately%2C+instead+of+waiting+for+the%0Agrounded+answer+on+an+open+conn\u0026amp;theme=bb4e877d2136d284fc6ff9a0\u0026amp;signature=584972423e6ed57e924bd1ba677c95d44cc9f8f3ce1715b4b71e7d1382f1f609\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/collective-x/0_4ikjEQ7vib8PG9/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"Collective X API\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#1a3f52\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.collectivex.health/api-reference/async-jobs/submit-a-question-as-an-async-job-asynchronous-v1query\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/api-reference/async-jobs/submit-a-question-as-an-async-job-asynchronous-v1query.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.collectivex.health/#organization\",\"name\":\"Collective X API\",\"url\":\"https://docs.collectivex.health\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/collective-x/XmdndrtGclqPo6DR/logo/light.svg?fit=max\u0026auto=format\u0026n=XmdndrtGclqPo6DR\u0026q=85\u0026s=a7e95b40e54fc2a5bb8a468486937833\"}},{\"@type\":\"WebSite\",\"@id\":\"https://docs.collectivex.health#website\",\"name\":\"Collective X API\",\"url\":\"https://docs.collectivex.health\",\"publisher\":{\"@id\":\"https://docs.collectivex.health/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.collectivex.health/api-reference/async-jobs/submit-a-question-as-an-async-job-asynchronous-v1query#webpage\",\"url\":\"https://docs.collectivex.health/api-reference/async-jobs/submit-a-question-as-an-async-job-asynchronous-v1query\",\"name\":\"Submit a question as an async job (asynchronous /v1/query)\",\"description\":\"Submit exactly the request body you would send to POST /v1/query and get a job id back immediately, instead of waiting for the grounded answer on an open connection. The answer is produced in the background and read with GET /v1/jobs/{id} — see the \\\"Async jobs\\\" tag description for the lifecycle, retention, and a polling recipe. Validation is identical to /v1/query, and happens now. The body must contain exactly one of question or messages (one user message), may set response_format, and may not contain anything else. Every 400 this endpoint returns — unknown field, both/neither shape, empty or whitespace-only question, a message with a file part, a non-user role — is one /v1/query would have returned for the same body. Nothing is accepted here only to fail an hour of polling later. One extra rule applies only here: the body is persisted and echoed back on every poll, so it is capped at 32 KiB of JSON. What runs, and when. On 202 the job is queued and starts running immediately, in the background. It is the same retrieval + safety + generation pipeline /v1/query uses, with the same published tone and the same deferral/crisis behaviour — so the eventual result is exactly the QueryResponse you would have received synchronously. Expect the job to finish in the same 10–40 s a synchronous call takes; a job is never faster than the sync call, it just doesn't make you wait on the socket. Metering. Submitting is one metered request against your per-key budget (the same pool as /v1/query, /v1/chat, and polling). The background execution is not metered again. Server-to-server only, with your long-lived key. A session token gets 403 forbidden here, exactly as on GET /v1/jobs/{id}. Never embed your long-lived key in a client.\",\"dateModified\":\"2026-09-13T05:30:51.784Z\",\"isPartOf\":{\"@id\":\"https://docs.collectivex.health#website\"},\"breadcrumb\":{\"@id\":\"https://docs.collectivex.health/api-reference/async-jobs/submit-a-question-as-an-async-job-asynchronous-v1query#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://docs.collectivex.health/api-reference/async-jobs/submit-a-question-as-an-async-job-asynchronous-v1query#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Collective X API\",\"item\":\"https://docs.collectivex.health\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Submit a question as an async job (asynchronous /v1/query)\",\"item\":\"https://docs.collectivex.health/api-reference/async-jobs/submit-a-question-as-an-async-job-asynchronous-v1query\"}]},{\"@type\":[\"TechArticle\",\"APIReference\"],\"@id\":\"https://docs.collectivex.health/api-reference/async-jobs/submit-a-question-as-an-async-job-asynchronous-v1query#apireference\",\"headline\":\"Submit a question as an async job (asynchronous /v1/query)\",\"name\":\"Submit a question as an async job (asynchronous /v1/query)\",\"description\":\"Submit exactly the request body you would send to POST /v1/query and get a job id back immediately, instead of waiting for the grounded answer on an open connection. The answer is produced in the background and read with GET /v1/jobs/{id} — see the \\\"Async jobs\\\" tag description for the lifecycle, retention, and a polling recipe. Validation is identical to /v1/query, and happens now. The body must contain exactly one of question or messages (one user message), may set response_format, and may not contain anything else. Every 400 this endpoint returns — unknown field, both/neither shape, empty or whitespace-only question, a message with a file part, a non-user role — is one /v1/query would have returned for the same body. Nothing is accepted here only to fail an hour of polling later. One extra rule applies only here: the body is persisted and echoed back on every poll, so it is capped at 32 KiB of JSON. What runs, and when. On 202 the job is queued and starts running immediately, in the background. It is the same retrieval + safety + generation pipeline /v1/query uses, with the same published tone and the same deferral/crisis behaviour — so the eventual result is exactly the QueryResponse you would have received synchronously. Expect the job to finish in the same 10–40 s a synchronous call takes; a job is never faster than the sync call, it just doesn't make you wait on the socket. Metering. Submitting is one metered request against your per-key budget (the same pool as /v1/query, /v1/chat, and polling). The background execution is not metered again. Server-to-server only, with your long-lived key. A session token gets 403 forbidden here, exactly as on GET /v1/jobs/{id}. Never embed your long-lived key in a client.\",\"url\":\"https://docs.collectivex.health/api-reference/async-jobs/submit-a-question-as-an-async-job-asynchronous-v1query\",\"mainEntityOfPage\":{\"@id\":\"https://docs.collectivex.health/api-reference/async-jobs/submit-a-question-as-an-async-job-asynchronous-v1query#webpage\"},\"dateModified\":\"2026-09-13T05:30:51.784Z\",\"publisher\":{\"@id\":\"https://docs.collectivex.health/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.collectivex.health#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"Collective X API\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"Collective X API","nav":{"global":null,"tabs":[{"tab":"API Reference","groups":[{"group":"Ask a question","pages":[{"title":"Ask a question — one-shot JSON answer","deprecated":null,"version":null,"openapi":"/openapi.yaml post /v1/query","href":"/api-reference/ask-a-question/ask-a-question-—-one-shot-json-answer"}]},{"group":"Have a conversation","pages":[{"title":"Have a conversation — streaming or sync","description":"Dual-mode endpoint. By default (`stream` omitted or `true`) returns a streaming\nUIMessage SSE response compatible with the Vercel AI SDK `useChat` hook. Send\n`stream: false` to receive a synchronous JSON response (the `SyncResponse` shape)\nsuited for Claude Skills, MCP tool calls, and agent pipelines.\n\n**Server-to-server (the only supported pattern for web), or\nnative-mobile-direct via a short-lived session token** minted by\nyour backend through `POST /v1/session-tokens` (native mobile only,\nnot browser/JS — see \"API keys\" above). Never embed your long-lived key in any client,\nbrowser or mobile.\n\nRate limits are communicated via `X-RateLimit-*` headers on every\nresponse and are shared with `/v1/query` per API key\n(see \"Rate limits\" above). When a 429 is returned, honour\n`Retry-After` before retrying.\n","deprecated":null,"version":null,"openapi":"/openapi.yaml post /v1/chat","href":"/api-reference/have-a-conversation/have-a-conversation-—-streaming-or-sync"}]},{"group":"Async jobs","pages":[{"title":"Submit a question as an async job (asynchronous /v1/query)","deprecated":null,"version":null,"openapi":"/openapi.yaml post /v1/jobs","href":"/api-reference/async-jobs/submit-a-question-as-an-async-job-asynchronous-v1query"},{"title":"Poll an async job's status","description":"Read-only poll for one async job's current state. Jobs are\ncreated by `POST /v1/jobs` — see the \"Async jobs\" tag for the\nlifecycle, retention, and polling guidance.\n\n**Reading past the deadline.** If the job is still `queued` or\n`running` when its `expires_at` has passed — or it has been\n`running` for more than 2 minutes — this read marks it `expired`\nbefore returning it, so you never see a stale in-flight state.\nA job that had already finished (`succeeded` / `failed` /\n`expired`) is returned as it finished **until `expires_at`**;\nthe first read after that answers `404 not_found`, clears the\nstored request and result, and the row is deleted within ~24 h.\nFetch your result before `expires_at`.\n\n**Transient errors while polling.** A `429`, or a `503` that\ncarries `Retry-After`, says nothing about the job: wait\n`max(Retry-After, 1)` seconds and poll again.\n\n**Server-to-server only, with your long-lived key.** A short-lived\nsession token (see \"Mobile-direct access\" above) cannot call this\nendpoint: its scope covers only\n`/v1/query` and `/v1/chat`, so it gets `403 forbidden` here like on\nevery other endpoint. Do NOT call this endpoint from browser-side\ncode, and never embed your long-lived key in any client.\n\nScoping: a job is visible to every long-lived key of the partner\nthat created it (ownership is per partner, not per key). A job id\nthat doesn't exist and a job id that belongs to a different\npartner both return the identical `404 not_found` — this endpoint\nnever confirms another partner's job id exists.\n\nRate limits are communicated via `X-RateLimit-*` headers on every\nresponse and are shared with `/v1/query` and `/v1/chat` per API\nkey (see \"Rate limits\" above). When a 429 is returned, honour\n`Retry-After` before retrying.\n","deprecated":null,"version":null,"openapi":"/openapi.yaml get /v1/jobs/{id}","href":"/api-reference/async-jobs/poll-an-async-jobs-status"}]},{"group":"Session tokens","pages":[{"title":"Mint a short-lived, scoped session token for a mobile client","deprecated":null,"version":null,"openapi":"/openapi.yaml post /v1/session-tokens","href":"/api-reference/session-tokens/mint-a-short-lived-scoped-session-token-for-a-mobile-client"}]}]},{"tab":"Guides","pages":[{"title":"Guides","href":""}]}]},"title":"Submit a question as an async job (asynchronous /v1/query) - Collective X API","buildId":"dpl_2xiSHD9XEvLsPGPcfoMtkLTB3dy9","deploymentId":"dpl_2xiSHD9XEvLsPGPcfoMtkLTB3dy9","dynamics":["c2aa737661","2b790eda92","b0f84f53ff","a9bb46feb3"],"chunks":["OpenApiReferenceProvider-DmVyH30O.js","utils-BHQqGGOd.js","cloneDeep-izs3Hzxb.js","_Uint8Array-BVIqJ90l.js","isObjectLike-C8vmzPEN.js","isArray-Dcu1dQ5t.js","utils-CD-E4BIt.js","toString-BX-iyh8G.js","isSymbol-CSH9U2Gm.js","addUniqueKeysToSchema-Bp5Lq9Ds.js","snippetPresets-C4xAVCFP.js","style-to-object-BY3JEAdk.js","v4-DIgz8zy6.js","ApiExamples-CgXVLxUC.js","isEqual-BfaiBbcl.js","usePlaygroundInputsStore-BF8I5oUf.js","shallow-Byd0lWnC.js","getApiPlaygroundDisplay-BGOZG1OK.js","CodeSnippet-CpvQL0CY.js","CodeSnippets-B8wkBKXa.js","Page-CBCH2P7b.js","getPastedJson-Ry3scI3Y.js","icon-D7a9HcdD.js","MethodPill-CJ8pmHhz.js","DuplicateIcon-rh1HJF0t.js","createIcon-DvGznqYs.js","DynamicMarkdown-Cc6AuSHS.js","SectionHeading-B4A0S-ep.js","axios-vUo_bRtp.js","Markdown-Cc16CX65.js","clsx-Cs6nMGht.js","lib-DuNMoZ05.js","lib-B8HbPgcC.js","html-void-elements-mCSPHezU.js","dist-zbf22OZA.js","katex-YMM-FMeD.js","marked.esm-CZWythHk.js","AssistantPageLink-C_fs-y5n.js"]}