> ## 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.

# QA

> Ask questions about a workspace as a conversation

`search.qa` asks one question about a workspace as one turn of a conversation.
It returns a `Promise<Job>` whose result is an `AutomaticAgentResult`. Every
turn runs the automatic search agent on its own: it opens with what a search of
the workspace finds, answers from that when it can, and otherwise reads
documents, queries tables, and selects documents from the metadata catalog.
Files must already be [ingested](/guides/ingest-reconcile).

***

## A conversation

```ts theme={"dark"}
import { NdiClient, UnknownResult } from "ndi-sdk";

const client = new NdiClient();
const workspaceId = "550e8400-e29b-41d4-a716-446655440001";
const first = await client.jobs.wait(
  (await client.search.qa(workspaceId, { query: "What was total freight spend in FY2024?", wait_seconds: 30 })).job_id,
  { timeout: 600 },
);
const result = first.result;
if (result && !(result instanceof UnknownResult) && result.result_type === "automatic_agent") {
  console.log(result.answer.answer);
  const followUp = await client.search.qa(workspaceId, {
    query: "and for FY2023?",
    session_id: result.answer.session_id,
    wait_seconds: 30,
  });
  console.log(followUp.job_id);
}
```

On a genuinely ambiguous question the agent may ask back instead of answering:
`answer.clarification_questions` lists the questions and `answer.answer` is
`null`. Reply with `clarification_answers` and the same `session_id`, or pass
`allow_clarification: false` to have every turn answer under the agent's best
reading.

***

## Structured answers

Pass `output_schema` (a JSON Schema object, under the same rules as
[automatic search's](/sdks/typescript/automatic-search#structured-answers)) to
get a turn's answer as JSON. On success `answer.answer` is the JSON text and
`answer.structured_answer` the parsed value, `null` if the answer still missed
the schema after the run's corrections. A field the source does not state comes back `null`, whatever type the schema gives it.

The schema applies to the turn that sends it, not to the conversation. To
keep structured answers across a conversation, send it on every turn,
including the one that carries `clarification_answers`. A turn that asks
clarifying questions returns them with `structured_answer` set to `null`.

```ts theme={"dark"}
import { NdiClient, UnknownResult } from "ndi-sdk";

const client = new NdiClient();
const workspaceId = "550e8400-e29b-41d4-a716-446655440001";
const queued = await client.search.qa(workspaceId, {
  query: "What were total liabilities at year end?",
  output_schema: {
    type: "object",
    required: ["total", "currency"],
    properties: { total: { type: "number" }, currency: { type: "string" } },
  },
  wait_seconds: 30,
});
const job = await client.jobs.wait(queued.job_id, { timeout: 600 });
const result = job.result;
if (result && !(result instanceof UnknownResult) && result.result_type === "automatic_agent") {
  console.log(result.answer.structured_answer);
}
```

***

## Method signature

```text theme={"dark"}
qa(
  workspace_id: string,
  opts: {
    query: string;
    context?: string | null;
    path_prefix?: string | null;
    paths?: string[] | null;
    session_id?: string | null;
    allow_clarification?: boolean | null;
    clarification_answers?: ClarificationAnswer[] | null;
    output_schema?: Record<string, unknown> | null;
    wait_seconds?: number;
    idempotency_key?: string | null;
  },
): Promise<Job>
```

### Parameters

| Parameter | Type | Required | Description |
| - | - | - | - |
| `workspace_id` | `string` | Yes | Workspace that already holds ingested files |
| `opts.query` | `string` | Yes | The question, in the conversation's own words |
| `opts.context` | `string` | No | Extra background the model should treat as given. Omit it to use the workspace's `global_context`; `""` runs with none |
| `opts.path_prefix` | `string` | No | Scope the conversation to this workspace subtree |
| `opts.paths` | `string[]` | No | Scope the conversation to exactly these uploaded files. Adds to `path_prefix` |
| `opts.session_id` | `string` | No | Continue a conversation: an earlier result's `answer.session_id` |
| `opts.allow_clarification` | `boolean` | No | Let the agent ask back on an ambiguous question. On when omitted |
| `opts.clarification_answers` | `ClarificationAnswer[]` | No | Answers to the last turn's `clarification_questions`, one per question, in order |
| `opts.output_schema` | `Record<string, unknown>` | No | JSON Schema this turn's answer must follow. Send it on every turn that should answer in that shape. See [Structured answers](#structured-answers) |
| `opts.wait_seconds` | `number` | No | Hold the submission request open (max 300) |
| `opts.idempotency_key` | `string` | No | Override the key the SDK normally mints |

### Returns

A `Job`. On success, `job.result` is an `AutomaticAgentResult`
(`result_type: "automatic_agent"`): `answer`, a `DeepSearchV2Result` with the
answer, citations, SQL receipts, `session_id` and any `clarification_questions`;
and `catalog`, a `FilteredSearchResult` present when the agent answered from the
metadata catalog.


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