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

# Upload Files

> Upload files to a chat for document analysis and image understanding

Upload one or more files to an existing chat session. Supported file types include documents (PDF, CSV, XLSX) and images (PNG, JPG).

<Warning>
  `chat_id` is now required. Call [Create Chat](/api-reference/chats/create-chat) first to obtain one — uploads without `chat_id` return `400 MISSING_CHAT_ID`, and an unknown ID returns `404 Chat not found`.
</Warning>

<Note>
  Uploaded files are processed server-side: documents are parsed and chunked for retrieval, images are captioned and embedded. After uploading, reference the returned upload IDs in a [Send Message](/api-reference/chats/send-message) request to attach them to a conversation turn.
</Note>

This endpoint is rate-limited to **30 requests per minute per JWT**. Excess requests return `429` with a `Retry-After` header.

<ParamField header="Authorization" type="string" required>
  Bearer token obtained from the authenticate endpoint.
</ParamField>

<ParamField body="files" type="file[]" required>
  One or more files to upload. Sent as `multipart/form-data`. Maximum 5 files per request, each up to 30 MB.

  Supported MIME types:

  * `application/pdf`
  * `text/csv`
  * `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` (XLSX)
  * `image/png`
  * `image/jpeg`
</ParamField>

<ParamField body="chat_id" type="string" required>
  ID of an existing chat to attach the files to. Obtain one from [Create Chat](/api-reference/chats/create-chat).
</ParamField>

## Response

<ResponseField name="chat_id" type="string" required>
  The chat the files were uploaded to. If no `chat_id` was provided in the request, this is the newly created chat's ID.
</ResponseField>

<ResponseField name="uploads" type="array" required>
  List of successfully processed uploads.

  <Expandable title="Upload object">
    <ResponseField name="id" type="string" required>
      Unique identifier for the upload. Use this in the `upload_ids` field when sending a message.
    </ResponseField>

    <ResponseField name="filename" type="string" required>
      Original filename.
    </ResponseField>

    <ResponseField name="mime_type" type="string" required>
      MIME type of the uploaded file.
    </ResponseField>

    <ResponseField name="file_size" type="integer" required>
      File size in bytes.
    </ResponseField>

    <ResponseField name="upload_type" type="string" required>
      Either `"document"` or `"image"`.
    </ResponseField>

    <ResponseField name="presigned_url" type="string" required>
      Temporary download URL for the file. Expires after 1 hour.
    </ResponseField>

    <ResponseField name="created_at" type="string" required>
      ISO 8601 timestamp when the upload was created.
    </ResponseField>

    <ResponseField name="message_id" type="string | null">
      ID of the chat message this upload is attached to. `null` until the upload is referenced via `upload_ids` in a [Send Message](/api-reference/chats/send-message) or [WebSocket](/api-reference/chats/websocket) request.
    </ResponseField>
  </Expandable>
</ResponseField>

<RequestExample>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl -X POST https://api.trellis.sh/v1/chats/uploads \
    -H "Authorization: Bearer YOUR_TOKEN" \
    -F "files=@report.pdf" \
    -F "files=@data.csv" \
    -F "chat_id=chat_abc123"
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import requests

  with open("report.pdf", "rb") as pdf, open("data.csv", "rb") as csv:
      response = requests.post(
          "https://api.trellis.sh/v1/chats/uploads",
          headers={"Authorization": "Bearer YOUR_TOKEN"},
          files=[
              ("files", ("report.pdf", pdf, "application/pdf")),
              ("files", ("data.csv", csv, "text/csv")),
          ],
          data={"chat_id": "chat_abc123"},
      )
  result = response.json()
  upload_ids = [u["id"] for u in result["uploads"]]
  ```

  ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  const form = new FormData();
  form.append("files", pdfBlob, "report.pdf");
  form.append("files", csvBlob, "data.csv");
  form.append("chat_id", "chat_abc123");

  const response = await fetch("https://api.trellis.sh/v1/chats/uploads", {
    method: "POST",
    headers: { Authorization: "Bearer YOUR_TOKEN" },
    body: form,
  });
  const { chat_id, uploads } = await response.json();
  const uploadIds = uploads.map((u) => u.id);
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={"theme":{"light":"github-light","dark":"github-dark"}}
  {
    "chat_id": "chat_abc123",
    "uploads": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "filename": "report.pdf",
        "mime_type": "application/pdf",
        "file_size": 245760,
        "upload_type": "document",
        "presigned_url": "https://s3.amazonaws.com/...",
        "created_at": "2025-06-15T09:30:00Z",
        "message_id": null
      },
      {
        "id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
        "filename": "data.csv",
        "mime_type": "text/csv",
        "file_size": 1024,
        "upload_type": "document",
        "presigned_url": "https://s3.amazonaws.com/...",
        "created_at": "2025-06-15T09:30:01Z",
        "message_id": null
      }
    ]
  }
  ```

  ```json 400 Unsupported file type theme={"theme":{"light":"github-light","dark":"github-dark"}}
  {
    "detail": "Unsupported file type 'application/octet-stream' for 'archive.bin'. Allowed: PNG, JPG, PDF, CSV, XLSX"
  }
  ```

  ```json 400 Too many files theme={"theme":{"light":"github-light","dark":"github-dark"}}
  {
    "detail": "Maximum 5 files per request"
  }
  ```

  ```json 400 File too large theme={"theme":{"light":"github-light","dark":"github-dark"}}
  {
    "detail": "File 'large.pdf' exceeds 30MB limit (45.2MB)"
  }
  ```

  ```json 400 Missing chat_id theme={"theme":{"light":"github-light","dark":"github-dark"}}
  {
    "detail": {
      "code": "MISSING_CHAT_ID",
      "message": "Provide chat_id; create one via POST /v1/chats/create"
    }
  }
  ```

  ```json 404 Chat not found theme={"theme":{"light":"github-light","dark":"github-dark"}}
  {
    "detail": "Chat not found"
  }
  ```

  ```json 429 Rate limited theme={"theme":{"light":"github-light","dark":"github-dark"}}
  {
    "detail": "Rate limit exceeded",
    "retry_after_seconds": 8
  }
  ```
</ResponseExample>
