---
title: File Uploads
description: Send and receive files across chat platforms.
type: guide
prerequisites:
  - /docs/usage
---

## Send files

Attach files to messages using the `files` property:

```typescript title="lib/bot.ts" lineNumbers
const reportBuffer = Buffer.from("PDF content");

await thread.post({
  markdown: "Here's the report you requested:",
  files: [
    {
      data: reportBuffer,
      filename: "report.pdf",
      mimeType: "application/pdf",
    },
  ],
});
```

### Typed attachments

Use `attachments` when you already have normalized `Attachment` objects and the adapter supports typed outgoing media. Telegram supports one outgoing attachment per message and uses the native media method for the attachment type:

```typescript title="lib/bot.ts" lineNumbers
await thread.post({
  markdown: "Here's the image:",
  attachments: [
    {
      data: imageBuffer,
      name: "diagram.png",
      mimeType: "image/png",
      type: "image",
    },
  ],
});
```

Outgoing `attachments` are available on `{ raw }`, `{ markdown }`, and `{ ast }` messages. Card messages use `files` for uploads. Use `files` for generic uploads. On Telegram, `files` always upload as documents, while `attachments` preserve image, audio, video, or file media type. Use `data` or `fetchData` for private/authenticated files; URL-only attachments must be public URLs Telegram can fetch directly.

### Multiple files

```typescript title="lib/bot.ts" lineNumbers
await thread.post({
  markdown: "Attached are the images:",
  files: [
    { data: image1, filename: "screenshot1.png" },
    { data: image2, filename: "screenshot2.png" },
  ],
});
```

### Files without text

```typescript title="lib/bot.ts" lineNumbers
await thread.post({
  markdown: "",
  files: [{ data: buffer, filename: "document.xlsx" }],
});
```

## Receive files

Access attachments from incoming messages:

```typescript title="lib/bot.ts" lineNumbers
bot.onSubscribedMessage(async (thread, message) => {
  for (const attachment of message.attachments ?? []) {
    console.log(`File: ${attachment.name}, Type: ${attachment.mimeType}`);

    if (attachment.fetchData) {
      const data = await attachment.fetchData();
      console.log(`Downloaded ${data.length} bytes`);
    }
  }
});
```

### Attachment properties

| Property | Type | Description |
|----------|------|-------------|
| `type` | `string` | Attachment type (e.g., "image", "file") |
| `url` | `string` (optional) | Public URL |
| `name` | `string` (optional) | Filename |
| `mimeType` | `string` (optional) | MIME type |
| `size` | `number` (optional) | File size in bytes |
| `width` | `number` (optional) | Image width |
| `height` | `number` (optional) | Image height |
| `fetchData` | `() => Promise<Buffer>` (optional) | Download the file data |
| `fetchMetadata` | `Record<string, string>` (optional) | Platform-specific IDs for reconstructing `fetchData` after serialization |
