total_matches, and a citation per
extracted value.
{"type": "query", ...} starts a search;
{"type": "page", ...} fetches the next page of an earlier one.
Query parameters:
wait_seconds(optional, default 0, max 300) — initial queries run an agent and usually outlast a sync window; page requests typically finish inside it.
Idempotency-Key(optional) — replays match on the full canonical body and the calling API key.
Start a search
Request body:query— the filter/rank/count request, in plain words.context(optional) — free-form background for the interpretation; it does not alter the access filter.path_prefix(optional) — narrow to a folder subtree. Intersects with the access filter, never overrides it.allow_clarification(optional, defaultfalse) — let the run finish with one clarifying question instead of a result, when the request is genuinely ambiguous and the ambiguity materially changes the result. The rejected result carries the question inclarification— no rows, noapplied_query,exhaustivefalse. Filtered search keeps no session, so answer it by sending a new initial query with the ambiguity resolved. Off — the default — the run always executes, stating its reading ininterpretation.
202 with a job of kind: "filtered_search"; poll GET /v1/jobs/{id}
for the FilteredSearchResult.
Result (abridged):
total_matches with no rows; a top-N question returns exactly N rows with
the pre-cutoff total; a list returns a bounded first page (≤ 200 rows) plus
next_cursor — matching more than one page never requires narrowing the query.
Page a result
Request body:executed_at, and catalog_drifted: true says the catalog changed since the
query was validated. Only the API key that ran the original query may page it;
an expired or purged parent job returns invalid_request — re-run the query.
Reading the result honestly
exhaustiveis the one completeness signal.truemeans every eligible component in the tables the query touched was completely cataloged and the whole qualifying set was evaluated — totals are a census and a zero-match result proves absence. Whenfalse, the matches shown are valid, but “all”-style claims,total_matchesas a census, and zero-match conclusions are not guaranteed (uncataloged files in scope, degraded extractions, or a residual content condition — which can prove presence, never absence).interpretationis the agent’s reading of your request — thresholds, field choices, assumptions. A misreading is visible there; re-ask with tighter wording.clarificationis non-null exactly when the run asked a clarifying question instead of executing — only possible when the initial query setallow_clarification. A rejected result carries no rows, noapplied_query, nointerpretation, andexhaustive: false. There is no session to continue: send a new initial query that answers the question.- Money is never converted. Every amount carries the ISO currency the source stated, and cross-currency comparison or aggregation is unsupported in this version: when amounts are compared or ranked, the interpretation states the common-currency assumption.
- Value statuses are data honesty: a cell’s
statusofnot_foundmeans the component was examined and the field is absent (usable as a filter — “invoices with no PO”);ambiguousmeans present but not normalizable (e.g. money without a currency);unexaminedmeans the extraction could not say. coverageis the standard access-withholding block: results are label-filtered first, and the narrowing is reported, never silent.componentis actionable. A row’scomponentis the same name the file tools take: pass it ascomponent=toqa-file/read-file(or inquery-tables’componentsfor a sheet row) to work on exactly that part of the file. Several document rows can share one section name —page_start/page_endtell them apart. The one exception is amediarow: recordings are unnamed in the file tools, so itscomponent(the file stem) is display-only and QA takes the whole file.
Coverage and re-ingestion
The catalog is built at ingestion. Files ingested before filtered search existed remain valid but uncovered — they blockexhaustive for every
query over their scope until they are re-ingested (POST /v1/workspaces/{id}/ingestions over the same selection re-ingests in place).