Skip to main content
One search concept, two implementations, two endpoints — the endpoint picks the implementation, so neither request body carries an implementation selector. Deep search separately accepts a quality/cost tier for its main agent:
  • Deep search (deep-search) runs an agent loop — the agent searches the index, reads and queries files, lists folders, and runs read-only commands, deciding what to retrieve next. It can search for minutes and can be continued as a conversation.
  • Fact search (fact-search) runs a fixed pipeline instead: one retrieval pass (hybrid search, plus the knowledge graph when the workspace has one), with reciprocal-rank fusion when both legs return candidates. A workspace without KG candidates keeps the hybrid ranking unchanged. The pipeline then directly reads matched pages or reuses retrieved passages for prose (specialized engines remain for tables and images), followed by one answering pass. It returns in seconds and is single-shot — there is no conversation to continue.
Both return the identical result shape (an IntelligentSearchResult on a job with kind: "intelligent_search"), so nothing but latency and thoroughness changes for a caller. Use fact search for factual lookups the documents answer directly; use deep search for multi-step questions that need an agent to chain several retrievals.
The former combined endpoint POST /v1/workspaces/{id}/intelligent-search (implementation selected by an effort field) has been removed. Calls to it return 404; select the implementation by calling one of the two endpoints below instead.

Query parameters:
  • wait_seconds (optional, default 0, max 300) — Most runs take longer than any sync window; callers normally poll the returned job.
Headers:
  • Idempotency-Key (optional)
Request body:
Body fields:
  • query (required, max 4096 chars) — The question to answer.
  • context (optional, max 20000 chars) — Free-form background for the agent. Rides the LLM prompt only; does not change the access filter or which files are retrieved.
  • tier (optional, default low) — Quality/cost tier for the main agent: low uses navi-dev, medium uses qwen3p8-27b, and high uses kimi-k3. The tabular QA engine behind query_tables answers on the same model, so a question over spreadsheets is reasoned about and written into SQL by one engine. Other supporting model calls inside retrieval tools retain their own defaults. A tier whose model is unavailable in the deployment returns 422; it never silently falls back.
  • allow_clarification (optional, default false) — Let the agent finish the run with one clarifying question instead of an answer, when the query is genuinely ambiguous and the ambiguity materially changes the answer. The rejected result carries the question in clarification (with answer, interpretation and reasoning null and evidences empty); answer it by re-asking in the same thread via the result’s session_id. Off — the default — the agent always answers with its best reading of the query, stated in the result’s interpretation.
  • path_prefix (optional) — Restrict all tool calls to files under this prefix. Composes with the access filter (intersection). Omit for the whole workspace.
  • session_id (optional) — Continue a prior conversation. Pass the session_id returned by an earlier result to add a follow-up in the same thread. Omit to start a new thread. Only the API key that started the thread may continue it.
Response (202 Accepted): A Job with kind: "intelligent_search".

Query parameters:
  • wait_seconds (optional, default 0, max 300) — Fact search typically finishes in seconds, so a wait_seconds of 60 usually returns the terminal job inline.
Headers:
  • Idempotency-Key (optional)
Request body:
Body fields:
  • query (required, max 4096 chars) — The question to answer.
  • context (optional, max 20000 chars) — Free-form background for the answering pass. Rides the LLM prompt only; does not change the access filter or which files are retrieved.
  • path_prefix (optional) — Restrict retrieval to files under this prefix. Composes with the access filter (intersection). Omit for the whole workspace.
  • top_k (optional, 1–50, default 6) — How many evidences to return, best first. A cap on the answer’s citation list, not on how much the pipeline reads: retrieval width is fixed, so a larger top_k returns more of what was already found rather than searching further. Values far above the default trade citation count for reliability — the whole list is composed in one pass.
There is no session_id field — fact search keeps no conversation, its result always carries session_id: null, and a body naming one is refused with 422. There is no allow_clarification either: the fixed pipeline has no agent who could ask back, so a fact-search run always answers and its result always carries clarification: null. top_k is fact-search only: the agent loop authors its own evidence list, so deep search refuses the field rather than accepting and ignoring it. Response (202 Accepted): A Job with kind: "intelligent_search" — the same job and result contract as deep search.

Result

