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

# file_metadata

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

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

Describe one uploaded file without opening it: what it is, its category, and the parts you can ask about.

**Cost class: fast.**

``path`` is the file's full path relative to the workspace root, exactly
as it was uploaded and as ``folder-metadata``, ``GET .../files`` and search
results (``source_path``) print it — for example
``evidence/doc-7f83/contract.pdf``. A bare file name (``contract.pdf``), a
folder, a file id, or a path that starts with the workspace id is not
found. ``file_not_found`` is final for that value: do not retry it or guess
variants — list the folder with ``folder-metadata`` (``name_contains=`` a
part of the name) and use the path it prints.

The response gives the file's ``category``, a ``summary`` of its first
section or tab, page or tab counts, and its ``components``: each section,
tab, image or recording with its own summary, categories, page range or
table shape, and ``key_terms`` — keywords whose role the source states
(who a name is, what a date marks). A recording reports its duration.
``readable`` means ``qa-file`` can answer from the component's text;
``queryable`` means it is a table ``query-tables`` can compute over. Pass a
component's ``name`` as ``component`` to scope either tool to it.

``component`` narrows this response to one section or tab by name; a name
that matches nothing is refused with ``invalid_request`` listing the valid
names. A file that is not ingested yet answers 200 with its
``ingestion_status`` and no components; a failed one carries
``ingestion_error``.

Args:
    body: The uploaded file's path, and optionally one component name to
        scope the response to.
    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

- FileMetadataRequest: Request body for ``file-metadata``. ``path`` names one uploaded file.
  - `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.
  - `component` string, nullable: Describe only this section or tab, named exactly as an unscoped call lists it.

## Response `200`

Successful Response

- FileMetadataToolResponse: Response body for ``file-metadata`` — what one uploaded file is, without opening it. ``summary`` is the summary of the file's first section or tab, never a concatenation across components (each component carries its own). A file that is not ingested yet answers 200 with its ``ingestion_status`` and no components; a failed one also carries ``ingestion_error``.
  - `path` string, required: The uploaded file's path relative to the workspace root, as requested.
  - `file_id` string, uuid, required: The uploaded file's id, as the files endpoints address it.
  - `kind` string, nullable: Ingestion lane (document / table / media), when known.
  - `ingestion_status` 'discovered' | 'queued' | 'ingesting' | 'ingested' | 'stale' | 'expired' | 'failed' | 'not_required', required: The only thing that says whether a file is searchable. ``expired`` cannot be derived from the hash pair: retention clears ``ingested_hash``, so an aged-out file and a never-ingested file look identical. They call for different customer actions, so the status carries the distinction the hashes cannot.
  - `ingestion_error` string, nullable: Why ingestion failed, when the ledger recorded a reason.
  - `size_bytes` integer, required: Byte size of the uploaded source object.
  - `category` string, nullable: The file's primary category path, when classified.
  - `page_count` integer, nullable: Pages in a paged document; absent for other files.
  - `tab_count` integer, nullable: Tabs in a workbook; absent for other files.
  - `summary` string: Summary of the file's first section or tab; each component has its own.
  - `components` union[]: The file's sections, tabs, image or recording, in reading order.
    - union
      - FileComponentInfo: One part of an uploaded file that can be asked about by name: a document section, a spreadsheet tab, an image, or the raw upload itself.
        - `kind` 'section' | 'tab' | 'image' | 'raw', required: What the component is: a document `section`, a spreadsheet `tab`, an `image`, or the `raw` file.
        - `name` string, required: The component's name — pass it as `component` to scope a question; empty when the whole file is the unit.
        - `summary` string: What the component contains, as summarized at ingestion.
        - `categories` string[]: Category/subcategory paths assigned to this section or tab at ingestion.
        - `key_terms` FileKeywordDescription[]: Up to FILE_METADATA_MAX_KEY_TERMS of the component's extracted keywords whose role the source states (who a name is, what a date or number marks), in extraction order. Empty for files ingested before keyword roles were extracted.
        - `page_start` integer, nullable: First source page covered, 1-based (sections).
        - `page_end` integer, nullable: Last source page covered, 1-based (sections).
        - `row_count` integer, nullable: Tabs: rows in the queryable table; sections: rows in their rows tables; else the source extent.
        - `column_count` integer, nullable: Columns in the component's table, when it has one.
        - `readable` boolean, required: True when the component has text a question about the file can be answered from.
        - `queryable` boolean, required: True when the component is a table that query-tables can compute over.
        - `error` string, nullable: Why ingestion could not read this component; it stays listed but has no content.
      - MediaComponentInfo: A recording: timeline facts, no paths — ask about it with qa-file.
        - `kind` 'media'
        - `modality` 'audio' | 'video', required: Whether the recording is `audio` or `video`.
        - `duration_ms` integer, required: Length of the recording in milliseconds.
        - `speaker_count` integer, required: Distinct speakers detected in the recording.
        - `summary` string: What the recording covers, as summarized at ingestion.
  - `figures` FileFigureInfo[]: PDFs: pictures inside the pages (seals, charts, photos, stamps), in page order; with `component`, only those on that component's pages. Empty for other files and for PDFs ingested before figures were recorded.
    - `name` string, required: `figure p<page>-<n>`. Pass it as qa-file's `component` to ask the vision engine about this figure alone.
    - `page` integer, required: 1-based page the figure is on.
    - `description` string, nullable: What the figure shows, written by a vision model at ingestion; null when not described.
    - `text` string: Text printed inside the figure, as OCR read it.
  - `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)
