---
title: "read_file"
method: POST
path: "/v1/workspaces/{workspace_id}/tools/read-file"
tags: ["tools"]
---

# read_file

`POST /v1/workspaces/{workspace_id}/tools/read-file`

## 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}/tools/read-file`
- Auth: API key in header `X-API-Key`
- TypeScript: <https://skmtc.dev/nace/apis/ndi-service/docs/v1/workspaces/:workspace_id/tools/read-file/post.ts> (a function with typed inputs; its response is checked against the schema)

Read the text of one uploaded file.

**Cost class: fast.**

``path`` names an **uploaded source file** — the only namespace this tool
serves.  A workspace-internal path is refused with ``invalid_request``
naming the uploaded file to use instead.

A file with several components (a spreadsheet's tabs, a pdf's sections)
answers with their text concatenated in the file's own order, each part
under a one-line header naming the component; ``components_returned`` and
``components_omitted`` report exactly what made it into the budget.  Scope
the read with ``component=<name>`` (the names ``file_metadata`` lists)
when only one tab or section matters, and with ``pages`` (1-based) for
paged documents — spreadsheets have no pages, so scope those by component
instead.  Images are refused — ask about them with ``qa_file``.

Args:
    body: Source path, optional component name, optional page list, and
        character limit.
    workspace: Resolved workspace and caller access predicate.  A
        non-existent workspace returns ``workspace_not_found`` before the
        body executes.
    session: Database session.

## Path parameters

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

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

- ReadFileRequest: Request body for ``read-file``. ``path`` names an **uploaded source file** — the only namespace this tool serves. A workspace-internal derivative path is refused with ``invalid_request`` naming the uploaded file that owns it. ``component`` scopes the read to one public component by name — a spreadsheet tab or a pdf section, as listed by ``file-metadata``. ``pages`` is 1-based and selects on the file's absolute page numbers; out-of-range pages are ignored (absent from ``pages_returned``). Selected pages come back de-duplicated in document order, whatever order they were asked in. Spreadsheets have no pages — scope those with ``component`` instead.
  - `path` string, required: The uploaded file's full path relative to the workspace root, exactly as it was uploaded and as listings and search results print it — e.g. `evidence/doc-7f83/contract.pdf`. Not a bare file name (a file's `file_name`), a folder, a file id, or a path that starts with the workspace id.
  - `pages` integer[], nullable: 1-based page numbers to read; omitted: every page. Spreadsheets have no pages.
  - `component` string, nullable: One tab or section to read, named as file-metadata lists it; omitted: the whole file.
  - `max_chars` integer: Character budget for the returned text; past it `truncated` is true.
  - `line_start` integer, nullable: First line to return, 1-based, counted in the text this read would return without a line range (after component and page selection). Omitted: line 1.
  - `line_end` integer, nullable: Last line to return, inclusive and 1-based like line_start. Omitted: the last line.

## Response `200`

Successful Response

- ReadFileResponse: Response body for ``read-file``. A file with several components (a spreadsheet's tabs, a pdf's sections) answers with their text concatenated in the file's own order, each part prefixed by a one-line header naming the component. ``components_returned`` lists what contributed to ``content``; ``components_omitted`` lists components that were cut for size — the character budget ran out, or a part was too large to serve — or that have no text rendering at all (a query-only tab; ask it with ``qa-file``) — so an absent component is always visible, never a silently missing tab. Unnamed components (a recording, a raw upload) are listed by kind. A table a PDF page holds comes as rows — one ``| a | b |`` line each under a header that joins the stacked column headings — the way grep and hybrid-search print it.
  - `path` string, required
  - `file_id` string, uuid, required
  - `content` string, required
  - `pages_returned` integer[], required
  - `truncated` boolean, required
  - `components_returned` string[], required
  - `components_omitted` string[], required
  - `sql_tables` string[]: The run-sql names of the tables holding the rows of the long tables printed in the returned sections — count, total or rank those rows there rather than from the text.
  - `line_start` integer, nullable: First line returned (1-based) when the request named a line range; None otherwise.
  - `line_end` integer, nullable: Last line returned (inclusive) when the request named a line range; None otherwise or when line_start is past the end of the text.
  - `line_count` integer, nullable: How many lines the whole selected text has, when the request named a line range; None otherwise.
  - `table` GrepTable: The table a matching line is a row of, so the row reads without another call.
    - `first_line` integer, required: Line of the table's first row (its header), in read-file's line numbers.
    - `last_line` integer, required: Line of the table's last row.
    - `title` string, nullable, required: The text printed just above the table (its caption, units), clipped; None when there is none.
    - `header` string, required: The column names: the table's first row, or for an HTML table every header row joined per column with ' > ' (empty when it has none), clipped when very wide.
    - `rows` integer, nullable: For a table stored as one HTML line (PDF ingestion): how many data rows it has.
  - `job_id` string, uuid, nullable: The external operation job; absent for commands inside a search job.

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