Skip to content
GET /file

Every file the caller can reach, newest first, never an expired one: the files they OWN, the files an owner SHARED with them by name — directly, through a team they are in, or organization-wide — and the files they reach THROUGH THE SOURCE: `use` or better on the connection the file came through, or on the toolbox it ran in (the toolbox only while it still pins the file’s connection with a working delegation). A file is treated like the connection or toolbox it came out of: if you can use what made it, you see it. No permission is required beyond organization membership: a file has no RBAC verb, and no admin or oversight view exists for it (an org owner sees what they own or reach, like anyone else). **An inherited file your organization’s restrictions block is still listed**, flagged `restricted_by` with `can_open: false`, the way a blocked connection stays in `GET /connection` — restrictions are evaluated per caller in the Worker, not in the list query. `can_open` is also `false` on an inherited toolbox row whose delegation chain no longer holds or whose source connection has been deleted — and on every inherited row that needed a restriction verdict when that lookup failed, since the list fails closed per row rather than predicting that bytes will open. The content route stays the authority. **Cost.** An unfiltered page reads on the order of the number of connections and toolboxes the caller can use times the page size — each source is probed through its own index and stops after one page — not the organization’s file table. A `connector_slug` filter narrows that source set and stays cheap; a rare substring `q` and `created_before` can still read much of what the caller reaches before a page fills (bounded by the 7-day retention). A caller that already ran the tool has the id from the tool result and can skip this entirely. `?q=`, `?scope=`, `?connection_id=`, `?connector_slug=`, `?type=` and `?created_before=` are all resolved server-side, inside the query, before paging — never a client-side filter over a page already fetched. Every row says how the caller reaches it (`access`, `access_via` — which for a file may also be `connection` or `toolbox`), whether it will open (`can_open`, `restricted_by`) and what they may do to it (`can_delete`, `can_share`, `can_see_shares`, `can_revoke_share` — all owner-only). A row the caller does not own omits the owner’s provenance — `connection_id`, `toolbox_id`, `tool_name` and `delegated_by_user_id` — and `access_summary`; it keeps the file, `connector_slug`, `tool_resource` / `tool_method` (the words for “from …”), `created_by` and the caller’s own `access_via`.

Query Parameters

qstring

Case-insensitive substring match on the file’s filename. LIKE wildcards are matched literally. Max 200 characters.

scopestring

owned — only files the caller ran the tool for. shared — every visible file the caller did NOT make: explicitly shared with them AND reached through a source connection or toolbox. Absent — both.

Possible values:
ownedshared
connection_idstring

Only the caller’s OWN files produced through this connection. A row the caller does not own does not disclose its connection_id, so this filter never matches one.

connector_slugstring

Only files produced through a connection of one of these connectors. Comma-separated or repeated, at most 25 (more is a 400). Files with no surviving connection never match.

typestring

Only files of these kinds (the row’s kind). Comma-separated, any of document, spreadsheet, presentation, image, archive, other (anything else is a 400).

created_beforestring · date-time

Only files created strictly before this ISO-8601 timestamp.

Response Body

can_uploadboolean
next_cursorstring,null
prev_cursorstring,null
resultobject[]
connectorobject,null
required·

connector_slug's display data — the same stamp a GET /connection row carries, resolved in one batch for the page. Null exactly when connector_slug is; a connector the catalog cannot resolve reads its slug as label and a null logo.

3 properties
labelstring
logostring,null
upstream_slugstring

Only on a fork whose upstream the caller may see — the GET /connector rule.

accessstring

The caller’s own reach: owner for a file they ran the tool for, view for one an owner shared with them or one they reach through its source connection or toolbox.

Possible values:
ownerview
access_summaryobject

The file’s audience as a bounded rollup — present on rows the caller OWNS only, never on a file shared with them (that summary describes the owner’s audience, which a grantee has no business seeing). Grants are view only. Page GET /file/{id}/share for the grants themselves.

5 properties
org_levelstring,null

