---
title: "get_file"
method: GET
path: "/v1/workspaces/{workspace_id}/files/{file_id}"
tags: ["files"]
---

# get_file

`GET /v1/workspaces/{workspace_id}/files/{file_id}`

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

Return a file plus a short-lived presigned download URL. Cost class: **fast**.

No separate ``GET .../content`` route: the presigned URL keeps raw bytes
off the API path.  The URL expires after ``DOWNLOAD_URL_TTL_SECONDS``
seconds; fetch it promptly.

A direct read of a named file is the one case where hard denial
(``403 access_denied``) is correct: the caller named the file, so there
is nothing to narrow and nothing to infer — they already knew it existed.

Args:
    context: Resolved workspace plus the caller's access predicate.
    session: Database session.
    file_id: The file to fetch.

Returns:
    ``FileDetail`` including the presigned ``download_url``.

Raises:
    PlatformError: ``file_not_found`` when the file does not exist.
    PlatformError: ``access_denied`` when the caller does not hold the
        required access label.

## Path parameters

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

## Response `200`

Successful Response

- FileDetail: A source file plus short-lived presigned URLs for its bytes. No separate ``GET .../content`` route: a presigned URL keeps raw bytes off the API path and avoids charging for a second network hop inside the platform. Both URLs expire after ``DOWNLOAD_URL_TTL_SECONDS`` seconds; fetch them promptly. The two differ only in the response headers S3 returns: ``download_url`` saves to disk, ``preview_url`` renders in place under the file's real media type. One URL cannot do both — a browser decides from the headers.
  - `file_id` string, uuid, required
  - `workspace_id` string, uuid, required
  - `path` string, required: Full path relative to the workspace root. Send this exact string wherever a tool takes `path`: tools match it exactly.
  - `file_name` string, required: The last segment of `path`, for display only. A tool given it as `path` answers file_not_found.
  - `file_type` string, required
  - `size_bytes` integer, required
  - `observed_hash` string, required
  - `ingested_hash` string, nullable: Content hash at the last successful ingestion. Null with ingested_at means never ingested.
  - `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.
  - `ingested_at` string, date-time, nullable
  - `version` integer, required: Starts at 1; increments on every content replacement.
  - `category` string, nullable: Null until categorised.
  - `access_label` string, nullable: Exactly one label, or null when the workspace declares none.
  - `access_label_source` 'inherited' | 'classified' | 'mirrored': Which mechanism produced a document's label. The ``default_label`` fallback records as ``inherited``, so an audit can tell a real classification from a fallback.
  - `expires_at` string, date-time, nullable: When derived material for this file ages out.
  - `graph_inclusion` 'auto' | 'include' | 'exclude': This file's knowledge-graph inclusion policy, not its effective membership. 'auto' defers to the workspace's KG exclusion rules; 'include'/'exclude' override them for this one file. A file excluded by a workspace rule still reads 'auto' here, so this does not on its own tell an excluded file from one whose graph is not built yet — read representations.knowledge_graph for whether a build has covered it.
  - `labels` object: Caller-supplied metadata.
  - `created_at` string, date-time, required
  - `updated_at` string, date-time, required
  - `download_url` string, required: Time-limited presigned GET URL that saves the file's bytes to disk.
  - `preview_url` string, required: Time-limited presigned GET URL that a browser renders inline.
  - `download_url_expires_at` string, date-time, required: When the presigned URLs stop being valid.

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