> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stefanbrain.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Follow the content contract in AGENTS.md.
> Treat text as the canonical explanation; video and screenshots enhance it.
> Do not publish unverified product behavior or duplicate an existing canonical article.

# Files

> Stage local files for StefanBrain asks, or list and download the current workspace files from a chat.

The Files API supports two workflows:

* Stage local files for `ask_stefanbrain` and receive temporary `upload_...` identifiers.
* List and download the current workspace files produced inside a chat.

Use a full-access `stefan_sk_...` key or one scoped to include `runs`, or any `stefan_oat_...` OAuth token (OAuth tokens are not scoped). A key that stages files and then calls `ask_stefanbrain` needs both `runs` and `tools`.

## Stage files for an ask

Send `multipart/form-data` to `POST /api/developers/v1/files`. Add each file as a repeated `files` field.

```bash theme={"system"}
curl -X POST https://stefanbrain.com/api/developers/v1/files \
  -H "Authorization: Bearer stefan_sk_your_key_here" \
  -F "files=@./ad.png" \
  -F "files=@./brief.pdf"
```

One request accepts up to 10 files. Supported types include images, PDFs, Office documents, spreadsheets, and text files.

Audio and video files are rejected. Host the media and put its public URL in the `ask_stefanbrain` message instead.

The endpoint returns `201`:

```json theme={"system"}
{
  "object": "uploaded_file_list",
  "files": [
    {
      "object": "uploaded_file",
      "id": "upload_550e8400e29b41d4a716446655440000",
      "name": "brief.pdf",
      "mime_type": "application/pdf",
      "size_bytes": 248310,
      "expires_at": "2026-08-28T16:00:00.000Z"
    }
  ],
  "guidance": "Pass these ids in ask_stefanbrain's `file_ids` so StefanBrain can see the files. Ids stay valid for 24 hours and can be reused across asks."
}
```

Pass the identifiers in `file_ids`:

```bash theme={"system"}
curl -X POST https://stefanbrain.com/api/developers/v1/tools/ask_stefanbrain \
  -H "Authorization: Bearer stefan_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "arguments": {
      "message": "Review the ad against the attached brief and rank the changes to make first.",
      "file_ids": [
        "upload_550e8400e29b41d4a716446655440000"
      ]
    }
  }'
```

Uploads expire after 24 hours and can be reused across asks before they expire. Staging consumes a request-limit unit but does not use the API wallet or plan pool.

<Note>
  An MCP client that cannot send multipart HTTP can call `stage_file` with a public URL or base64-encoded file. Its `upload_id` works in the same `file_ids` field.
</Note>

## List a chat's current workspace files

Use `GET /api/developers/v1/files?chat_id=...` to retrieve the current workspace file heads from an accessible chat.

```bash theme={"system"}
curl "https://stefanbrain.com/api/developers/v1/files?chat_id=chat_your_chat" \
  -H "Authorization: Bearer stefan_sk_your_key_here"
```

The response is a complete snapshot, not a paginated history:

```json theme={"system"}
{
  "object": "workspace_file_list",
  "chat": "chat_your_chat",
  "complete": true,
  "files": [
    {
      "object": "workspace_file",
      "id": "cf761b140dbf0a777ea2a27e20fc53a8afd4be1712d688f87b7deb7b2b40eb1f",
      "file_name": "positioning-brief.md",
      "path": "deliverables/positioning-brief.md",
      "media_type": "text/markdown",
      "size_bytes": 18420,
      "sha256": "cf761b140dbf0a777ea2a27e20fc53a8afd4be1712d688f87b7deb7b2b40eb1f",
      "updated_at": "2026-08-27T16:20:00.000Z",
      "download_url": "/api/developers/v1/files/cf761b140dbf0a777ea2a27e20fc53a8afd4be1712d688f87b7deb7b2b40eb1f/download?chat_id=chat_your_chat&path=deliverables%2Fpositioning-brief.md"
    }
  ]
}
```

Each logical file appears once, keyed by `path`. `id` is the SHA-256 of the current head, so `id`, `sha256`, and `download_url` change when the file is edited; list again for the current URL.

Do not send a cursor. The endpoint returns every current workspace file head in one response.

## Download the current file

Request the returned `download_url` with the same credential:

```bash theme={"system"}
curl -L \
  -H "Authorization: Bearer stefan_sk_your_key_here" \
  -o positioning-brief.md \
  "https://stefanbrain.com/api/developers/v1/files/cf761b140dbf0a777ea2a27e20fc53a8afd4be1712d688f87b7deb7b2b40eb1f/download?chat_id=chat_your_chat&path=deliverables%2Fpositioning-brief.md"
```

The response streams the current bytes as an attachment and supports range requests. Workspace heads are mutable, so downloads use `private, no-store` caching.

Listing and downloading are reads of existing work. They do not consume request limits or a billing budget.

## Handle file errors

* `400 invalid_multipart_body`, `no_files`, `too_many_files`, `unsupported_attachment`, or `attachment_too_large`: change the upload.
* `400 chat_id_required`, `invalid_chat`, or `cursor_not_supported`: correct the listing request.
* `400 invalid_file_id`, `invalid_chat`, or `path_required`: request the `download_url` exactly as the listing returned it.
* `404 chat_not_found`: the credential cannot access the requested chat.
* `404 file_not_found`: the file is unavailable or is not a current workspace file head.

See [StefanBrain MCP](/developers/mcp) for in-client staging and [Tools and jobs](/developers/tools-and-jobs) for `ask_stefanbrain` addressing.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.