# Files

> Upload documents and images through the REST API and attach them to a message so the agent can read them.

Source: https://docs.runbear.io/api/files

Last updated: 2026-09-30

An agent can read files a user sends with a message: a contract to review, a
spreadsheet to summarize, a screenshot to explain. Through the REST API, you
upload the file first, then attach the result to the message.

## What you need

- An API key with the `chat` capability. See
  [Authentication and API keys](/api/api-keys.md).
- A **thread**. Uploads belong to a thread, so create one with
  `POST /v1/threads` before uploading. See [Chat](/api/chat.md).

## Uploading a file

`POST /v1/files/upload` takes a `multipart/form-data` body with three fields,
all required:

| Field          | Value                      |
| -------------- | -------------------------- |
| `file`         | The file, up to 50 MB      |
| `assistant_id` | The agent's id             |
| `thread_id`    | The thread the file is for |

```bash
curl -s https://api.runbear.io/v1/files/upload \
  -H "Authorization: Bearer $RUNBEAR_API_KEY" \
  -F "file=@q3-report.pdf" \
  -F "assistant_id=$AGENT_ID" \
  -F "thread_id=$THREAD_ID"
```

The response describes the stored file:

```json
{
  "url": "https://storage.googleapis.com/...",
  "name": "attachments/<agent id>/<thread id>/<id>-q3-report.pdf",
  "contentType": "application/pdf"
}
```

> **Warning**
>
> `url` is a signed link that anyone holding it can open until it expires,
> 15 minutes after the upload. Treat it like the file itself: don't log it or
> share it beyond the request that attaches it.

## Supported file types

| Kind          | Types                                                                                          |
| ------------- | ---------------------------------------------------------------------------------------------- |
| Documents     | PDF, Word (`.docx`, `.doc`), Excel (`.xlsx`, `.xls`), PowerPoint (`.pptx`, `.ppt`)             |
| Text and data | CSV, plain text, Markdown, Python (`text/x-python`), JSON, XML (`application/xml`, `text/xml`) |
| Images        | PNG, JPEG, GIF, WebP, SVG                                                                      |

The file's own content type decides whether it is accepted. When the client
sends `application/octet-stream`, `application/zip`, or no type at all, Runbear
infers the type from the file name's extension instead, which is how Office
files that some clients label as zip archives are recognized. Any other type is
refused with `400`.

## Attaching a file to a message

Pass the upload response as an entry of the message's `attachments`, on a run or
a chat completion:

```json
{
  "assistant_id": "<agent id>",
  "messages": [
    {
      "role": "user",
      "content": "What are the three biggest risks in this report?",
      "attachments": [
        {
          "name": "q3-report.pdf",
          "url": "https://storage.googleapis.com/...",
          "contentType": "application/pdf"
        }
      ]
    }
  ]
}
```

Send the message soon after the upload, while the link is still valid. For a
file you need again later, upload it again.

## Limits

- **50 MB** per file.
- **10 attachments per message** on Claude Agent SDK agents. A message with more
  is refused with `422` before the turn starts. Other agent types have no
  per-message count limit from Runbear.
- Each upload counts against the chat
  [rate limit](/api/errors-and-limits.md#rate-limits) for its agent.
- Uploading isn't available with a Web SDK session pass. It needs an API key.
- A thread that doesn't exist, or belongs to another organization, answers
  `404`.

## Related

- [Chat](/api/chat.md) — the message format
- [Errors and limits](/api/errors-and-limits.md)