Level of the org-wide grant, or null when there is none.

Possible values:
viewuseeditnull
previewobject[]

At most 5 grantees, broadest first (org, then teams, then members), for a hover preview.

4 properties
grantee_idstring,null
grantee_typestring
Possible values:
userteamorg
levelstring
Possible values:
viewuseedit
namestring,null

Display name; null for org grants and for grantees no longer in the org.

team_countinteger
totalinteger

Every grant, the org-wide one included.

user_countinteger
access_viastring

How the CALLER reaches this file — not who else can. owner — they own it. direct — a grant naming them personally. team — a grant to a team they belong to (or, per §6.2, one they administer), named in access_via_team. org — an organization-wide grant. When several sources apply the BROADEST wins and the caller's access level is not consulted: owner, else org, else team, else direct. So a caller granted edit personally AND view org-wide reads org — a narrower grant must never mask org-wide exposure, since this field says how far the file reaches, not what the caller may do with it. Deliberately NOT gated on can_see_shares, and deliberately not a widening of it: this is the caller's OWN grant and their OWN team memberships, so a view/use grantee receives it while the grantee list — information about colleagues — stays closed to them. No other member is ever named. ABSENT when nothing reaches the caller — a transfer answering the ex-owner of a resource that was never shared, or a catalog row browsed with no grant behind it. Absent means "no source to name", never "not permitted", and never an implied org.

A file adds two values for reach nobody granted by name (tier 3, docs/access-model.md §15): connection — the caller has use or better on the connection the file came through; toolbox — they have use or better on the toolbox it ran in (and that toolbox still pins the file’s connection). A named grant (direct / team / org) outranks both when it also applies, and toolbox outranks connection when a file is reached both ways. Only file rows carry connection / toolbox.

Possible values:
ownerdirectteamorgconnectiontoolbox
access_via_teamobject

Present exactly when access_via is team, absent otherwise. The team the caller reaches this file through — one of their own teams, never a disclosure about anybody else.

2 properties
idstring

Team id (team_…).

namestring,null

Team display name, or null when the team no longer resolves in the directory — the same null contract every resolved grantee name carries.

can_deleteboolean

Whether the caller may DELETE /file/{id} — the owner, and only the owner. No permission verb to mirror.

can_openboolean

Whether the file’s bytes will be served to the caller right now. true for an owned or explicitly shared row. On an inherited row it is false when an org restriction blocks the connector or the tool (restricted_by is then set), when the toolbox’s delegation chain no longer holds (its pinning entry’s delegator lost use), or when the source connection has been deleted (the read path fails closed) — in the last two restricted_by is null. If the org-restriction lookup itself errors, the list fails closed per row: every inherited row that needed a verdict reads false (and restricted_by null). A prediction made for the page, not a second gate: GET /file/{id}/content stays the authority and answers its uniform 404 for anything this says false to. Read this instead of re-deriving it.

can_revoke_shareboolean

Whether the caller may DELETE /file/{id}/share/{aclId} — the owner, and only the owner. Named for the question it answers: no file:revoke verb exists.

can_see_sharesboolean

Whether the caller may GET /file/{id}/share — the owner, and only the owner. Read this rather than inferring "not permitted" from the absence of access_summary.

can_shareboolean

Whether the caller may POST /file/{id}/share — the owner, and only the owner. There is no file:share permission; ownership is the gate.

connection_idstring,null

Provenance, and the source of the inherited reach: a member with use or better on this connection sees the file in GET /file and can open it. Never the owner’s gate — a use grantee can own a file made over a connection they cannot see at all (delegation is disclosed, not gated). Present only on rows the caller owns — omitted, not null, on a file shared with them.

connector_slugstring,null

Connector of the connection the file came through (the ?connector_slug= filter). Null when it came through no connection or that connection has since been deleted.

created_atstring · date-time
created_byobject

Who ran the tool, resolved in one batch for the page. name is null for someone who has left the organization.

2 properties
idstring

