---
title: "Download a tool file’s bytes"
method: GET
path: "/file/{id}/content"
tags: ["Files"]
---

# Download a tool file’s bytes

`GET /file/{id}/content`

## Request

- Base URL: `https://api.elaichi.ai`
- URL: `https://api.elaichi.ai/file/{id}/content`
- Auth: HTTP bearer

Downloads the file. Streams the bytes (never JSON on success) with `Content-Disposition: attachment` and `X-Content-Type-Options: nosniff`. Supports `Range` (standard partial-content semantics; answers `206` with `Content-Range` when the request carries one).

**Three things decide who may open a file, checked on every request:** (1) the owner: the member who ran the tool, always (deliberately not the owner of the connection behind it, because the person a file was made for routinely has no access to that connection); (2) anyone the owner shared it with (`POST /file/{id}/share`): a user, a team they are in, or the whole organization, at `view`; (3) anyone with `use` or better on the connection or the toolbox the file came from, because they could have produced the same output. The third follows those grants as they move (revoke someone’s toolbox share and the file goes with it) and is vetoed by the caller’s own organization restrictions on that connector: a member a restriction blocks from the connector cannot open its files through this route either. A file reached the third way is listed by `GET /file` (flagged `restricted_by` and `can_open: false` when a restriction blocks it), but this route stays the authority. No permission gates any of it: an org permission plays no part in a file’s reach, and org owners and admins get no extra read.

Send no `X-Organization-Id` header: both a session cookie and an org API token work here, and an API token’s own organization is checked against the file’s organization, so a token minted in one org can never read a file in another, even when the same person is a member of both. The caller must also be a current member of the file’s org, not merely its creator back when the tool ran.

Every refusal answers the identical `404`, never `403`: a malformed or unknown id, a caller none of the three reaches, an access through a connection or toolbox that a restriction vetoes (or whose source connection has since been deleted), an API token pinned to another organization, a member who has left the org, an expired file, an Elaichi support session impersonating the owner in an org that turned support access off, a suspended org, and an org with no active plan. Existence is not disclosed to anyone the file does not reach, and the response never names which check failed. The one other refusal is `416` for a `Range` that starts at or past the end of the file.

## Path parameters

- `id` string, required

## Headers

- `Range` string

## Response `200`

The full file.

## Other responses

- `206`: Partial content, honoring the `Range` request header.
- `400`: Bad request
- `401`: Unauthorized
- `403`: Forbidden
- `404`: Not found: the one answer for every refusal (unknown id, a file the caller cannot reach, access through a connection or toolbox that a restriction vetoes, a cross-org token, a caller who has left the org, an expired file, a blocked support session, a suspended org, or an org with no active plan)
- `416`: The `Range` header cannot be satisfied (it starts at or past the end of the file). No body; `Content-Range` carries the real size.
- `429`: Rate limited

---

[API](https://skmtc.dev/elaichi/apis/elaichi-api.md) · [All operations](https://skmtc.dev/elaichi/apis/elaichi-api/llms.txt) · [OpenAPI document](https://skmtc.dev/elaichi/apis/elaichi-api/revisions/b22cfb7b6d15?raw)
