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

# File uploads

> Upload a file from your page so the agent can read it in the conversation.

## Requirements

<Warning>
  Uploads need an embed key on the `<div>`. Without `data-chatbot-embed-key`, `uploadBlob()` rejects with
  "Upload is not configured for this embed (missing key)".
</Warning>

Create the key in TeamAI: open the agent, go to **Channels**, select **Website**, and use **Create key** under **Website embed keys**. Each agent can have two keys. The key is publishable: it is safe in frontend code and only lets this agent request temporary upload URLs. It cannot call other workspace APIs.

```html theme={null}
<div
  data-chatbot-id="YOUR_ASSISTANT_ID"
  data-chatbot-embed-key="YOUR_PUBLISHABLE_EMBED_KEY"></div>
<script src="https://app.teamai.com/chatbot-embed.js" defer></script>
```

Limits enforced by the SDK:

| Limit             | Value                                               |
| ----------------- | --------------------------------------------------- |
| Maximum file size | 10 MB. Larger blobs reject before any network call. |
| Upload timeout    | 60 seconds.                                         |
| Cache lifetime    | 30 days per file, in `localStorage`.                |

## Upload and ask

`uploadBlob()` uploads the file, attaches it to the chat, and resolves with the upload record. Send a message afterwards to have the agent use it.

```javascript theme={null}
const chatbot = tai.getInstance("YOUR_ASSISTANT_ID");

const csv = new File(
  ["region,revenue\nEast,120000\nWest,98000"],
  "revenue.csv",
  { type: "text/csv" }
);

try {
  const upload = await chatbot.uploadBlob(csv);
  console.log("Stored as", upload.objectName);
  const result = await chatbot.sendMessage("Which region had higher revenue in the attached CSV?");
  if (!result.success) console.warn(result.code, result.message);
} catch (error) {
  console.error("Upload failed:", error.message);
}
```

A file picked by the visitor works the same way:

```javascript theme={null}
document.getElementById("file").addEventListener("change", async (event) => {
  const file = event.target.files[0];
  if (file) await chatbot.uploadBlob(file);
});
```

Call `uploadBlob()` after the agent is ready. Before that there is no iframe to perform the upload and the Promise rejects with "Chatbot iframe is not initialized".

<ParamField path="blob" type="Blob | File" required>
  The file content. Set `type` to the MIME type; the stored file's extension is derived from it.
</ParamField>

## Supported file types

CSV is the format the data-analysis features are built around. The upload path also accepts images and PDFs. The stored object's extension comes from the MIME type:

| MIME type                    | Stored as |
| ---------------------------- | --------- |
| `text/csv` and anything else | `.csv`    |
| `image/png`                  | `.png`    |
| `image/jpeg`, `image/jpg`    | `.jpg`    |
| `image/webp`                 | `.webp`   |
| `application/pdf`            | `.pdf`    |

Any MIME type not in the table is stored with a `.csv` extension. Test non-CSV files against your agent before relying on them, since what the agent can do with an image or PDF depends on the agent's configuration.

## The upload record

```typescript theme={null}
interface UploadResponse {
  url: string;         // https://storage.googleapis.com/<bucket>/<objectName>
  objectName?: string; // generated ID plus extension, for example clx1...abc.csv
  fileId?: string;     // same value as objectName
  type?: string;       // "file"
  fileName?: string;
}
```

The original file name is not sent. If the agent needs it, include it in the message you send after the upload.

## Duplicate uploads are cached

The SDK hashes the blob's content (SHA-256), size, and MIME type. If the same file was uploaded from this browser within 30 days, it re-attaches the stored file instead of uploading again and resolves with the cached record. The cache lives in `localStorage` under `teamai-uploads-YOUR_ASSISTANT_ID`.

* `chatbot.clearUploadCache()` forgets every cached upload for the assistant.
* `chatbot.clearExpiredUploads()` drops entries older than 30 days. The SDK also does this on load.
* `tai.destroyInstance()` clears the cache as part of teardown.

## Errors

`uploadBlob()` rejects with an `Error` whose message is one of:

| Message                                                 | Cause                                                   |
| ------------------------------------------------------- | ------------------------------------------------------- |
| `File is too large. Maximum allowed size is 10 MB.`     | Blob over 10 MB.                                        |
| `Chatbot iframe is not initialized`                     | Called before the agent rendered.                       |
| `Upload is not configured for this embed (missing key)` | No `data-chatbot-embed-key` on the div.                 |
| `Upload timed out`                                      | No result within 60 seconds.                            |
| `Failed to get signed URL: ...`                         | TeamAI rejected the key or the request.                 |
| `File upload failed with status: ...`                   | The storage upload itself failed.                       |
| `Chatbot instance destroyed during file upload`         | `destroyInstance()` ran while the upload was in flight. |
