> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ndi.nace.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Choose the right tool

> Which search or question tool to use for a question over a workspace, with example prompts

NDI has several ways to ask questions of a workspace. They are not aliases:
each one answers a different kind of question, and the wrong one gives a
weaker answer or none. This page says which to use, with examples. Examples
use the [`ndi` CLI](/guides/cli); each tool also lists its SDK method and
endpoint.

## Before you ask

Every tool on this page reads an **ingested** workspace. Upload the files,
ingest them, and wait for ingestion to finish before asking.

```bash theme={"dark"}
pip install ndi-cli
ndi login
ndi workspace create fy25-audit
ndi workspace use <workspace-id>
ndi files upload ./corpus --ingest
ndi workspace stats
```

`ndi workspace create --json` prints the workspace as JSON; the id is its
`workspace_id` field. `ndi workspace stats` shows what has been ingested. A file that is not
ingested yet answers `file_not_ingested`.

## Look before you ask

Two fast calls show what a workspace holds before you spend a question on it:

* `POST /v1/workspaces/{workspace_id}/tools/folder-metadata` lists a folder and
  finds files by name (`name_contains`), with each file's exact path.
* `POST /v1/workspaces/{workspace_id}/tools/file-metadata` describes one file by
  path: its category, a summary, and its sections or tabs with their own
  summaries and key terms.

Every tool that takes a path wants the file's full path relative to the
workspace root, exactly as uploaded: never a bare file name or a path that
starts with the workspace id. See [File tools](/guides/file-tools).

## Pick in order

Read the question, then take the **first** rule that matches.

