---
title: "deep_search"
method: POST
path: "/v1/workspaces/{workspace_id}/deep-search"
tags: ["intelligent-search"]
deprecated: true
---

# deep_search

`POST /v1/workspaces/{workspace_id}/deep-search`

## Request

- Base URL: `https://ndi-api.nace.ai` (the document declares no server; this is the origin it was published from)
- URL: `https://ndi-api.nace.ai/v1/workspaces/{workspace_id}/deep-search`
- Auth: API key in header `X-API-Key`
- TypeScript: <https://skmtc.dev/nace/apis/ndi-service/docs/v1/workspaces/:workspace_id/deep-search/post.ts> (a function with typed inputs; its response is checked against the schema)

> **Deprecated.**

Run an agentic search over a workspace. Cost class: **job**.

The caller receives a ``202`` with a queued
:class:`~ndi_service.app.platform_api.results.Job` immediately, and polls
``GET /jobs/{id}`` for the result once the job succeeds.

Every run uses one configuration: a reasoning engine with a thinking
budget, the internal CLI tool surface, and the workspace's knowledge graph
when one is built (the 2026-09 five-lane evaluation picked it as the most
accurate lane).

One implementation serves every scope: the source-level
agent (``deep_search_v2``). It browses, reads, and computes over the
uploaded files exactly as an API caller would, writes its own SQL over the
tables in scope, and every executed statement lands on the result as a
receipt (``sql_receipts``) — a figure and the query behind it travel
together. (Jobs created before the 2026-09 consolidation may carry the
retired classic loop's ``intelligent_search`` kind; they stay readable via
``GET /jobs``.)