User id (usr_…) — same value as created_by_user_id.

namestring,null
created_by_user_idstring

The member who RAN the tool — the file’s owner, and the first of its three access tiers (see GET /file/{id}/content). Not the owner of the connection behind it.

delegated_by_user_idstring,null

Set when the tool ran over a connection delegated to the creator; null when they used their own. Present only on rows the caller owns — omitted, not null, on a file shared with them.

expires_atstring · date-time

Defaults to 7 days after creation. Enforced at read time: past this instant the file is absent from GET /file and answers 404 on the content route, for the owner and every grantee alike. A daily sweep then deletes the R2 object and this row.

filenamestring
idstring

File id (file_…).

kindstring

Coarse bucket derived from the stored mime by the same classifier ?type= filters with, so a row’s kind and the filter cannot disagree. other is everything the five named kinds do not claim.

Possible values:
documentspreadsheetpresentationimagearchiveother
mimestring
organization_idstring
originstring

How the bytes got here: tool (a connector tool returned them), upload (a person chose it on the Files screen), request (a person handed it to an app that asked, through an upload request; origin_host is the app), inline (content an assistant wrote with file.create), api (a backend sent it to POST /file with a token), url_import, web_fetch (fetched from the web), export (produced by an export). Provenance for wording (“Uploaded by Dana”), never an access gate.

Possible values:
tooluploadrequestinlineapiurl_importweb_fetchexport
origin_hoststring,null

The web host an import or web fetch read from, or the asking app’s name for origin request; null otherwise.

restricted_bystring,null

Which precedence layer’s restriction blocks this file for the caller, null when none does. Same field and meaning as restricted_by on a connector or connection row — adds which to can_open’s whether, and no rule id, author or reason. Non-null only on inherited rows (access_via connection / toolbox): the restriction veto applies to tier 3 alone, so an owned or explicitly shared row is always null, and it is null on an inherited row that will not open for any other reason (a lapsed toolbox delegation, a deleted connection). The row is LISTED and flagged, never omitted — restrictions are evaluated per caller in the Worker, not in the list query — so a page can hold rows the caller cannot open.

Possible values:
roleusernull
size_bytesinteger
tool_methodstring,null

Catalog method of the tool that produced the file (e.g. export), safe to show a person. Set on every file; typed nullable in the schema only, like tool_resource.

tool_namestring,null

The model-facing tool name; can be an opaque tb__… string. Never show it to a person — word tool_resource and tool_method instead. Present only on rows the caller owns — omitted, not null, on a file shared with them.

tool_resourcestring,null

Catalog resource of the tool that produced the file (e.g. slides), safe to show a person. Set on every file, because only a connected tool can produce one (a synthetic tool never does); typed nullable in the schema only; no code path writes a null.

toolbox_idstring,null

Provenance, and the second source of inherited reach: use or better on this toolbox lists and opens the file (while the toolbox still pins the file’s connection). Present only on rows the caller owns — omitted, not null, on a file shared with them.

upload_limitsobject
acceptarray,null

Allowed media types (image/*, application/pdf) and extensions (.csv); null means anything.

max_file_bytesinteger

The most one file may be. 95 MiB, or the request’s lower setting.

max_filesinteger

Files at most. 10.

max_total_bytesinteger

The most the whole request may hold. 250 MiB.

curl -X GET 'https://api.elaichi.ai/file' \
  -H 'Authorization: Bearer $ELAICHI_API_TOKEN' \
  -H 'Content-Type: application/json'
const response = await fetch('https://api.elaichi.ai/file', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer ' + process.env.ELAICHI_API_TOKEN,
    'Content-Type': 'application/json',
  },
});

const data = await response.json();
console.log(data);
import os
import requests

url = "https://api.elaichi.ai/file"
headers = {
    "Authorization": f"Bearer {os.environ['ELAICHI_API_TOKEN']}",
    "Content-Type": "application/json",
}

response = requests.get(url, headers=headers)
print(response.json())