1. The question is an audit test over journal entries or a general ledger
   (approvals, posting dates, users, round amounts, cut-off): use
   **[journal-entry testing](#journal-entry-testing)**.
2. The question is about one file you can name: use **[file QA](#file-qa)**.
3. The question needs a calculation or comparison across one or more
   spreadsheets or table files: use **[table QA](#table-qa)**.
4. The question is one turn of a conversation: a follow-up to an earlier
   answer, or one the asker may need to clarify: use **[QA](#qa)**.
5. Anything else: use **[automatic search](#automatic-search)**. When you
   already know the route, say so: `--mode fact` for a fact written in the
   documents (a number, a name, a date, a clause) that needs no reasoning;
   `--mode agent` for reasoning (comparing documents, explaining why,
   reconciling, several steps) or for filtering, ranking, or counting
   documents by their values, which the agent answers from the metadata
   catalog as a table. Unsure: omit `--mode` and the service picks.

```mermaid theme={"dark"}
flowchart TD
    Q[Question] --> J{"Audit test over journal entries or a ledger?"}
    J -->|yes| JET[je-testing]
    J -->|no| One{"About one named file?"}
    One -->|yes| FQA[ask-file]
    One -->|no| T{"Calculation across spreadsheets or tables?"}
    T -->|yes| TQA[query-tables]
    T -->|no| C{"A turn in a conversation?"}
    C -->|yes| QA[qa]
    C -->|no| L{"A stated fact, no reasoning?"}
    L -->|yes| Fact["automatic-search --mode fact"]
    L -->|no| R{"Reasoning, or a filtered list or count?"}
    R -->|yes| Agent["automatic-search --mode agent"]
    R -->|unsure| Auto[automatic-search]
```

Fact search, deep search, and filtered search are deprecated; see
[Deprecated tools](#deprecated-tools) for what replaces each. Their endpoints
keep working until they are retired.

Build the **[knowledge graph](#knowledge-graph)** once when the workspace has
many documents about the same companies, people, contracts, or accounts. It is
not a separate question tool: once built, automatic search (both routes) and
QA use it on their own.

## Example prompts

| Question | Tool | Why |
| - | - | - |
| What was total revenue for FY2025? | Automatic search, `--mode fact` | A figure stated in the financial statements |
| Who signed the engagement letter for the client? | Automatic search, `--mode fact` | A name written in one place |
| What is the notice period in the Acme master services agreement? | Automatic search, `--mode fact` | A clause; no reasoning needed |
| When does the Northwind lease expire? | Automatic search, `--mode fact` | A date stated in a document |
| Why did gross margin fall compared with last year? | Automatic search, `--mode agent` | Needs an explanation drawn from several documents |
| Do the board minutes support the impairment recorded in Q3? | Automatic search, `--mode agent` | Compares two sources and judges them |
| Which subsidiaries missed covenant tests, and by how much? | Automatic search, `--mode agent` | Several documents, then a comparison per subsidiary |
| Reconcile the revenue in the MD\&A with the income statement | Automatic search, `--mode agent` | Multi-step reconciliation |
| All invoices over 10,000 USD dated in March 2025 | Automatic search, `--mode agent` | Threshold plus date filter; wants a list |
| How many purchase orders from Acme Corp in Q4? | Automatic search, `--mode agent` | A count over documents |
| Top 10 contracts by value signed in 2025 | Automatic search, `--mode agent` | Rank plus filter |
| Contracts in the register that expire before 2026 | Automatic search, `--mode agent` | Register entries filtered by date |
| Summarize the termination clause in contracts/acme-msa.pdf | File QA | One named file |
| What is the invoice total on invoices/INV-1043.pdf? | File QA | One named file |
| What does the CFO say about guidance in calls/q3-call.mp3? | File QA | One named recording |
| Which accounts in the trial balance moved more than 10% from last year? | Table QA | Calculation over a table |
| Total the cash accounts across the TB and the bank reconciliation | Table QA | Arithmetic across two workbooks |
| Find journal entries posted on weekends or public holidays | Journal-entry testing | An audit test over the ledger |
| Were any journal lines approved by their own preparer? | Journal-entry testing | Segregation-of-duties test |
| Entries just below the 50,000 approval limit, posted after period end | Journal-entry testing | Ledger test with thresholds and cut-off |
| And for FY2024? (after an answer about FY2025) | QA | A follow-up in the same conversation |
| Anything about related-party transactions? | Automatic search | Open-ended; no clear tool |

Filters on **journal or ledger lines** go to journal-entry testing, not
automatic search. Automatic search lists documents and register entries; it
does not test ledger rows.

## File QA

`ndi ask-file PATH QUERY` · SDK `client.tools.qa_file` · `POST /v1/workspaces/{workspace_id}/tools/qa-file`

Answers a question about one file: a document, a workbook, an image, or a
recording. It reads that file and reasons over it, and cites the passages it
used.

* **Returns:** an `answer` and `citations` from the file. Pass
  `output_schema` (SDK and API) to get the answer as JSON in
  `structured_answer`.
* **Use when:** the question names one file, or you already know which file
  holds the answer.
* **Do not use when:** the answer could be in any of several files (automatic
  search); the question spans several workbooks (table QA).

```bash theme={"dark"}
ndi ask-file contracts/acme-msa.pdf "Summarize the termination clause"
ndi ask-file invoices/INV-1043.pdf "What is the invoice number, date, and total?"
ndi ask-file calls/q3-earnings.mp3 "What does the CFO say about full-year guidance?"
ndi ask-file finance/fixed-assets.xlsx "Which assets were disposed of this year?" --component Disposals
```

`PATH` is the file's full path in the workspace, exactly as uploaded (see
[file paths](/guides/file-tools#file-paths)). `--component` limits the question
to one sheet or section, named as
[file metadata](/guides/file-tools#file-metadata) lists it.

## Table QA

SDK `client.tools.query_tables` · `POST /v1/workspaces/{workspace_id}/tools/query-tables`

Answers one question over one or more spreadsheets or table files: totals,
movements, comparisons, and joins across workbooks. It cites the tables it
used.

* **Returns:** an `answer`, citations, any sources it could not use, and a
  preview of the rows behind the answer. Pass `output_schema` to get JSON in
  `structured_answer`.
* **Use when:** the answer has to be computed from tables, especially across
  several files.
* **Do not use when:** the files are PDFs or other documents (file QA or deep
  search); the question is an audit test over a ledger (journal-entry
  testing).

**Python**

```python theme={"dark"}
from ndi_sdk import NdiClient

with NdiClient() as client:
    workspace_id = "550e8400-e29b-41d4-a716-446655440001"
    answer = client.tools.query_tables(
        workspace_id,
        paths=["finance/trial-balance-fy25.xlsx", "finance/trial-balance-fy24.xlsx"],
        query="Which accounts moved more than 10 percent from last year?",
    )
    print(answer.answer)
```

**curl**

```bash theme={"dark"}
curl -s -X POST "$NDI_BASE_URL/v1/workspaces/$WS/tools/query-tables" \
  -H "X-API-Key: $NDI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"paths":[{"path":"finance/trial-balance-fy25.xlsx"},{"path":"finance/trial-balance-fy24.xlsx"}],"query":"Which accounts moved more than 10 percent from last year?"}'
```

See [Query tables](/guides/query-tables).

## Journal-entry testing

`ndi je-testing` · SDK `client.search.je_testing` · `POST /v1/workspaces/{workspace_id}/je-testing`

Performs an audit test over an ingested journal or general-ledger package. It
does not search for passages: it tests the whole population against your
criterion and reports how many entries meet it, with the queries behind
every figure so the result can be checked.

* **Returns:** a job whose result has an `answer`, `figures` (each criterion
  with its count and population), `criteria`, `discrepancies`, and
  `sql_receipts`. `exhausted` means the run stopped before it finished; the
  answer is partial.
* **Use when:** the question is a journal-entry test: who posted or approved,
  when, at what amount, with what description, compared with what threshold
  or period.
* **Do not use when:** the question is about documents rather than ledger
  lines (automatic search); it is a calculation over a trial
  balance or another non-ledger workbook (table QA).

```bash theme={"dark"}
ndi je-testing "Find journal entries posted on weekends or public holidays" --path ledger/journal.xlsx
ndi je-testing "Test whether any posted journal line was approved by its own preparer" --path ledger/journal.xlsx
ndi je-testing "List manual entries posted after period end that are just below the 50,000 approval limit" --prefix ledger/
ndi je-testing "Find entries with round amounts over 100,000 and no description" --reasoning-effort high
```

`--path` scopes the test to one file and can repeat. Save facts every test
should know, such as how the journal ID is built, once with
`ndi workspace global-context --set "..."`. See
[Journal-entry testing](/api-reference/je-testing).

## Automatic search

`ndi automatic-search` · SDK `client.search.automatic` · `POST /v1/workspaces/{workspace_id}/automatic-search`

Answers one question over the workspace by one of two routes: the fact route,
a quick lookup that quotes what is written, or the agent, which reads
documents, queries tables, and filters the metadata catalog, and explains how
it reached the answer. Without `--mode` the service picks the route and says
which it took.

* **Returns:** a job whose result has `routing` (the route taken), `output`
  (that route's answer), and `output_job_id`. When the agent answers from the
  catalog, `output.catalog` is a table of matching documents with an exact
  `total_matches`; it carries up to 200 rows, and `next_cursor` pages the
  rest with `ndi filtered-search --cursor`. Pass `output_schema` (SDK and API)
  to get the answer as JSON in the route's `structured_answer`.
* **Use when:** the question is about the workspace and no earlier rule in
  [Pick in order](#pick-in-order) matches.
* **Know the route already?** `--mode fact` for a stated fact, `--mode agent`
  for reasoning or a filtered list or count. Either runs with no routing step
  and keeps the automatic result shape.

```bash theme={"dark"}
ndi automatic-search "Anything about related-party transactions?"
ndi automatic-search "What was total revenue for FY2025?" --mode fact
ndi automatic-search "Which subsidiaries missed covenant tests, and by how much?" --mode agent
ndi automatic-search "How many purchase orders from Acme Corp in Q4 2025?" --mode agent --prefix purchasing/
```

## QA

SDK `client.search.qa` · `POST /v1/workspaces/{workspace_id}/qa`

One turn of a conversation, answered by automatic search's agent. Pass the
previous result's `answer.session_id` as `session_id` to ask a follow-up; the
agent may ask a clarifying question instead of guessing.

* **Returns:** a job whose result is an `automatic_agent`: `answer` (with
  citations, SQL receipts, `session_id`, and any `clarification_questions`)
  and, when the agent answered from the catalog, a `catalog` table. Pass
  `output_schema` on a turn to get that turn's answer as JSON.
* **Use when:** the question follows up an earlier answer, or the asker can
  answer a question back.
* **Do not use when:** the question stands alone (automatic search can take
  the faster fact route). The CLI has no QA command; use the SDK or the API.

```python theme={"dark"}
from ndi_sdk import NdiClient
from ndi_sdk.models.jobs import AutomaticAgentResult

with NdiClient() as client:
    workspace_id = "550e8400-e29b-41d4-a716-446655440001"
    first = client.jobs.wait(
        client.search.qa(workspace_id, query="What was total revenue for FY2025?").job_id,
        timeout=600,
    )
    if isinstance(first.result, AutomaticAgentResult) and first.result.answer.session_id:
        client.search.qa(workspace_id, query="And for FY2024?", session_id=first.result.answer.session_id)
```

See [QA](/api-reference/qa).

## Knowledge graph

Build: SDK `client.ingestion.build_knowledge_graph` · `POST /v1/workspaces/{workspace_id}/knowledge-graph/builds`

The knowledge graph links the companies, people, contracts, accounts, and
other entities your documents mention, across all files. It is not a separate
question tool. Build it once, and automatic search (both routes) and QA use
it on their own. Questions about how entities relate, or
every document that mentions one, get better answers once it is built.

A workspace has no graph until you build one. Build after ingestion, and
again after you add many files, or set `knowledge_graph.auto_build` on the
workspace to rebuild after every ingestion.

**Python**

```python theme={"dark"}
from ndi_sdk import NdiClient

with NdiClient() as client:
    workspace_id = "550e8400-e29b-41d4-a716-446655440001"
    build = client.ingestion.build_knowledge_graph(workspace_id)
    client.jobs.wait(build.job_id, timeout=1800)
```

**curl**

```bash theme={"dark"}
curl -s -X POST "$NDI_BASE_URL/v1/workspaces/$WS/knowledge-graph/builds" \
  -H "X-API-Key: $NDI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
```

See [Knowledge graph](/guides/knowledge-graph).

## Deprecated tools

These keep working until they are retired. Each names what replaces it.

### Fact search (deprecated)

SDK `client.search.fact` · `POST /v1/workspaces/{workspace_id}/fact-search`

> [!WARNING]
>
> Deprecated. Use [automatic search](#automatic-search) with `--mode fact`
> (`mode="fact"` in the SDK): the same pipeline, in the automatic result. The
> endpoint keeps working until it is retired; `ndi fact-search` is removed from
> CLI 0.27.0.

Finds facts stated in the workspace and answers with them. It retrieves and
quotes; it does not compare, calculate, or explain. It answers in seconds and
always returns an answer.

* **Returns:** a job whose result has an `answer`, the `evidences` it is
  drawn from (each with its source file), and `grounding_job_ids` for exact
  locations in the source.
* **Use when:** the answer is written down somewhere: a figure, a name, a
  date, a clause, a definition.
* **Do not use when:** the question asks why or how, or compares sources
  (deep search); filters a list by value (filtered search); needs arithmetic
  over tables (table QA).

```bash theme={"dark"}
ndi automatic-search "What was total revenue for FY2025?" --mode fact
ndi automatic-search "What is the notice period in the Acme master services agreement?" --mode fact --prefix contracts/
ndi automatic-search "What interest rate applies to the revolving credit facility?" --mode fact --top-k 5
```

`--prefix` limits the search to files under a folder, and `--path` (repeatable)
to exact files; the two add up, so the search covers the folder's files plus the
named ones. `--top-k` caps the number of citations when the fact route answers.

### Deep search (deprecated)

SDK `client.search.deep` · `POST /v1/workspaces/{workspace_id}/deep-search`

> [!WARNING]
>
> Deprecated. Use [QA](#qa) for a conversation, or
> [automatic search](#automatic-search) with `--mode agent` for one question.
> The endpoint keeps working until it is retired; `ndi deep-search` is removed
> from CLI 0.27.0.

Answers questions that need reasoning. It reads across documents and tables,
compares and calculates, and explains how it reached the answer. It takes
longer than fact search, up to minutes.

* **Returns:** a job whose result has an `answer`, the `evidences` and claims
  behind it, receipts for any calculation it ran, and `grounding_job_ids`.
* **Use when:** the question compares sources, asks why, reconciles figures,
  or needs several steps to answer.
* **Do not use when:** a single stated fact answers it (fact search is
  faster); it is a filter or count over documents (filtered search); it is an
  audit test over a ledger (journal-entry testing).

```bash theme={"dark"}
ndi automatic-search "Why did gross margin fall compared with last year?" --mode agent
ndi automatic-search "Reconcile revenue in the MD&A with the income statement" --mode agent --prefix reports/fy25/
```

Deep search uses a consistent reasoning engine with medium thinking budget and accesses the knowledge graph when the workspace has one.

### Filtered search (deprecated)

`ndi filtered-search` · SDK `client.search.filtered` (Python) · `POST /v1/workspaces/{workspace_id}/filtered-search`

> [!WARNING]
>
> Deprecated for new queries. Use [automatic search](#automatic-search): its
> agent answers filter, rank, and count questions from the metadata catalog and
> returns the matches as a table. Paging a catalog table with `--cursor`
> (`client.search.filtered_page`) is not deprecated; it pages automatic search
> and QA tables too.

Answers questions that filter, rank, or count documents and register entries
by their values: amounts, dates, parties, statuses, document types. The answer
is a table with one row per matching document or entry, and a total.

* **Returns:** a job whose result has `rows`, `columns`, `total_matches`,
  `total_files`, `exhaustive`, and `next_cursor` for the next page.
* **Use when:** the question has conditions such as "over 1,000 USD",
  "dated in March", "from vendor X", "not yet paid", or asks for "all",
  "how many", or "top N".
* **Do not use when:** you want one fact or an explanation (automatic
  search); the rows are journal or ledger lines (journal-entry
  testing); you need arithmetic across workbooks (table QA).

```bash theme={"dark"}
ndi automatic-search "all invoices over 10000 USD dated in March 2025" --mode agent
ndi automatic-search "top 10 contracts by value signed in 2025, with counterparty and end date" --mode agent
ndi filtered-search --cursor <next_cursor>
```

Name the columns you want beside each match in the question ("with
counterparty and end date"). When `exhaustive` is false, `total_matches` is a
floor, not a census. Page with `--cursor`, pasting the previous result's
`next_cursor`.

## Common mistakes

* **`--mode fact` for a "why" question.** The fact route quotes what is
  written; it does not explain or compare. Use `--mode agent`, or omit
  `--mode`.
* **Starting a new search for a follow-up.** Automatic search keeps no
  conversation; "and for FY2024?" means nothing on its own. Use QA with the
  previous turn's `session_id`.
* **File QA across many files.** File QA reads exactly one file. If you do not
  know which file holds the answer, use automatic search.
* **Automatic search over ledger lines.** Its catalog lists documents and
  register entries. Tests over journal lines are journal-entry testing.
* **Journal-entry testing on non-ledger data.** A trial balance or a fixed
  asset register is table QA.
* **Table QA on PDFs.** Table QA reads spreadsheets and table files. Ask a PDF
  with file QA, or search across PDFs with automatic search.
* **Asking before ingestion finishes.** Every tool answers only from files
  that are already ingested.
* **Expecting graph answers without a build.** The knowledge graph does
  nothing until it has been built for the workspace.

See [Search methods](/concepts/search-methods).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.