Scope never changes the configuration: ``paths`` scopes the run to exactly
those uploaded files — every tool the agent runs (search, listings, reads,
ask-file, SQL, the shell's ``ndi`` commands) sees only them and the
content ingested from them, enforced server-side against the stored job.
Each entry must name a visible uploaded source file (``file_not_found`` /
``access_denied`` otherwise, before any job is created). With ``path_prefix``
the two add up: the run covers the folder's files plus the named ones.

Pass the result's ``session_id`` to ask a follow-up. With the same
folder/file scope and access grants, the agent resumes that transcript.
Changing either starts a fresh conversation and returns its new
``session_id``, so prior tool results cannot cross the new scope boundary.
Older transcripts without a scope snapshot also restart. Classic thread
ids return ``session_not_found``; retrying the original request with its
idempotency key still returns that historical job.

``allow_clarification`` lets the agent ask instead of guessing on a genuinely
ambiguous query: the succeeded result then carries the questions in
``clarification_questions`` (and as text in ``clarification``) with
``answer`` null. Answer them in the same thread — send the result's
``session_id`` with the same ``query`` and ``clarification_answers``, or with
any refined query. Off (the default) the agent never asks back.

Retrieval only: this job never grounds.  To pin a quote to a region of its
source document, call document-operations ``ground`` on the file the
citation names with its quote as the target text — separately, and only
for the citations you actually need a locator for.

``exhausted: true`` means the step budget ran out before the agent
finished — a successful, partial response, not a failure.

Args:
    body: Search parameters: query, context, allow_clarification,
        clarification_answers, path_prefix, paths, session_id.
    context: Resolved workspace plus the caller's access predicate.
    session: Database session.
    wait_seconds: Bounded inline wait (``0`` → immediate ``202``).
    idempotency_key: Replaying with the same key returns the original job.
    response: Injected by FastAPI for status-code management.
    temporal: Temporal client used to dispatch the workflow.
    search_model: Internal engine pin; overrides the configuration's engine,
        never its tooling.
    search_playbook: Internal procedure pin; retired with the classic loop — refused.
    report_usage: Internal usage-accounting switch.
    postprocessing: Internal optional-postprocessing mode; ``none`` skips
        the detached presentation/grounding sidecars after commit.

Returns:
    A queued :class:`~ndi_service.app.platform_api.results.Job`.  On
    success the result is a
    :class:`~ndi_service.app.platform_api.results.DeepSearchV2Result`,
    naming itself in ``result_type``.

## Path parameters

- `workspace_id` string, uuid, required: Workspace identifier.

## Query parameters

- `wait_seconds` integer: Bounded inline wait. 0 (the default) returns 202 immediately. Above 0 the route returns 200 with a terminal job if the work finished in time, and 202 with the still-running job if it did not. Either way, read `status`.

## Headers

- `Idempotency-Key` string, nullable: Replaying a request with the same key returns the original job with 200, never a second job. The key is kept with the job for replay and never cleared, so it must not contain personal data.

## Request body

- DeepSearchRequest: Body for ``POST /workspaces/{workspace_id}/deep-search`` — every agentic search. Every run uses one configuration (see :mod:`~ndi_service.app.platform_api.intelligent_search.effort`), so no body field selects a model, a tool surface, a reasoning budget, or an effort level. ``effort``, ``use_kg`` and ``include_answer`` stay on the body only because published SDK, CLI and MCP releases still send them (see :data:`RetiredEffort`): deprecated, accepted, and ignored. ``tier`` never shipped in a published client and stays rejected by the strict base. Scope never changes the configuration (NDI-573): every run — scoped by ``paths``/``path_prefix`` or not — gets the same source-level configuration. The folder and file selectors resolve to one file set that every tool enforces server-side against the stored job.
  - `query` string, required: What to answer, in the caller's own words. Stripped before its length is checked; a blank query is refused rather than run.
  - `context` string, nullable: Free-form background for the model — the engagement, the entity, what the caller already knows. It informs the answer; it does not widen or narrow which files the caller may see.
  - `path_prefix` string, nullable: Put every uploaded file under one folder subtree, workspace-relative, in the run's scope; `paths`, where the body takes it, adds more files. Omit both for the whole workspace.
  - `allow_clarification` boolean: Let the agent finish the run with clarifying questions instead of an answer, when the query is genuinely ambiguous and the ambiguity materially changes the answer. The result then carries the questions in `clarification_questions` (and as text in `clarification`), with `answer` and `reasoning` null and `evidences` empty; answer them in the same thread via the result's `session_id`, with `clarification_answers` or a follow-up query. Off, the default, means the agent always answers with its best reading, stated in the answer.
  - `clarification_answers` ClarificationAnswer[], nullable: Answers to the clarifying questions this thread's last turn asked (its result's `clarification_questions`): one entry per question, in order. Send them with that turn's `session_id` and its `query`; the agent then answers that query with the answers applied, and sees which answer belongs to which of its questions. Any other follow-up query in the thread works too — this field is the structured way to answer. Cannot be combined with `allow_clarification`: a run answering questions does not ask again.
    - `question` string, required: The question being answered, as the asking result's `clarification_questions[].question` states it.
    - `answer` string, nullable: The answer: one of the question's `options`, or your own words. Null skips the question; the agent then uses its best judgment and states the assumption in its answer.
  - `effort` 'high' | 'medium' | 'low', nullable: Deprecated and ignored: the service resolves the run's configuration itself. Accepted so clients released before the effort levels were retired keep working; omit it.
  - `use_kg` boolean, nullable: Deprecated and ignored: the service decides whether the knowledge graph is consulted. Accepted so clients released before it was retired keep working; omit it.
  - `include_answer` boolean, nullable: Deprecated and ignored: every run answers. Accepted so clients released before it was retired keep working; omit it.
  - `paths` string[], nullable: Scope the run to exactly these uploaded files. Every retrieval tool the agent runs — search, listings, reads, table queries, the shell — sees only these files and the content ingested from them; everything else is invisible to the run. Each entry names an uploaded source file (never a folder — scope folders with path_prefix; the two add up, so the run covers the folder's files plus these). No limit on how many. Unknown or inaccessible paths are refused up front.
  - `session_id` string, uuid, nullable: Continue a prior deep-search conversation: the id echoed on an earlier result's `session_id`. With the same folder/file scope and access grants, the agent resumes that thread's context. A changed scope or grants starts a fresh conversation and returns a new `session_id`; older threads without a scope snapshot also restart. Omit to start a new thread. Only the API key that started the thread may continue it; a thread whose turn is still running returns 409 session_busy.
  - `surface` 'deep_search' | 'qa' | 'workbook': Which console history this run belongs to. It does not pick the agent, the chassis, or the engine: an omitted value is `deep_search` (the public API and the Deep Search page). The NDI console sends `qa` or `workbook` so Activity and thread lists can split those skins. A follow-up (`session_id`) inherits the thread head's surface; a conflicting value on the follow-up body is ignored.
  - `output_schema` object, nullable: A JSON Schema (Draft 2020-12, object root, local non-recursive `$ref` only, no `pattern`, `patternProperties` or `unevaluated*`) the answer must follow. When set, the agent writes `answer` as JSON text conforming to it, the run checks that and asks the agent to correct a mismatch, and the result carries the parsed value in `structured_answer`. A field the source does not state comes back null, whatever type the schema gives it. Explanation goes to `reasoning` where the result has one, and evidences are unchanged. If the answer still misses the schema, the run succeeds with `answer` as written and `structured_answer` null. Omit for a prose answer.

## Response `202`

Successful Response

- DeepSearchJob: The consolidated deep-search route's response shape. One route, one public ``effort`` knob, one implementation: every new run — scoped or not — lands the source-level ``deep_search_v2`` result (answer plus SQL receipts). ``intelligent_search`` stays in the ``kind`` union only so jobs created by the retired classic loop remain readable via ``GET /jobs``; ``result_type`` inside the result repeats the discriminator, so a caller reading only the result payload can still tell them apart.
  - `job_id` string, uuid, required: Server-minted id. Poll GET /v1/jobs/{job_id} or pass wait_seconds on create.
  - `kind` 'intelligent_search' | 'deep_search_v2', required: deep_search_v2, the source-level agent — every new run, scoped or not. intelligent_search appears only on deep-search jobs created before the 2026-09 consolidation retired the classic scoped loop; those stay readable here.
  - `status` 'queued' | 'running' | 'succeeded' | 'failed' | 'cancelled', required: ``cancelled`` is a first-class terminal status, not a ``failed`` variant. Conflating them makes error-rate metrics lie and forces every client to string-match an error code.
  - `name` string, nullable: Display name (the console's 'project'). Public Parse, Split, Classify, Extract, and Ground runs supply a stable server default when blank; citation-generated Ground jobs are excluded. mutable via PATCH /v1/jobs/{job_id}.
  - `project_id` string, uuid, nullable: Caller-minted grouping id shared by jobs submitted together. Filterable on GET /v1/jobs.
  - `workspace_id` string, uuid, nullable: The workspace this job reads. Null for a document operation whose source is an upload, a URL, or a prior parse result.
  - `idempotency_key` string, nullable: Key that was replayed to return this job. Absent when the create call had no Idempotency-Key.
  - `force` boolean, nullable: The request's own `force` flag, echoed regardless of outcome. Present for kinds whose request carries one (e.g. kg_build, where a true value cleared the durable store before the job ran) so a caller can tell that apart from a build that failed leaving the prior store untouched — `result` alone cannot, since it is absent on failure either way.
  - `source` union: What this job read, for the document operations that take a single `source` (parse, split, classify, extract, ground); null for every other kind. Together with `workspace_id` this is what lets a caller re-open the document a past run was about — both `workspace_file` and `upload` sources remain until explicit deletion. Read from the stored request, so it rides the same retention path as `query` and `result`: null once `result_state` leaves `available`.
    - UploadSource: Bytes staged through ``POST /v1/uploads``. Retained until explicit deletion, except under Zero retention.
      - `type` 'upload'
      - `upload_id` string, uuid, required: Id returned by POST /v1/uploads. Ordinary bytes remain until their consuming jobs or tenant are deleted.
    - UrlSource: A public or presigned HTTPS location we fetch server-side.
      - `type` 'url'
      - `url` string, required: HTTPS URL the service fetches.
      - `file_name` string, required: Filename to record for the fetched bytes.
    - WorkspaceFileSource: A file already in a workspace. The single seam between the two contexts: a granular operation may read a workspace file and still write nothing back. The file need **not** be ingested — granular operations read source bytes, not representations.
      - `type` 'workspace_file'
      - `workspace_id` string, uuid, required: Workspace that owns the file.
      - `file_id` string, uuid, required: File id from GET /v1/workspaces/{workspace_id}/files.
    - ParseResultSource: A prior Parse job whose parsed artifact can be reused by downstream methods.
      - `type` 'parse_result'
      - `job_id` string, uuid, required: Succeeded parse job whose artifact should be reused.
  - `query` string, nullable: The question this job was asked, for the search kinds that carry one (intelligent_search, qa_file, qa_tables, filtered_search); null for every other kind. Read from the stored request, so it rides the same retention path as `result`: null once `result_state` leaves `available`. Narrow by design — the frozen access context never reaches the wire. File scope is summarised separately as `query_scope`.
  - `query_scope` union: Where this search ran, summarised for history UIs: the whole workspace, one file path, one folder prefix, or a count of files. Null for non-query jobs, filtered-search page continuations, and once `result_state` leaves `available`. Never lists multiple filenames, never the raw request, and never the frozen API-key id.
    - WorkspaceQueryScope: The question ran against the whole workspace.
      - `type` 'workspace'
    - FileQueryScope: The question ran against one workspace-relative file.
      - `type` 'file'
      - `path` string, required
    - PathPrefixQueryScope: The question ran against one folder subtree.
      - `type` 'path_prefix'
      - `prefix` string, required
    - FilesQueryScope: The question ran against several files; only the count is public.
      - `type` 'files'
      - `count` integer, required
  - `effort` string, nullable: How hard this run worked, for the search kinds that carry it. A deep-search job (kind deep_search_v2) created before the effort knob was removed echoes its requested level: 'low', 'medium', or 'high'; newer deep-search jobs, which all run one configuration, read null. An intelligent_search row carries an internal selector instead: 'fast' is fact-search, 'automatic' is automatic-search, and 'deep' (or the legacy 'balanced') is a deep-search or console QA chat job created before the 2026-09 consolidation — those two are not distinguishable by this field. Null for every other kind. Read from the stored request, so it rides the same retention path as `query` and `result`: null once `result_state` leaves `available`.
  - `surface` 'deep_search' | 'qa' | 'workbook', nullable: Which console history this deep-search job belongs to. `deep_search` is the public route and the Deep Search page; `qa` and `workbook` are the console chat skins over the same agent. It does not pick the implementation. Null for fact-search and every non-deep-search kind. Jobs stored before this field existed echo `deep_search`. Read from the stored payload, so it rides the same retention path as `query`.
  - `progress` JobProgress: Absent until the job is ``running``.
    - `units_total` integer, nullable
    - `units_done` integer
    - `items_total` integer, nullable: Files, for batch kinds.
    - `items_done` integer
    - `items_failed` integer: Quarantined after exhausting retries, for batch kinds. Excluded from items_done.
    - `stage` string, nullable: Current pipeline stage, for display only.
    - `knowledge_graph` KnowledgeGraphBuildProgress: Where a ``kg_build`` is, beyond what ``stage`` alone can say. A graph build runs for minutes to hours through a fixed sequence of phases, and ``stage`` names only the current one. Turning that into "how far along" otherwise means hard-coding the build engine's phase order in every client — an order that lives in another package and is free to gain a phase. So the position is reported rather than inferred. The counters are deliberately not a percentage. The phases are wildly unequal (extraction is ~60% of a build, and its per-document cost varies 25x with how dense the documents are), so weighting them is a presentation decision, made against measurements, that belongs to whoever is drawing the bar.
      - `phase_index` integer, required: 0-based position of the current phase in the build's fixed run order; equals phase_count when done.
      - `phase_count` integer, required: How many phases a build passes through before it is done. Currently 9.
      - `countable` boolean, required: Whether items_total/items_done describe this phase. False for the phases that are a single step, which have no unit to count — draw those as indeterminate rather than as a bar stuck at zero.
      - `documents` integer, nullable: Documents registered from the corpus, once the registering phase has run.
      - `mentions` integer, nullable: Mentions extracted across the corpus, once the validating phase has run.
      - `step` string, nullable: The span in flight inside a single-step phase, reported by the running step itself. Null when the phase counts its own units, or before the step has reported one.
      - `step_done` integer, nullable: Units of `step` finished, when it has countable units.
      - `step_total` integer, nullable: Units `step` has to get through, when that is known.
    - `routing` AutomaticSearchRouting
      - `selected` 'fact' | 'agent' | 'deep' | 'filtered', required: The route the router chose, the one the request's `mode` named, or `fact` when nothing was searched: the workspace had no ingested files, or the query asked for nothing that a document contains.
      - `final` 'fact' | 'agent' | 'deep' | 'filtered', required: The pipeline that produced the returned output. It differs from `selected` only in `auto` mode, when the router chose fact search, fact search could not settle the question, and the agent answered instead.
      - `reason` string, required: The router's explanation of its decision, or why no router ran.
      - `router_model` string, required: The model used to choose the search pipeline; `none` when no router ran.
      - `router` union: Which router decided, and what it measured: an LLM (its explanation is ``reason``), NaceDM (its probability and decision point), or `requested` when no router ran: the request's `mode` was `fact` or `agent`, or the workspace had no ingested files. Results routed before this field existed read as the LLM.
        - AutomaticLlmRouter: An LLM chose the route. Its explanation is the routing's ``reason``.
          - `kind` 'llm'
        - AutomaticNaceDmRouter: NaceDM, the decision model, chose the route by scoring how likely the query needs the agent.
          - `kind` 'nacedm'
          - `p_agent` number, nullable, required: NaceDM's probability that the query needs the investigating agent rather than one fact-search pass; 1 - p_agent is the probability that fact search settles it. Null when the request was too long for NaceDM to score, which routes to the agent.
          - `decision_point` number, required: The agent runs when ``p_agent`` is at or above this; fact search runs below it.
        - AutomaticRequestedRouter: No router ran: the request's ``mode`` named the route, or the workspace had no ingested files.
          - `kind` 'requested'
      - `router_wall_ms` integer, nullable: Wall time spent selecting the initial route.
      - `fact_wall_ms` integer, nullable: Wall time spent running fact search, when selected.
      - `delegated_wall_ms` integer, nullable: Wall time spent preparing and running the native deep or filtered job.
  - `result` union: Present only on succeeded, and only while retained.
    - IntelligentSearchResult
      - `result_type` 'intelligent_search'
      - `claims` DeepSearchVerifiedClaim[]: Calculated answer values bound to recorded executions.
        - `claim_id` string, required: Unique id of this calculation within the result.
        - `answer_excerpt` string, nullable: Exact displayed answer value selected for its calculation citation.
        - `answer_occurrence` integer: Zero-based occurrence of answer_excerpt in the answer.
        - `evidence_indices` integer[]: Source evidence indices supporting the calculation inputs.
        - `claim` string, required: The method or formula behind the calculated value, as the agent wrote it.
        - `reference` DeepSearchClaimReference, required: A claim's address within a published SQL or command execution receipt.
          - `kind` 'result_cell' | 'result_count' | 'result_absence' | 'command_result', required: How the result supports the claim.
          - `result_id` string, required: The matching result_id in sql_receipts or command_receipts.
          - `row` integer, nullable: 0-based result row for a cell claim; null otherwise.
          - `column` string, nullable: Result column for a cell claim; null otherwise.
          - `cited_value` string, required: The exact value checked against the retained result.
      - `command_receipts` CommandReceipt[]: Successful shell calculations cited by this answer.
        - `result_id` string, required: Job-scoped identity of a recorded shell execution.
        - `command` string, required: The shell command as executed.
        - `stdout` string, required: The complete standard output of the successful command.
        - `outputs` object, required: Named scalar outputs parsed from stdout; a scalar output is named value.
      - `sql_receipts` SqlReceipt[]: SQL executions supporting calculated answer claims.
        - `result_id` string, required: Stable per-run identity of this statement, in execution order (e.g. 'sql_3').
        - `sql` string, required: The statement as executed, verbatim.
        - `handle` string, nullable: Content-addressed key of this statement's stored result, derived from the statement text. Server-side identity: it is what lets the run's own publication find the stored result and its capture-time provenance, and it is NOT an address an agent may query or chain from — `result_id` is the identity the caller and the row pager use. Null on a run that stored no result for the statement. A `cat_` handle names an exploratory document-catalog query: it stores no result beyond the rows carried here.
        - `table_bindings` SqlTableBinding[]: The tables this statement registered, each with the view name it was referenced by.
          - `path` string, required: The uploaded table file this view read, e.g. 'ledger/journal.xlsx'.
          - `component` string, nullable: The source worksheet, including when selected alone. Null for a direct upload or a historical unnamed tab.
          - `table_name` string, required: The view name the SQL referenced this table by, for THIS run.
          - `source_sha256` string, nullable: SHA-256 identity of the ingested source generation behind this table binding.
        - `replay` SqlReplayReceipt: Immutable inputs and engine identity for independent result recomputation.
          - `schema_version` 1
          - `kind` 'aggregate' | 'rowset', required: The verification lane appropriate for this statement.
          - `engine` 'duckdb', required: The query engine that executed the statement.
          - `engine_version` string, required: The query-engine version that executed the statement.
          - `engine_threads` integer, required: DuckDB thread count used for this execution.
          - `normalized_sql` string, required: The normalized single SELECT statement to execute during independent replay.
          - `sql_sha256` string, required: SHA-256 identity of the normalized statement.
          - `logical_plan_sha256` string, required: SHA-256 identity of the parser tree after source-position fields are removed.
          - `catalog_sha256` string, required: SHA-256 identity of the ordered source bindings used by this statement.
          - `result_artifact_sha256` string, required: SHA-256 identity of the complete retained result Parquet produced by the service.
          - `result_data_sha256` string, required: SHA-256 identity of the complete typed result rows.
          - `result_digest_version` string, required: Versioned Arrow serialization scheme used for the typed-result identity.
          - `result_dependencies` string[]: Earlier retained result handles read by this statement, in deterministic order.
        - `columns` string[]: Result column names, in result order.
        - `rows` object[]: Result rows, values rendered as text so a decimal, a date and a NULL all survive the wire.
        - `row_count` integer: Rows carried below — not the statement's unbounded result size.
        - `result_row_count` integer: Rows in the statement's complete stored result. Equal to `row_count` when nothing was cut; the number a total or an exception count must be read from when `truncated` is set.
        - `truncated` boolean: Whether rows past the stored cap were cut from this receipt.
        - `grounding_status` 'exact' | 'unavailable' | 'zero_result', nullable: Whether this statement's stored result already carried proof of which source rows it read when the receipt was recorded. 'exact' means it did; 'unavailable' means the proof could not be read; 'zero_result' means there were no rows to ground. Null when nothing had been captured yet — the ordinary case for a lane that captures at publication time, so this is a record of what execution happened to know and NOT a prediction of whether the receipt can be grounded. What was actually proven is published as `grounding_status` on the matching evidence query, beside the `regions` it earned.
        - `views_used` string[]: Session views registered when this statement ran, in definition order — the registry snapshot it executed under. A statement naming a view is replayable only against these, which is what makes named views admissible where chaining a previous call's result handle is not.
        - `saved_as` string, nullable: The session view this statement was registered as, when it was. Every name in another receipt's ``views_used`` appears here on exactly one receipt, so the run's receipts define every view they cite.
        - `view_sql` string, nullable: The definition registered under `saved_as`, when it is not this statement itself. Null on an ordinary `save_as`, where `sql` IS the definition. Set by `build_journal_view`, which registers the per-journal frame and runs a bounded probe over it rather than materializing the whole frame — so the definition every later `views_used` refers to is still published by exactly one receipt.
        - `warnings` string[]: Pre-execution lint notes attached to this statement — non-blocking, and published as part of its working.
        - `executor` string, nullable: The named executor that generated this statement (`reconcile_movement`, `test_timing`, `find_duplicates`, `field_completeness`, `journal_balance`), when one did, `custom_sql` for a statement the fixed-pipeline engine authored under its escape, or `ledger_understanding` for the ledger-understanding census's own statement, which ran before this run and is cited rather than re-run. Null when no named generator produced it — a population count, an absence probe, or SQL the agent wrote itself. Both engines use the same words here.
        - `parameters` object: The executor's resolved call, flattened to text — the clock and its true grain, the window's day offsets, the signature, the tolerance, the population. Empty on a statement no executor generated. This is what makes a figure trace to a named, parameterized computation rather than to SQL alone.
      - `evidences` SearchEvidence[]: The evidence the agent found for the query, in order of importance.
        - `source_path` string, required: The uploaded file the quote belongs to, e.g. 'reports/policy.pdf'. This is the file to ground the quote against.
        - `source_file_id` string, uuid, nullable: 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` integer, nullable: 1-based page number, when the agent saw one.
        - `component` string, nullable: Public component of the file the quote came from — the tab name for a spreadsheet, the section for a split document; the same vocabulary file-metadata lists. Pass it as the ground target's sheet when grounding a spreadsheet quote. None for a single-component file or when attribution was unavailable.
        - `quote` string, required: The key passage, as retrieved from the file.
        - `passages` string[]: Contiguous pieces of the source file that quote was stitched from, in the file's own text. Ground each piece as its own target when this list is present, with max_matches 2. A piece that returns two matches repeats in the file, so which copy the quote used is unknown. A ' | ' marks a table cell boundary added by the check. Replace it with a space before grounding. Filled only for PDF, Word and PowerPoint sources that Ground reads from the PDF text layer. Absent when quote is already one contiguous passage, could not be checked, or could not be verified.
        - `relevance` string: Deprecated: empty on new results, where `reasoning` says how the evidences support the answer.
        - `queries` TableQueryGrounding[]: The SQL the run executed over this evidence's table, with a bounded preview of each result — server-recorded from the run's tables engine, never model-authored. Attached whenever the run queried the cited table (or the row extraction of the same spreadsheet tab), so a verbatim quote from a queried sheet carries the computation trail alongside it; empty when the run never queried the cited file's tables.
          - `sql` string, required
          - `supports_answer` boolean: False for exploratory SQL the tables engine ran but did not mark as part of the answer's calculation trail.
          - `result_id` string, nullable: Stable identity of the stored query result behind this receipt. Lets the grounding-rows endpoint replay this exact query for matched-row pages beyond the regions' inline runs.
          - `source_paths` string[]: The uploaded table files this query read — directly, or through a prior result it built on — e.g. 'planning/FS GF Custom.xlsx'.
          - `row_count` integer: Rows in the query's full result, not in the preview below.
          - `columns` TableQueryColumn[]
            - `name` string, required
            - `data_type` string, required
          - `rows` object[]: Leading result rows, values rendered as text — a bounded preview, never the full result.
          - `preview_truncated` boolean: Whether the preview cut rows or columns.
          - `verified` boolean: True when at least one claim behind this query passed mechanical verification against the source data. Independent of 'regions': a source without an addressable grid verifies but cannot be pointed at, so an empty 'regions' never means unverified.
          - `regions` union[]: Original-file coordinates for the verified claims whose source file has an addressable grid.
            - union
              - …
          - `grounding_status` 'exact' | 'unavailable' | 'zero_result', nullable: Capture-time provenance status behind this query's regions. 'exact': the exact-provenance sidecar proved every row the query read, so its 'provenance'-basis regions are trustworthy (source regions may be broader than the minimal contributing set when grounding_reason carries a 'regions_broad:…' qualifier). 'unavailable': no exact proof could be captured (a legacy result, or a statement shape the capturer cannot instrument) — no 'provenance'-basis regions are attached, never a guessed whole-table span. 'zero_result': the query matched no rows, so there is nothing to ground. None on legacy payloads and on a query with no unclaimed table left to ground.
          - `grounding_reason` string, nullable: Qualification on grounding_status, when present. Set for 'unavailable' (explains why proof was not possible) and for 'exact' when capture succeeded but the source regions are broader than the minimal contributing set (e.g. 'regions_broad:narrowing_specs_unavailable'). None when grounding is fully exact or absent.
        - `derivation` DerivationTrace: The answer's computation DAG when evidence is derived rather than extracted.
          - `steps` DerivationStep[]
            - `result_id` string, required
            - `sql` string, required
            - `kind` 'extraction' | 'transformation', required
            - `label` string, required
            - `preview_rows` object[]
              - …
            - `reads` string[]: result_ids this step depends on.
      - `reasoning` string, nullable: The agent's account of how the pieces of evidence chain together to address the query, including any assumption it made in reading the query.
      - `answer` string, nullable: A direct answer to the query, grounded in the evidences. Always present on a newly finalized run that answered; null when the run rejected the query with a `clarification`, and on results stored before answers became unconditional (the evidence-only mode).
      - `structured_answer` unknown
      - `clarification` string, nullable: Non-null exactly when the run rejected the query as genuinely ambiguous instead of answering — only possible when the request set `allow_clarification`: one direct question back to the caller, with `answer` and `reasoning` null and `evidences` empty. Answer it by re-asking in the same thread via the result's `session_id`.
      - `confidence` 'certain' | 'very_high' | 'high' | 'medium' | 'low' | 'speculative': How well the retrieved evidence supports the answer, worst-case wins.
      - `coverage` Coverage: What the access gate withheld, in counts and label names only. Never a name, path, or fragment of withheld content. Naming the labels leaks nothing the caller cannot already read from the workspace config, and it turns "some results were withheld" into an actionable request.
        - `restricted_candidates` integer
        - `labels_required` string[]: Which labels would have been needed to see the withheld candidates.
        - `request_access_hint` string, nullable
      - `candidates_seen` integer
      - `steps_used` integer
      - `exhausted` boolean: Budget hit before the loop was satisfied. A successful response, not a failure. When the agent authored no answer or reasoning, reasoning carries an actionable briefing and answer stays null so presentation does not treat the briefing as an answer.
      - `tool_calls` ToolCallTrace[]
        - `tool` string, required
        - `arguments_digest` string, required
        - `hits` integer, required
        - `duration_ms` integer, required
      - `session_id` string, uuid, nullable: The conversation this run belongs to. Send it back as the request's `session_id` to ask a follow-up in the same thread. Null only on results finalized before sessions existed.
      - `cache` AnswerCacheInfo: How a search answer relates to the workspace's semantic answer cache. Present only on a run that served a previously derived answer instead of deriving a new one. Everything else on the result — the evidences, the reasoning, the tool-call trace, the receipts — describes that ORIGINAL derivation, which is what this block exists to say out loud: the answer was computed once, for a question near enough to this one, under an identical scope and an identical access gate, and re-checked against the workspace before it was handed back. A result with no ``cache`` block was derived by the run that returned it.
        - `served_from_cache` boolean, required: True when this result was re-served from a previously derived answer rather than computed by this run. Always true where this block appears at all.
        - `source_job_id` string, uuid, nullable: The job whose run originally derived this answer. Null when that job's row has since been reaped — the answer outlives the record of the run that produced it.
        - `cached_at` string, date-time, required: When the answer was originally derived. Read it as the age of the evidence: the workspace may have changed since, which is what the re-check below is for.
        - `verified_at` string, date-time, required: When this run re-checked the cached answer's citations against the workspace and accepted them. Always this run's own time — a cached answer is never served unchecked.
      - `usage` SearchLlmUsage: What answering one search query cost in LLM work. Present only when the run was asked to account for itself *and* spend reached its ledger; ``null`` otherwise, so a caller can tell "nothing was measured" from "nothing was spent". An empty ledger publishes ``null`` rather than a zeroed object for that reason: zeros would claim a measured run cost nothing, when what an empty ledger most likely records is accounting that never landed. The totals cover recorded model calls at any depth: the agent's own turns, and the calls the retrieval tools made on its behalf — a document read by the recursive-LLM engine, a scan cracked by the vision engine, a tables question answered by the SQL agent. That is the number a per-query cost figure needs, and it is invariably several times the agent's own turn count. ``by_model`` is the same spend split by the model that served it, because a run legitimately spends on several at different prices — a total alone cannot be turned into money. The totals are the sum of ``by_model`` and are published anyway so the common case needs no arithmetic. Observed retries are included. These public token totals are not a complete cost certificate: provider omissions and failed persistence can leave gaps. Internal coverage and non-token counters stay in the ledger and are not added to this wire shape without a separate SDK contract update.
        - `requests` integer: Completed LLM requests, tool-internal ones included.
        - `input_tokens` integer: Prompt tokens across every request, cached ones included.
        - `output_tokens` integer: Completion tokens across every request.
        - `cached_input_tokens` integer: The share of input_tokens served from a provider prompt cache — billed at a reduced rate, so it is reported separately rather than subtracted.
        - `by_model` ModelTokenUsage[]: The same spend per model that served it, highest first.
          - `model` string, required: Model identifier as the provider reported it, or as the caller asked for it.
          - `requests` integer: Observed provider attempts, including explicitly recorded failures.
          - `input_tokens` integer: Prompt tokens sent to this model, cached ones included.
          - `output_tokens` integer: Completion tokens this model generated.
          - `cached_input_tokens` integer: The share of input_tokens served from the provider's prompt cache — billed at a reduced rate, so pricing needs it separated out rather than subtracted.
      - `grounding_job_ids` string[], nullable: Ground jobs planned for this search's evidences. Empty when postprocessing is disabled for this caller by deployment policy, explicitly disabled for the run, or no evidence is groundable. Absent on older results. When empty or absent, callers may POST /v1/ground explicitly to locate quotes.
    - DeepSearchV2Result: The result of one source-level deep-search run (``JobKind.DEEP_SEARCH_V2``). Field-for-field the JET shape — answer, reasoning, evidences, the run's ``sql_receipts``, the tool-call trace, session handle, usage, exhaustion — under its own discriminator: the two kinds share one agent chassis and one fold, and a second field list here would only be a place for them to drift. On this surface a receipt's ``rows`` are the statement's bounded preview; the full result is retained server-side and re-queryable through the ``res_…`` handle appearing in any statement that pages it.
      - `result_type` 'deep_search_v2'
      - `answer` string, nullable: The direct, grounded answer to the request. Null only on a run that exhausted its budget before authoring one, or that asked `clarification_questions` instead.
      - `clarification` string, nullable: The run's clarifying questions as plain text, instead of an answer. Only ever set when the request carried `allow_clarification` and the run needed the user's input to go on; `answer` is null and `evidences` empty alongside it. The same questions, structured, are in `clarification_questions`.
      - `clarification_questions` ClarifyingQuestion[], nullable: The questions behind `clarification`, at most 2, each with up to 4 suggested answers. Answer them with a follow-up on `session_id` that repeats the query and carries `clarification_answers` (one entry per question, a null answer skips it), or with any follow-up query in your own words. Null on every run that answered.
        - `question` string, required: The question, as the run asked it.
        - `options` string[]: Suggested answers, most likely first. Never exhaustive: the user may always answer in their own words, so no option stands for 'Other'. Empty when the run offered none.
      - `confidence` 'certain' | 'very_high' | 'high' | 'medium' | 'low' | 'speculative': How well the retrieved evidence supports the answer, worst-case wins.
      - `population_statement` string, nullable: The population every figure was computed over — file, period, layer, status, basis, grain — in the run's own words. Null only on a run that never authored an answer.
      - `figures` JetFigure[]: One entry per criterion tested, each linked to the receipts its figures are readable out of. Empty on a run that answered without testing anything, or that exhausted its budget first.
        - `criterion` string, required: What was tested, in the caller's terms.
        - `definition` string, required: The definition it was tested under: which column and values encoded the term, which clock, which basis, which threshold and where it came from.
        - `spec_id` string, required: The run's committed TestSpec this figure was computed under.
        - `population` integer, required: The denominator — rows at this test's grain that the criterion ran over.
        - `count` integer, required: Members of the population that met the criterion.
        - `value` string, nullable: The exceptions' value as text, so a decimal survives the wire. Null for a count-only criterion.
        - `result_ids` string[]: `sql_receipts[].result_id` of every statement this figure is readable out of.
        - `exception_listing_result_id` string, nullable: The receipt that lists the exceptions themselves, when one was produced.
        - `band_group` string, nullable: Set when this figure is one band of a family that partitions a population — the same name on every band of the family, whose counts sum to that population.
        - `cannot_be_performed` boolean: The workspace does not hold the material this criterion needs. `result_ids` then name the probes that establish the absence rather than the figures.
        - `breakdown` JetBreakdown: The tabular result behind one figure, attached by the server. What a caller asking "by FSLI and month" or "top 10 preparers" wanted, in the shape the question implies. The rows are NOT model-authored and never were: they are the cited receipt's own result, stringified and capped here, attached at finalize from :class:`SqlReceipt` rows the run already published. That is the whole design — a figure's table costs the model no output tokens and cannot carry a cell no statement returned — and it is why there is no reshaping either: the receipt's shape IS the breakdown, so a matrix the SQL never grouped by month is a wrong statement rather than a rendering problem. ``result_id`` names the receipt the rows came from, which is always one of the figure's own citations — either on ``result_ids`` or its ``exception_listing_result_id``, the two id sets the console makes openable — so the breakdown is a convenience over the payload's own working, never a second source of truth, and a reader who wants the statement, the whole bounded result or its row provenance follows that id. (When the model names a listing it did not also list in ``receipt_ids``, that id reaches the figure through ``exception_listing_result_id`` rather than ``result_ids``; both render as receipt buttons, so the table stays one click away either way.)
          - `columns` string[]: Column names, in the statement's own result order.
          - `rows` array[]: Rows in receipt order, cells as text so a decimal, a date and a NULL all survive the wire. At most 200; a longer result sets `truncated` and stays whole on its receipt.
            - string[]
          - `truncated` boolean: Whether rows were cut — either by this table's own cap or already on the receipt it came from. A capped breakdown is never read as the complete listing.
          - `result_id` string, required: `sql_receipts[].result_id` of the statement these rows are.
      - `criteria` JetCriterionOutcome[]: Every requested test and its final disposition, including tests that never reached binding, measurement, or a published figure. Empty only on results stored before this checklist existed.
        - `criterion_id` string, required: Stable short id for this requested test. Its TestSpec and published figure use the same id.
        - `description` string, required: The requested test in the caller's terms, without an invented conclusion.
        - `status` 'performed' | 'cannot_be_performed' | 'incomplete', required: Whether the test produced a finding, proved it could not be performed, or remained incomplete.
        - `spec_id` string, nullable: The committed TestSpec id and matching `figures[].spec_id`. Null when setup never reached a spec.
        - `blocker` 'binding_unresolved' | 'measurement_failed' | 'missing_finding' | 'population_incomplete' | 'context_exhausted' | 'missing_business_parameter' | 'methodology_unresolved' | 'finding_unverified', nullable: Why an incomplete test did not finish, including reasons newer than the three `incomplete_reason` keeps to. Null for performed and cannot-be-performed tests.
        - `incomplete_reason` 'binding_unresolved' | 'measurement_failed' | 'missing_finding', nullable: `blocker` in the three values older SDKs accept: `population_incomplete` and `finding_unverified` read as `measurement_failed`, and every other newer blocker as `missing_finding`. Read `blocker` for the full reason.
        - `missing_parameters` string[]: Policy inputs this test was missing. Set only when `blocker` is `missing_business_parameter` or `methodology_unresolved`; empty otherwise.
      - `discrepancies` string[]: Where the data contradicted the request's premise or a figure it stated. Empty when nothing did — the run is asked for this explicitly, so an empty list is an answer rather than an omission.
      - `reasoning` string, nullable: How the receipted statements chain together into the answer.
      - `evidences` SearchEvidence[]: The material the answer cites, each resolved to the caller's own uploaded file.
        - `source_path` string, required: The uploaded file the quote belongs to, e.g. 'reports/policy.pdf'. This is the file to ground the quote against.
        - `source_file_id` string, uuid, nullable: 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` integer, nullable: 1-based page number, when the agent saw one.
        - `component` string, nullable: Public component of the file the quote came from — the tab name for a spreadsheet, the section for a split document; the same vocabulary file-metadata lists. Pass it as the ground target's sheet when grounding a spreadsheet quote. None for a single-component file or when attribution was unavailable.
        - `quote` string, required: The key passage, as retrieved from the file.
        - `passages` string[]: Contiguous pieces of the source file that quote was stitched from, in the file's own text. Ground each piece as its own target when this list is present, with max_matches 2. A piece that returns two matches repeats in the file, so which copy the quote used is unknown. A ' | ' marks a table cell boundary added by the check. Replace it with a space before grounding. Filled only for PDF, Word and PowerPoint sources that Ground reads from the PDF text layer. Absent when quote is already one contiguous passage, could not be checked, or could not be verified.
        - `relevance` string: Deprecated: empty on new results, where `reasoning` says how the evidences support the answer.
        - `queries` TableQueryGrounding[]: The SQL the run executed over this evidence's table, with a bounded preview of each result — server-recorded from the run's tables engine, never model-authored. Attached whenever the run queried the cited table (or the row extraction of the same spreadsheet tab), so a verbatim quote from a queried sheet carries the computation trail alongside it; empty when the run never queried the cited file's tables.
          - `sql` string, required
          - `supports_answer` boolean: False for exploratory SQL the tables engine ran but did not mark as part of the answer's calculation trail.
          - `result_id` string, nullable: Stable identity of the stored query result behind this receipt. Lets the grounding-rows endpoint replay this exact query for matched-row pages beyond the regions' inline runs.
          - `source_paths` string[]: The uploaded table files this query read — directly, or through a prior result it built on — e.g. 'planning/FS GF Custom.xlsx'.
          - `row_count` integer: Rows in the query's full result, not in the preview below.
          - `columns` TableQueryColumn[]
            - `name` string, required
            - `data_type` string, required
          - `rows` object[]: Leading result rows, values rendered as text — a bounded preview, never the full result.
          - `preview_truncated` boolean: Whether the preview cut rows or columns.
          - `verified` boolean: True when at least one claim behind this query passed mechanical verification against the source data. Independent of 'regions': a source without an addressable grid verifies but cannot be pointed at, so an empty 'regions' never means unverified.
          - `regions` union[]: Original-file coordinates for the verified claims whose source file has an addressable grid.
            - union
              - …
          - `grounding_status` 'exact' | 'unavailable' | 'zero_result', nullable: Capture-time provenance status behind this query's regions. 'exact': the exact-provenance sidecar proved every row the query read, so its 'provenance'-basis regions are trustworthy (source regions may be broader than the minimal contributing set when grounding_reason carries a 'regions_broad:…' qualifier). 'unavailable': no exact proof could be captured (a legacy result, or a statement shape the capturer cannot instrument) — no 'provenance'-basis regions are attached, never a guessed whole-table span. 'zero_result': the query matched no rows, so there is nothing to ground. None on legacy payloads and on a query with no unclaimed table left to ground.
          - `grounding_reason` string, nullable: Qualification on grounding_status, when present. Set for 'unavailable' (explains why proof was not possible) and for 'exact' when capture succeeded but the source regions are broader than the minimal contributing set (e.g. 'regions_broad:narrowing_specs_unavailable'). None when grounding is fully exact or absent.
        - `derivation` DerivationTrace: The answer's computation DAG when evidence is derived rather than extracted.
          - `steps` DerivationStep[]
            - `result_id` string, required
            - `sql` string, required
            - `kind` 'extraction' | 'transformation', required
            - `label` string, required
            - `preview_rows` object[]
              - …
            - `reads` string[]: result_ids this step depends on.
      - `sql_receipts` SqlReceipt[]: Every statement the run executed, in execution order — the answer's working.
        - `result_id` string, required: Stable per-run identity of this statement, in execution order (e.g. 'sql_3').
        - `sql` string, required: The statement as executed, verbatim.
        - `handle` string, nullable: Content-addressed key of this statement's stored result, derived from the statement text. Server-side identity: it is what lets the run's own publication find the stored result and its capture-time provenance, and it is NOT an address an agent may query or chain from — `result_id` is the identity the caller and the row pager use. Null on a run that stored no result for the statement. A `cat_` handle names an exploratory document-catalog query: it stores no result beyond the rows carried here.
        - `table_bindings` SqlTableBinding[]: The tables this statement registered, each with the view name it was referenced by.
          - `path` string, required: The uploaded table file this view read, e.g. 'ledger/journal.xlsx'.
          - `component` string, nullable: The source worksheet, including when selected alone. Null for a direct upload or a historical unnamed tab.
          - `table_name` string, required: The view name the SQL referenced this table by, for THIS run.
          - `source_sha256` string, nullable: SHA-256 identity of the ingested source generation behind this table binding.
        - `replay` SqlReplayReceipt: Immutable inputs and engine identity for independent result recomputation.
          - `schema_version` 1
          - `kind` 'aggregate' | 'rowset', required: The verification lane appropriate for this statement.
          - `engine` 'duckdb', required: The query engine that executed the statement.
          - `engine_version` string, required: The query-engine version that executed the statement.
          - `engine_threads` integer, required: DuckDB thread count used for this execution.
          - `normalized_sql` string, required: The normalized single SELECT statement to execute during independent replay.
          - `sql_sha256` string, required: SHA-256 identity of the normalized statement.
          - `logical_plan_sha256` string, required: SHA-256 identity of the parser tree after source-position fields are removed.
          - `catalog_sha256` string, required: SHA-256 identity of the ordered source bindings used by this statement.
          - `result_artifact_sha256` string, required: SHA-256 identity of the complete retained result Parquet produced by the service.
          - `result_data_sha256` string, required: SHA-256 identity of the complete typed result rows.
          - `result_digest_version` string, required: Versioned Arrow serialization scheme used for the typed-result identity.
          - `result_dependencies` string[]: Earlier retained result handles read by this statement, in deterministic order.
        - `columns` string[]: Result column names, in result order.
        - `rows` object[]: Result rows, values rendered as text so a decimal, a date and a NULL all survive the wire.
        - `row_count` integer: Rows carried below — not the statement's unbounded result size.
        - `result_row_count` integer: Rows in the statement's complete stored result. Equal to `row_count` when nothing was cut; the number a total or an exception count must be read from when `truncated` is set.
        - `truncated` boolean: Whether rows past the stored cap were cut from this receipt.
        - `grounding_status` 'exact' | 'unavailable' | 'zero_result', nullable: Whether this statement's stored result already carried proof of which source rows it read when the receipt was recorded. 'exact' means it did; 'unavailable' means the proof could not be read; 'zero_result' means there were no rows to ground. Null when nothing had been captured yet — the ordinary case for a lane that captures at publication time, so this is a record of what execution happened to know and NOT a prediction of whether the receipt can be grounded. What was actually proven is published as `grounding_status` on the matching evidence query, beside the `regions` it earned.
        - `views_used` string[]: Session views registered when this statement ran, in definition order — the registry snapshot it executed under. A statement naming a view is replayable only against these, which is what makes named views admissible where chaining a previous call's result handle is not.
        - `saved_as` string, nullable: The session view this statement was registered as, when it was. Every name in another receipt's ``views_used`` appears here on exactly one receipt, so the run's receipts define every view they cite.
        - `view_sql` string, nullable: The definition registered under `saved_as`, when it is not this statement itself. Null on an ordinary `save_as`, where `sql` IS the definition. Set by `build_journal_view`, which registers the per-journal frame and runs a bounded probe over it rather than materializing the whole frame — so the definition every later `views_used` refers to is still published by exactly one receipt.
        - `warnings` string[]: Pre-execution lint notes attached to this statement — non-blocking, and published as part of its working.
        - `executor` string, nullable: The named executor that generated this statement (`reconcile_movement`, `test_timing`, `find_duplicates`, `field_completeness`, `journal_balance`), when one did, `custom_sql` for a statement the fixed-pipeline engine authored under its escape, or `ledger_understanding` for the ledger-understanding census's own statement, which ran before this run and is cited rather than re-run. Null when no named generator produced it — a population count, an absence probe, or SQL the agent wrote itself. Both engines use the same words here.
        - `parameters` object: The executor's resolved call, flattened to text — the clock and its true grain, the window's day offsets, the signature, the tolerance, the population. Empty on a statement no executor generated. This is what makes a figure trace to a named, parameterized computation rather than to SQL alone.
      - `binding` LedgerBindingWire: The definitional layer a run committed, echoed for the caller to read. This is the answer to "on what definitions does this figure rest" — the question the prose relay of the old playbook could not answer, and the reason the binding is a validated artifact rather than a paragraph. Every pin was checked against the tables' own profiles before the run was allowed to compute anything: the columns exist with compatible types, the period roles partition the observed domain, each clock's declared grain matches its column, and every encoding value occurs in its column. Published as flat slot listings rather than as a mirror of the internal model's tree. Two reasons, both practical: a reader wants "which column is the approval clock" answered in one place rather than by walking a nested object, and a slot the design adds later becomes another row here instead of a change to this contract.
        - `tables` BoundTable[]
          - `role` string, required: The slot it fills: 'journal_lines', 'journal_headers', 'trial_balance', ….
          - `path` string, required: The uploaded file, e.g. 'ledger/journal.xlsx'.
          - `component` string, nullable: The tab, when the file contributed more than one table.
          - `table_name` string, required: The view name this run's SQL referenced it by.
        - `policy_sheets` string[]: Documents the run treated as settling parameters (a sequence policy, a topside register).
        - `columns` BoundColumn[]: Every column pinned to a slot, in slot order.
          - `slot` string, required: The slot this column fills, dotted: 'journal_key', 'clocks.created', 'amount_bases[reporting].debit', 'accounts.fsli'.
          - `path` string, required
          - `component` string, nullable
          - `table_name` string, required
          - `column` string, required
          - `detail` string, nullable: What the slot additionally states about this column — a clock's true grain, that a basis is the one the trial balance ties to, what a calendar date means.
        - `periods` BoundPeriod[]: The period domain, in period order, with roles.
          - `value` string, required
          - `role` string, required: 'opening', 'month', 'year_end_adj' or 'total_row'.
        - `value_sets` BoundValueSet[]: Encoding candidates and layer memberships. More than one entry under 'encodings.manual' means the run kept both readings and resolved them per test.
          - `slot` string, required: 'encodings.posted', 'encodings.manual', 'layers.book', ….
          - `name` string, required: The candidate's name, as a test spec cites it.
          - `path` string, required
          - `component` string, nullable
          - `table_name` string, required
          - `column` string, required
          - `values` string[]: The values of that column which mean this.
          - `note` string: Where this reading came from — a policy sheet, a flag column, a profiled domain.
        - `notes` string[]: Deviations the run observed while orienting.
        - `revised_because` string, nullable: Why the binding was replaced mid-run, when it was. Null on a run that bound once.
      - `tool_calls` ToolCallTrace[]: One entry per tool the run invoked, in execution order — the tool's name, a digest of its arguments, how many results came back, and how long it took. The run's shape, not its content.
        - `tool` string, required
        - `arguments_digest` string, required
        - `hits` integer, required
        - `duration_ms` integer, required
      - `steps_used` integer: Legacy procedure-step count retained for compatibility. It is engine-specific and is not a model-request count; agent runs expose completed root-model requests in `agent_usage`. Read it with `exhausted`, which says whether the configured request budget ran out before the run was satisfied.
      - `exhausted` boolean: Step budget hit before the run was satisfied. A successful response, not a failure.
      - `degraded` string, nullable: Why this result is degraded, when it is. The run broke before it could finalize an answer but had already computed real working, so what is published is that working — `sql_receipts` and `binding` — with `figures` empty and `answer` saying so. Null on every run that finished normally, exhausted ones included.
      - `quality` JetQuality: Structured execution-quality reporting for one JE-testing result. Distinguishes a clean, fully evidenced success from a recovered or partially validated one without changing ``ok`` or inventing figures. Contains only check names, tool names, outcome categories, and counts — never prompts, answers, evidence bodies, shell output, or client identifiers.
        - `status` 'clean' | 'recovered_with_warnings' | 'degraded', required: ``clean`` when nothing was recovered; ``recovered_with_warnings`` when a fail-open check, tool failure, or missing audit artifact was recorded; ``degraded`` when the run salvaged working after a hard failure (see ``JetResult.degraded``).
        - `validation_warnings` JetValidationWarning[]: Fail-open output checks that passed the answer through after their corrective retry.
          - `check` 'specs' | 'scope' | 'cycle' | 'grain' | 'basis' | 'absence' | 'bands' | 'pointer' | 'delivery' | 'figures', required: Stable machine-readable name of the fail-open check that was not satisfied. ``specs`` covers two causes: a finding that names no committed test, and a finding computed for a test the run still declared blocked by a missing parameter.
          - `status` 'failed_open': Always ``failed_open``: the check rejected the answer once, then passed it through.
        - `tool_failures` JetToolFailure[]: Non-success tool outcomes the agent recovered from, aggregated by tool and category.
          - `tool` string, required: Tool name that produced the non-success outcome (for example ``run_command``).
          - `outcome` 'model_retry' | 'command_error' | 'timeout' | 'tool_error', required: Category of the non-success: ``model_retry`` (a call the run corrected: a gate refusal, a rejected argument shape or a validation retry; does not lower `quality.status`), ``command_error`` (non-zero shell exit), ``timeout``, or ``tool_error`` (the tool itself failed, for example a SQL error).
          - `count` integer, required: How many times this tool×outcome pair occurred in the run.
        - `missing_artifacts` string[]: Required audit artifact classes absent from a quantitative result (``binding`` and/or ``sql_receipts``). Empty when every finding is ``cannot_be_performed`` or when both artifacts are present.
        - `retried_checks` string[]: Output-contract check families that cost the answer a model re-emission, whether or not the re-emission repaired them. Attribution only — a retry the model then satisfied is a clean answer, so this does not move ``status``.
        - `withheld_receipt_figures` integer: How many performed figures were published with no readable receipt id — either the statements behind them are not visible to this caller, or the figure was computed without one. The figures themselves are still returned; this counts the ones whose working cannot be inspected.
      - `execution_status` 'performed' | 'partially_performed' | 'not_performed', nullable: Whether the run tested what it was asked to test: ``performed`` when every figure was computed, ``partially_performed`` when some criteria could not be, ``not_performed`` when none could. Read it alongside `quality`, which grades how well a performed test went. JE-testing always sets this on a finished run; other JetResult subclasses (deep search) leave it null, as do results stored before the field existed.
      - `agent_usage` JetAgentUsage: Completed root-agent model requests by session phase. This is intentionally separate from :class:`SearchLlmUsage`: that ledger covers every nested LLM call and token, while this block explains the agent procedure itself. Provider transport attempts are not exposed by the model boundary reliably and are therefore not guessed here.
        - `execution_requests` integer, required: Completed model requests in normal execution cycles.
        - `compaction_requests` integer, required: Completed model requests used to summarize compacted history.
        - `finalization_requests` integer, required: Completed requests in the tool-less recovery finalizer.
        - `total_requests` integer, required: Sum of execution, compaction, and finalization requests.
      - `spec_digest` string, nullable: A short fingerprint of what this run committed to TEST — the rows, grain, clock, encoding, thresholds and time window each criterion was pinned to — with the free-text justifications beside those choices excluded. Two runs of the same question carry the same value when they tested the same thing, so an unstable answer can be told apart from an answer to a quietly different question without re-reading either run. Null on a run that committed no test spec, and on other JetResult subclasses.
      - `session_id` string, uuid, nullable: The conversation this run belongs to. Send it back as the request's `session_id` to ask a follow-up in the same thread.
      - `usage` SearchLlmUsage: What answering one search query cost in LLM work. Present only when the run was asked to account for itself *and* spend reached its ledger; ``null`` otherwise, so a caller can tell "nothing was measured" from "nothing was spent". An empty ledger publishes ``null`` rather than a zeroed object for that reason: zeros would claim a measured run cost nothing, when what an empty ledger most likely records is accounting that never landed. The totals cover recorded model calls at any depth: the agent's own turns, and the calls the retrieval tools made on its behalf — a document read by the recursive-LLM engine, a scan cracked by the vision engine, a tables question answered by the SQL agent. That is the number a per-query cost figure needs, and it is invariably several times the agent's own turn count. ``by_model`` is the same spend split by the model that served it, because a run legitimately spends on several at different prices — a total alone cannot be turned into money. The totals are the sum of ``by_model`` and are published anyway so the common case needs no arithmetic. Observed retries are included. These public token totals are not a complete cost certificate: provider omissions and failed persistence can leave gaps. Internal coverage and non-token counters stay in the ledger and are not added to this wire shape without a separate SDK contract update.
        - `requests` integer: Completed LLM requests, tool-internal ones included.
        - `input_tokens` integer: Prompt tokens across every request, cached ones included.
        - `output_tokens` integer: Completion tokens across every request.
        - `cached_input_tokens` integer: The share of input_tokens served from a provider prompt cache — billed at a reduced rate, so it is reported separately rather than subtracted.
        - `by_model` ModelTokenUsage[]: The same spend per model that served it, highest first.
          - `model` string, required: Model identifier as the provider reported it, or as the caller asked for it.
          - `requests` integer: Observed provider attempts, including explicitly recorded failures.
          - `input_tokens` integer: Prompt tokens sent to this model, cached ones included.
          - `output_tokens` integer: Completion tokens this model generated.
          - `cached_input_tokens` integer: The share of input_tokens served from the provider's prompt cache — billed at a reduced rate, so pricing needs it separated out rather than subtracted.
      - `grounding_job_ids` string[], nullable: Ground jobs planned for this search's evidences. Empty when postprocessing is disabled for this caller by deployment policy, explicitly disabled for the run, or no evidence is groundable. Absent on older results. When empty or absent, callers may POST /v1/ground explicitly to locate quotes.
      - `structured_answer` unknown
      - `command_receipts` CommandReceipt[]: Successful shell calculations cited by this answer.
        - `result_id` string, required: Job-scoped identity of a recorded shell execution.
        - `command` string, required: The shell command as executed.
        - `stdout` string, required: The complete standard output of the successful command.
        - `outputs` object, required: Named scalar outputs parsed from stdout; a scalar output is named value.
      - `claims` DeepSearchVerifiedClaim[]: Calculated answer values, each bound to the execution that produced it.
        - `claim_id` string, required: Unique id of this calculation within the result.
        - `answer_excerpt` string, nullable: Exact displayed answer value selected for its calculation citation.
        - `answer_occurrence` integer: Zero-based occurrence of answer_excerpt in the answer.
        - `evidence_indices` integer[]: Source evidence indices supporting the calculation inputs.
        - `claim` string, required: The method or formula behind the calculated value, as the agent wrote it.
        - `reference` DeepSearchClaimReference, required: A claim's address within a published SQL or command execution receipt.
          - `kind` 'result_cell' | 'result_count' | 'result_absence' | 'command_result', required: How the result supports the claim.
          - `result_id` string, required: The matching result_id in sql_receipts or command_receipts.
          - `row` integer, nullable: 0-based result row for a cell claim; null otherwise.
          - `column` string, nullable: Result column for a cell claim; null otherwise.
          - `cited_value` string, required: The exact value checked against the retained result.
      - `coverage` DeepSearchCoverage[]: Complete result tables the answer attached, each with server-rendered rows.
        - `deliverable_id` string, required: Unique id for one requested output.
        - `description` string, required: Requested output this item accounts for.
        - `basis` 'sql' | 'document' | 'calculation': Whether support is retained SQL or document retrieval; document absence is not a SQL-verified proof.
        - `evidence_indices` integer[]: Zero-based indices into this result's published evidences supporting a document deliverable.
        - `status` 'answered' | 'partial' | 'unanswerable', required: Whether the output was fully answerable.
        - `claim_ids` string[]: Verified claims satisfying this output.
        - `complete_result_id` string, nullable: Receipt containing the requested complete listing; page it when its attached breakdown is truncated.
        - `complete_result` JetBreakdown: The tabular result behind one figure, attached by the server. What a caller asking "by FSLI and month" or "top 10 preparers" wanted, in the shape the question implies. The rows are NOT model-authored and never were: they are the cited receipt's own result, stringified and capped here, attached at finalize from :class:`SqlReceipt` rows the run already published. That is the whole design — a figure's table costs the model no output tokens and cannot carry a cell no statement returned — and it is why there is no reshaping either: the receipt's shape IS the breakdown, so a matrix the SQL never grouped by month is a wrong statement rather than a rendering problem. ``result_id`` names the receipt the rows came from, which is always one of the figure's own citations — either on ``result_ids`` or its ``exception_listing_result_id``, the two id sets the console makes openable — so the breakdown is a convenience over the payload's own working, never a second source of truth, and a reader who wants the statement, the whole bounded result or its row provenance follows that id. (When the model names a listing it did not also list in ``receipt_ids``, that id reaches the figure through ``exception_listing_result_id`` rather than ``result_ids``; both render as receipt buttons, so the table stays one click away either way.)
          - `columns` string[]: Column names, in the statement's own result order.
          - `rows` array[]: Rows in receipt order, cells as text so a decimal, a date and a NULL all survive the wire. At most 200; a longer result sets `truncated` and stays whole on its receipt.
            - string[]
          - `truncated` boolean: Whether rows were cut — either by this table's own cap or already on the receipt it came from. A capped breakdown is never read as the complete listing.
          - `result_id` string, required: `sql_receipts[].result_id` of the statement these rows are.
        - `missing_inputs` string[]: Inputs preventing a complete answer.
        - `absence_result_ids` string[]: Zero-row receipts proving those inputs absent.
      - `cache` AnswerCacheInfo: How a search answer relates to the workspace's semantic answer cache. Present only on a run that served a previously derived answer instead of deriving a new one. Everything else on the result — the evidences, the reasoning, the tool-call trace, the receipts — describes that ORIGINAL derivation, which is what this block exists to say out loud: the answer was computed once, for a question near enough to this one, under an identical scope and an identical access gate, and re-checked against the workspace before it was handed back. A result with no ``cache`` block was derived by the run that returned it.
        - `served_from_cache` boolean, required: True when this result was re-served from a previously derived answer rather than computed by this run. Always true where this block appears at all.
        - `source_job_id` string, uuid, nullable: The job whose run originally derived this answer. Null when that job's row has since been reaped — the answer outlives the record of the run that produced it.
        - `cached_at` string, date-time, required: When the answer was originally derived. Read it as the age of the evidence: the workspace may have changed since, which is what the re-check below is for.
        - `verified_at` string, date-time, required: When this run re-checked the cached answer's citations against the workspace and accepted them. Always this run's own time — a cached answer is never served unchecked.
  - `result_state` 'available' | 'expired' | 'not_retained' | 'cancelled' | 'storage_unavailable': Why ``Job.result`` is null. ``available`` with a null result means the job has not succeeded yet; ``expired`` means its content was removed; ``not_retained`` means the client is provisioned ``zero_retention`` and already had its one chance to read it; ``cancelled`` means the job, or the root job it ran for, was cancelled. A cancelled job serves no results (it is not charged, including delivered parts) — not its own progressive values, and not a delegated child's result. It is a read state only, derived on the wire; no row stores it. ``storage_unavailable`` withholds a retained result while its workspace binding refuses access; ``Job.result_error`` explains the current refusal. A late poll returns the job with a state, never a 404 — "this expired" and "this never existed" are a scheduling problem and a bug respectively, and a 404 makes them identical.
  - `result_error` Error: The error object, per spec 12 §1.8.
    - `code` 'storage_connection_not_found' | 'storage_connection_in_use' | 'storage_limit_exceeded' | 'storage_conflict' | 'storage_auth_required' | 'storage_unavailable' | 'scan_required' | 'scan_rejected' | 'scan_unavailable' | 'invalid_request' | 'unsupported_file_type' | 'file_too_large' | 'invalid_schema' | 'invalid_sql' | 'invalid_path_prefix' | 'command_not_permitted' | 'batch_too_large' | 'domain_validation_failed' | 'default_label_required' | 'wait_required_for_zero_retention' | 'ledger_decision_invalid' | 'tenant_required' | 'partner_required' | 'workspace_not_found' | 'file_not_found' | 'job_not_found' | 'upload_not_found' | 'node_not_found' | 'schema_not_found' | 'domain_not_found' | 'session_not_found' | 'access_label_not_found' | 'ledger_understanding_not_found' | 'ledger_table_not_found' | 'tenant_not_found' | 'path_conflict' | 'schema_version_conflict' | 'access_label_name_taken' | 'workspace_name_taken' | 'access_label_in_use' | 'upload_expired' | 'file_not_ingested' | 'workspace_deleting' | 'workspace_not_deletable' | 'ingestion_in_progress' | 'job_not_cancellable' | 'confirmation_required' | 'domain_not_published' | 'domain_in_use' | 'reserved_domain_slug' | 'kg_not_built' | 'kg_build_in_progress' | 'ledger_understanding_in_progress' | 'ledger_understanding_version_outdated' | 'session_busy' | 'tenant_limit_reached' | 'range_not_satisfiable' | 'result_expired' | 'access_denied' | 'tenant_disabled' | 'access_backstop_violation' | 'access_mode_not_implemented' | 'rate_limited' | 'concurrency_limit_reached' | 'quota_exceeded' | 'corrupt_file' | 'zero_byte_file' | 'encrypted_file' | 'csv_dialect_ambiguous' | 'csv_parse_failed' | 'parse_failed' | 'ocr_failed' | 'conversion_failed' | 'sheetless_workbook' | 'unplayable_media' | 'unclassified_pages' | 'job_cancelled' | 'job_timeout' | 'upstream_unavailable' | 'internal_error' | 'unauthorized' | 'not_implemented', required: Every error the ``/v1`` surface can return. Clients must treat this as an **open** enum: a new member is an additive change (spec 12 §9), and a client that crashes on an unknown code makes every future error a breaking change.
    - `message` string, required: Human- and model-readable; says what to do differently.
    - `detail` object, nullable: Structured payload, when the code carries one.
    - `retryable` boolean, required: Whether retrying the identical request could succeed.
    - `request_id` string, nullable: Per-request correlation id; matches the ``X-Request-Id`` header. Null only when the error was minted outside any HTTP request, e.g. by a worker.
  - `error` Error: The error object, per spec 12 §1.8.
    - `code` 'storage_connection_not_found' | 'storage_connection_in_use' | 'storage_limit_exceeded' | 'storage_conflict' | 'storage_auth_required' | 'storage_unavailable' | 'scan_required' | 'scan_rejected' | 'scan_unavailable' | 'invalid_request' | 'unsupported_file_type' | 'file_too_large' | 'invalid_schema' | 'invalid_sql' | 'invalid_path_prefix' | 'command_not_permitted' | 'batch_too_large' | 'domain_validation_failed' | 'default_label_required' | 'wait_required_for_zero_retention' | 'ledger_decision_invalid' | 'tenant_required' | 'partner_required' | 'workspace_not_found' | 'file_not_found' | 'job_not_found' | 'upload_not_found' | 'node_not_found' | 'schema_not_found' | 'domain_not_found' | 'session_not_found' | 'access_label_not_found' | 'ledger_understanding_not_found' | 'ledger_table_not_found' | 'tenant_not_found' | 'path_conflict' | 'schema_version_conflict' | 'access_label_name_taken' | 'workspace_name_taken' | 'access_label_in_use' | 'upload_expired' | 'file_not_ingested' | 'workspace_deleting' | 'workspace_not_deletable' | 'ingestion_in_progress' | 'job_not_cancellable' | 'confirmation_required' | 'domain_not_published' | 'domain_in_use' | 'reserved_domain_slug' | 'kg_not_built' | 'kg_build_in_progress' | 'ledger_understanding_in_progress' | 'ledger_understanding_version_outdated' | 'session_busy' | 'tenant_limit_reached' | 'range_not_satisfiable' | 'result_expired' | 'access_denied' | 'tenant_disabled' | 'access_backstop_violation' | 'access_mode_not_implemented' | 'rate_limited' | 'concurrency_limit_reached' | 'quota_exceeded' | 'corrupt_file' | 'zero_byte_file' | 'encrypted_file' | 'csv_dialect_ambiguous' | 'csv_parse_failed' | 'parse_failed' | 'ocr_failed' | 'conversion_failed' | 'sheetless_workbook' | 'unplayable_media' | 'unclassified_pages' | 'job_cancelled' | 'job_timeout' | 'upstream_unavailable' | 'internal_error' | 'unauthorized' | 'not_implemented', required: Every error the ``/v1`` surface can return. Clients must treat this as an **open** enum: a new member is an additive change (spec 12 §9), and a client that crashes on an unknown code makes every future error a breaking change.
    - `message` string, required: Human- and model-readable; says what to do differently.
    - `detail` object, nullable: Structured payload, when the code carries one.
    - `retryable` boolean, required: Whether retrying the identical request could succeed.
    - `request_id` string, nullable: Per-request correlation id; matches the ``X-Request-Id`` header. Null only when the error was minted outside any HTTP request, e.g. by a worker.
  - `units` integer: Measured work in legacy credit units, retained even when a failed or cancelled job is not billed. Kept for compatibility: `credits` is the authoritative billed amount.
  - `credits` integer, nullable: Final credits billed for this job, priced once under `credit_schedule_version`. Null until `usage_final` is true. Never changes afterwards, including after payload expiry or deletion. Failed and cancelled-before-completion jobs finalize with 0 credits, including delivered parts; ones finalized before that policy keep their credits. Always null on a stage job that Automatic Search delegated: its work is billed on the parent job.
  - `usage_final` boolean: True once `credits` is final and will not change. False while the job is queued or running, and until any cleanup of its delegated stages has been accounted for. A failed tool job (read_file, run_sql, hybrid_search, grep, file_metadata, folder_metadata) created with an Idempotency-Key stays false for one hour after it fails, because a retry with the same key re-runs it; after that hour it is final and the retry returns the stored error.
  - `credit_schedule_version` integer, nullable: Version of the credit schedule `credits` were priced under. Null until `usage_final` is true.
  - `usage_finalized_at` string, date-time, nullable: When `credits` became final. Null until `usage_final` is true.
  - `cost` JobCost: An immutable USD price independent of the legacy credit schedule.
    - `currency` 'USD'
    - `pricing_version` string, required: The dollar price-sheet version pinned when this job was accepted.
    - `total_usd` string, required: Sum of the component amounts in USD, serialized as a decimal string.
    - `lines` CostLine[], required: The independently priced Parse and operation components, including paid reuse.
      - `operation` 'parse' | 'split' | 'classify' | 'extract' | 'ground', required: The work this dollar amount pays for.
      - `quantity` integer, required: The number of page-equivalents or Ground targets priced by this line.
      - `unit` 'page' | 'item', required: The quantity's billing unit: page-equivalent or Ground target.
      - `execution` 'fresh' | 'reused', required: The price tier: normal work or verified, billing-eligible tenant reuse; not provider token caching.
      - `unit_price_usd` string, required: USD per unit under the job's pinned price sheet, serialized as a decimal string.
      - `amount_usd` string, required: This line's USD amount: quantity multiplied by unit_price_usd, serialized as a decimal string.
      - `reuse_reason` 'reused_parse' | 'repeat_of_job', nullable: Why the lower reuse rate applies; null on a normal-price line.
  - `usage` JobUsage: How a job's final credits were computed.
    - `lines` UsageLine[], required: One line per part. Their credits sum to the job's credits; waived parts remain listed at 0 credits.
      - `part` 'parse' | 'operation' | 'image_ocr', required: parse: reading the document into text. operation: the tool's own work on what was parsed. image_ocr: reading the images inside an email, HTML or spreadsheet file. A ground job's image_ocr is the email images it read to find targets its text search missed.
      - `quantity` integer, required: How many units of work this part measured, including waived or included work.
      - `unit` 'page' | 'item' | 'unit' | 'image', required: What quantity counts: page is a page or page-equivalent (a spreadsheet's non-empty cells, a minute of audio), item is a ground item, image is an image Parse read inside an email, HTML or spreadsheet file, unit is a job priced by its units directly (agent steps).
      - `credits_per_unit` integer, required: The credit schedule's rate for one unit of this part; zero for OCR included in the ordinary price.
      - `multiplier` integer, required: 2 on the parse and image_ocr parts of a parse_mode "high" job, otherwise 1.
      - `credits` integer, required: quantity x credits_per_unit x multiplier: what this part added to the job's credits. 0 when waived or included in the ordinary price.
      - `waiver` UsageWaiver: Why part of a job, or the whole job, was not charged.
        - `part` 'parse' | 'job', required: parse: the parse part was not charged. job: nothing in the job was charged.
        - `reason` 'reused_parse' | 'repeat_of_job', required: reused_parse: the job read a parse your earlier job already paid for. repeat_of_job: the job repeated your earlier job exactly and was answered with its result.
        - `source_job_id` string, uuid, nullable: Always null: billing history must not disclose another job as its visibility changes.
  - `created_at` string, date-time, required: When the job row was created.
  - `started_at` string, date-time, nullable: When execution began. Null while still queued.
  - `finished_at` string, date-time, nullable: When the job reached a terminal status.
  - `payload_expires_at` string, date-time, nullable: Cleanup deadline after explicit deletion. Null for retained content; Zero retention uses its delivery window.
  - `deleted_at` string, date-time, nullable: When the caller hid this run from listings, via `delete_job`. A soft delete: the job still reads by id and still counts toward `GET /v1/usage` — what it stops doing is appearing in `GET /v1/jobs`.

## Other responses

- `400`: Bad Request
- `401`: Unauthorized
- `402`: Payment Required
- `403`: Forbidden
- `404`: Not Found
- `409`: Conflict
- `410`: Gone
- `413`: Request Entity Too Large
- `422`: Unprocessable Entity
- `424`: Failed Dependency
- `429`: Too Many Requests
- `500`: Internal Server Error
- `501`: Not Implemented
- `503`: Service Unavailable
- `504`: Gateway Timeout

---

[API](https://skmtc.dev/nace/apis/ndi-service.md) - [All operations](https://skmtc.dev/nace/apis/ndi-service/llms.txt) - [OpenAPI document](https://skmtc.dev/nace/apis/ndi-service/revisions/d9d1f8ec2207?raw)
