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

# Ask

> Answer a question from the content of a workflow's documents, with citations

*Rate limit tier: **extraction** (60 req/min) — see [Rate limits](/api-reference-v3/introduction#rate-limits).*

Asks a question about what a workflow's documents **say** — contract terms, invoice details, anything that needs reading rather than a field lookup. Extraction answers "what is in this document"; this answers "what do these documents say about X", across the whole workflow.

Every answer comes back with the exact quotes it rests on, each resolved to a page and a region in the source PDF. That is the point: you can show a customer where a number came from instead of asking them to trust it.

## Requires a knowledge base

The workflow needs a **knowledge node** — that is what indexes its documents into a corpus. Without one, this returns `409 KNOWLEDGE_NOT_ENABLED`. Add the node in Studio, or include it when you create the workflow.

The corpus is **workflow-scoped, not version-scoped**: it accumulates the latest parse of every live file across all runs and versions. A document parsed under an older workflow version is still answerable.

## Errors

| Status | `error_code`            | Meaning                                                                                 |
| ------ | ----------------------- | --------------------------------------------------------------------------------------- |
| `402`  | `PAYMENT_REQUIRED`      | The organization has no credits. Refused before the question runs — nothing is charged. |
| `404`  | `NOT_FOUND`             | Unknown workflow, or one belonging to another organization.                             |
| `409`  | `KNOWLEDGE_NOT_ENABLED` | No knowledge node on the workflow. Permanent until you add one — do not retry.          |
| `409`  | `KNOWLEDGE_NOT_READY`   | The first index is still building. Retry shortly.                                       |

## Follow-up questions

Pass a `thread_id` you mint yourself, starting with `kb-`, to ask in the context of earlier questions in that thread. Omit it and each question stands alone. Reuse the same id to continue a conversation.

## Billing

Charged per question, metered on the text the agent actually reads — so a narrow question over a small corpus costs a fraction of a broad sweep over a large one. A question the organization cannot pay for is refused up front rather than answered into debt.

<RequestExample>
  ```bash curl theme={null}
  curl -X POST 'https://api.anyformat.ai/v3/workflows/0686bb97-8c30-70f0-8000-97669e000eb8/knowledge/ask' \
    -H 'Authorization: Bearer YOUR_API_KEY' \
    -H 'Content-Type: application/json' \
    -d '{"question": "Which contracts renew before March and what notice do they need?"}'
  ```

  ```python Python (SDK) theme={null}
  from anyformat import Client

  client = Client(api_key="YOUR_API_KEY")
  answer = client.ask(
      "0686bb97-8c30-70f0-8000-97669e000eb8",
      "Which contracts renew before March and what notice do they need?",
  )

  print(answer)
  for citation in answer.citations:
      print(f"  {citation.path} p.{citation.page}: {citation.quote}")
  ```

  ```typescript TypeScript theme={null}
  const response = await fetch(
    'https://api.anyformat.ai/v3/workflows/0686bb97-8c30-70f0-8000-97669e000eb8/knowledge/ask',
    {
      method: 'POST',
      headers: {
        'Authorization': 'Bearer YOUR_API_KEY',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        question: 'Which contracts renew before March and what notice do they need?',
      }),
    },
  );

  if (!response.ok) {
    throw new Error(`API error: ${response.status}`);
  }

  const { answer, citations } = await response.json();
  console.log(answer);
  ```
</RequestExample>

<ResponseExample>
  ```json Response (200 OK) theme={null}
  {
    "answer": "Two contracts renew before March. Meridian MSA renews 2026-02-01 and needs ninety days' notice to terminate. Aldrete supply agreement renews 2026-02-14 with thirty days' notice.",
    "citations": [
      {
        "path": "2026-07/meridian-msa-2024.md",
        "quote": "Either Party may terminate upon ninety (90) days' notice.",
        "file_id": "0192f0c1-a2b3-4c5d-8000-abcdef012345",
        "block_id": "block-2",
        "page": 2,
        "bbox": { "x0": 0.08, "y0": 0.31, "x1": 0.92, "y1": 0.36 }
      }
    ],
    "path": [
      { "tool": "kb_grep", "args": { "pattern": "renewal" } },
      { "tool": "kb_read", "args": { "path": "2026-07/meridian-msa-2024.md" } }
    ],
    "steps_used": 2,
    "thread_id": null
  }
  ```

  ```json Response (409 — no knowledge base) theme={null}
  {
    "error": "Knowledge base not enabled",
    "detail": "This workflow has no knowledge base. Add a knowledge node to the workflow to start indexing its documents.",
    "error_code": "KNOWLEDGE_NOT_ENABLED",
    "retryable": false
  }
  ```

  ```json Response (402 — out of credit) theme={null}
  {
    "error": "Payment required",
    "detail": "insufficient_credit",
    "error_code": "PAYMENT_REQUIRED",
    "retryable": false
  }
  ```
</ResponseExample>