Poll GET /v1/jobs/{job_id} until status is "succeeded" or "failed". The result shape is identical for both endpoints. On success, result is an IntelligentSearchResult:
Result fields:
  • evidences — Ordered by importance. Each SearchEvidence item:
    • source_path — The uploaded file the quote belongs to (e.g. reports/policy.pdf). This is the file to ground the quote against. Retrieval happens over workspace-internal representations of your files, but those are an implementation detail — every citation is resolved server-side to the uploaded file it belongs to, and a citation that cannot be resolved to a file visible to you (deleted mid-run, access-refused, hallucinated) is dropped rather than published under a path you cannot use.
    • source_file_id — The id of the file at source_path, resolved server-side. Always set on newly finalized results; null only on results stored before unresolvable citations were dropped.
    • page (optional) — 1-based page number, when one was seen.
    • quote — The key passage, as retrieved from the file.
    • relevance — A one-sentence account of how this supports the query.
  • reasoning — The run’s account of how the pieces of evidence chain together.
  • answer — A direct answer to the query, grounded in the evidences. Every run answers: the field is always set on a newly finalized run, null only when the run rejected the query with a clarification (and on results stored before answers became unconditional).
  • interpretation — The run’s reading of your query: its assumptions, how it resolved ambiguous terms, the period or scope it took as implied. Stated on every answered run, even one that took the query at face value — no interpretation is silent, so a misreading is visible and you can re-query with the ambiguity resolved. null on runs rejected with a clarification.
  • clarification — Non-null exactly when the run rejected the query as genuinely ambiguous instead of answering — only possible when the request set allow_clarification (deep search only): one direct question back, with answer, interpretation and reasoning null and evidences empty. Answer it by re-asking in the same thread via the result’s session_id. Fact search never clarifies — always null there.
  • coverage — Access-label reporting: how many candidates were withheld and which labels would unlock them.
  • steps_used — Number of retrieval steps: agent tool calls for deep search, logical pipeline steps for fact search.
  • exhaustedtrue when a deep-search step budget ran out before the agent finished — still a successful response, not a failure. Always false for fact search, whose pipeline has no step budget.
  • tool_calls — One entry per retrieval step. Each ToolCallTrace: tool (tool name), arguments_digest (hex digest of the arguments — not raw arguments, to avoid a side channel around coverage), hits (result count), duration_ms (wall time).
  • session_id — The conversation ID for a deep-search run; pass it as session_id on the next deep-search request to continue the thread. Always null for fact search.

Locating a quote in the original document

Both searches are retrieval only — they never ground. The evidence tells you which uploaded file a quote belongs to (source_path) and, when seen, its page — but not where on the page it sits. When you need a region (page + bounding box for visual formats, sheet/range for spreadsheets), ground the citations you care about yourself with document-operations ground. The evidence carries both halves of that call: the file to read (source_file_id, the id of the file at source_path) and the text to locate (quote).
One call covers up to 30 quotes you took from the same file — targets batches under a single source, capped at 30, so a file you cited more often than that takes a second call for the remainder — and the cited page is a useful hint. A quote is retrieved from a parsed rendering of the file, so it may carry markdown the uploaded document does not contain (table tags, escaped entities, --- Page N --- markers); strip that before sending it as target text. Doing this outside the search job means you pay for locators only on the evidence you actually display, and the search job finishes as soon as the run does.

Multi-turn sessions (deep search only)

A session is a persistent conversation thread. The session_id from any deep-search result is its thread ID. To ask a follow-up:
The same loop answers a clarification: when a result carries a non-null clarification instead of an answer, send the missing detail as the next query on the same session_id and the agent resumes with the thread’s full context.
  • A thread is only accessible by the API key that started it.
  • Sending a follow-up while the previous turn is still running returns 409 session_busy.
  • A fact-search job’s id anchors no thread: passing one as a deep-search session_id returns 404 session_not_found.

Errors

  • invalid_request (422) — An unknown tier, a tier whose mapped model is unavailable in this deployment, or a fact-search body with a session_id (fact search keeps no conversation).
  • session_not_found (404) — No deep-search session with that ID in this workspace, or the ID belongs to a follow-up job (not the thread root) or to a fact-search job.
  • session_busy (409) — The session is still answering a previous turn. Wait for that job to finish.
  • access_denied (403) — Session belongs to a different API key.