
## Upload a file to a chat

`POST /v1/chats/:chatId/files`

Uploads a file to a chat. The content is passed in base64 and published in the chat as a message.

To download the file content in the v2 mode, use the authenticated [GET /v1/files/:fileId/download](/docs/entities/files/download). Requires `disk` **or** `crm`. The `im` scope alone is not enough. Uploading a file to a chat requires `im`.

## Parameters

| Parameter | Type | Required | Description |
|----------|-----|:-----:|---------|
| `chatId` (path) | number | yes | Chat ID. Get it from: [`POST /v1/chats`](/docs/chats/management/create) or the `chatId` field of a [`GET /v1/chats/recent`](/docs/chats/discovery/recent) row |
| `format` (query) | string | no | `v2` turns on the v2 mode. Any other value is ignored, and the upload runs the usual way. A repeated `format` is not rejected: with `format=v1&format=v2` the last value wins, so the v2 mode is on, while `format=v2&format=v1` gives the usual upload. The `format[]=v2` form turns on the v2 mode when all its values are `v2` |

## Request fields (body)

| Field | Type | Required | Description |
|------|-----|:-----:|---------|
| `filename` | string | yes | File name with extension |
| `content` | string | yes | File content in base64 encoding |
| `message` | string | no | Message text attached to the file in the same message |

**The v2 mode.** `format=v2` uploads the file and publishes the message in one call. The response is HTTP 200 with the v2 object: `file` — the chat file entity, `messageId`, `chatId`, `dialogId`; keys are in camelCase. The mode accepts only `filename`, `content` and `message` in the body and only `format` in the query string. Any other field or parameter is rejected with `400 INVALID_PARAMS` before any call to Bitrix24. The v2 response carries no links that embed Bitrix24 account credentials: the links in `file` open only in the user's own Bitrix24 session, and the file content comes from [`GET /v1/files/:fileId/download`](/docs/entities/files/download).

The `message` text is limited to 20000 Unicode code points. The complete outgoing string is counted, including markup, URLs and mentions, without truncation or trimming. For example, `😀` is one code point, while a compound emoji may consist of several code points. Requests over the limit are rejected before any call to Bitrix24.

Without `format=v2`, matching text aliases `message`, `MESSAGE`, `Message` and `fields.message` are accepted, including other case variants. Their values must agree. A conflict returns `400 INVALID_PARAMS`. An object is never converted to text. The v2 mode keeps its flat body whitelist: case variants and `fields` are not accepted.

## Examples

### curl — personal key

```bash
curl -X POST https://vibecode.bitrix24.com/v1/chats/1001/files \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "filename": "report.pdf",
    "content": "aGVsbG8=",
    "message": "Report for the period"
  }'
```

### curl — OAuth application

```bash
curl -X POST https://vibecode.bitrix24.com/v1/chats/1001/files \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "filename": "report.pdf",
    "content": "aGVsbG8=",
    "message": "Report for the period"
  }'
```

### JavaScript — personal key

```javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/chats/1001/files', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    filename: 'report.pdf',
    content: 'aGVsbG8=',
    message: 'Report for the period',
  }),
})

const { success, data } = await res.json()
console.log('File ID:', data.fileId)
```

### JavaScript — OAuth application

```javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/chats/1001/files', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    filename: 'report.pdf',
    content: 'aGVsbG8=',
    message: 'Report for the period',
  }),
})

const { success, data } = await res.json()
console.log('File ID:', data.fileId)
```

### curl — v2 mode

```bash
curl -X POST "https://vibecode.bitrix24.com/v1/chats/1001/files?format=v2" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "filename": "report.pdf",
    "content": "aGVsbG8=",
    "message": "Report for the period"
  }'
```

## Response fields

The usual mode — without `format=v2`:

| Field | Type | Description |
|------|-----|---------|
| `success` | boolean | Always `true` on success |
| `data.fileId` | number | ID of the uploaded file. Use it for `GET /v1/chats/files/:fileId` |
| `data.name` | string | File name |
| `data.downloadUrl` | string | Vibecode API download address in the usual mode, [`GET /v1/files/:id/download`](/docs/entities/files/download). Following it requires the `disk` or `crm` scope |
| `data.size` | string | File size in bytes |

The v2 mode — `format=v2`:

| Field | Type | Description |
|------|-----|---------|
| `success` | boolean | Always `true` on success |
| `data.file` | object\|null | The chat file entity. `null` if Bitrix24 returned no file after publishing |
| `data.file.id` | number | File ID. Needed to [save it to Drive](/docs/chats/files/save), to [transcribe it](/docs/chats/files/transcribe) and to download it via `GET /v1/files/:fileId/download` |
| `data.file.chatId` | number | Chat ID |
| `data.file.name` | string | File name |
| `data.file.extension` | string | Extension in lowercase |
| `data.file.size` | number | Size in bytes |
| `data.file.type` | string | File type: `file`, `image`, `audio`, `video` and others |
| `data.file.date` | string | Upload date (ISO 8601) |
| `data.file.authorId` | number | Author ID |
| `data.file.urlDownload` | string | Link to the file in Bitrix24. Opens only in the user's own Bitrix24 session |
| `data.file.urlShow` | string | Link to view the file in Bitrix24; opens in the same session |
| `data.file.urlPreview` | string | Link to the preview. An empty string when there is no preview |
| `data.file.isTranscribable` | boolean | Whether the file can be [transcribed](/docs/chats/files/transcribe) |
| `data.messageId` | number | ID of the published message |
| `data.chatId` | number | Chat ID |
| `data.dialogId` | string | Dialog ID — `chatN` |

Besides the fields above, the file entity carries Bitrix24 display fields — `image`, `status`, `progress`, `authorName`, `viewerAttrs`, `mediaUrl`, `isVideoNote`, `isVoiceNote`, `duration`.

## Response example

The usual mode, HTTP 201:

```json
{
  "success": true,
  "data": {
    "fileId": 9265,
    "name": "report.pdf",
    "size": "24576"
  }
}
```

### Response example — v2 mode

```json
{
  "success": true,
  "data": {
    "file": {
      "id": 7701,
      "chatId": 1001,
      "date": "2026-09-25T10:00:00+00:00",
      "type": "file",
      "name": "report.pdf",
      "extension": "pdf",
      "size": 24576,
      "image": false,
      "status": "done",
      "progress": 100,
      "authorId": 5,
      "authorName": "John Smith",
      "urlPreview": "",
      "urlShow": "https://YOUR_PORTAL.bitrix24.com/bitrix/services/main/ajax.php?action=disk.api.file.download&SITE_ID=s1&humanRE=1&fileId=7701&exact=N&fileName=report.pdf",
      "urlDownload": "https://YOUR_PORTAL.bitrix24.com/bitrix/services/main/ajax.php?action=disk.api.file.download&SITE_ID=s1&humanRE=1&fileId=7701&exact=N&fileName=report.pdf",
      "isTranscribable": false,
      "isVideoNote": false,
      "isVoiceNote": false,
      "duration": null
    },
    "messageId": 36890,
    "chatId": 1001,
    "dialogId": "chat1001"
  }
}
```

## Error response example

400 — required fields not provided:

```json
{
  "success": false,
  "error": {
    "code": "MISSING_PARAMS",
    "message": "Required: filename (string) and content (base64-encoded file content)"
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|---------|
| 400 | `MESSAGE_TOO_LONG` | More than 20000 Unicode code points in the outgoing text. `error.details` contains only `field: "message"`, `used`, `limit: 20000`, `unit: "unicode-code-points"`. No Bitrix24 call is made |
| 400 | `INVALID_CHAT_ID` | `chatId` is not a positive integer — checked before the Bitrix24 call |
| 400 | `MISSING_PARAMS` | The required `filename` or `content` fields were not provided |
| 400 | `INVALID_PARAMS` | Conflicting legacy text aliases or an object instead of text. In the v2 mode: a body field other than `filename`, `content`, `message`; a query parameter other than `format`; or `filename` or `content` is missing, empty or not a string; or `message` is not a string. Checked before any call to Bitrix24 |
| 404 | `FOLDER_NOT_FOUND` | The chat folder on Drive was not found |
| 500 | `UPLOAD_FAILED` | The file upload did not complete |
| 422 | `BITRIX_ERROR` | Bitrix24 error during upload. In the v2 mode the Bitrix24 code arrives in `error.b24Code` — for example, `FILE_INVALID_CONTENT` when `content` is not base64 |
| 413 | `PAYLOAD_TOO_LARGE` | The request body is larger than 40 MiB |
| 429 | `LARGE_BODY_BACKEND_BUSY` | Too many large request bodies are being received right now — retry after `error.retryAfter` seconds |
| 403 | `SCOPE_DENIED` | The key lacks the `im` scope |
| 403 | `WRITE_BLOCKED_READONLY_KEY` | The key is read-only — an upload changes the chat |
| 401 | `TOKEN_MISSING` | The key has no configured Bitrix24 tokens |

Full list of common API errors — [Errors](/docs/errors).

## Known specifics

**Three steps in one call.** In the usual mode the upload is performed in three steps: getting the chat folder, uploading the file to Drive, publishing in the chat. If any step fails, the corresponding error is returned. If Bitrix24 returns HTTP 503 or a rate-limit error for a step, the step is retried automatically. If the upload or publishing step fails or times out, the file may remain in the chat folder on Drive: the error response does not include its `fileId`, and calling again uploads another copy. HTTP 201 does not confirm that the message with the file appeared in the chat: the result of the publishing step is not checked. Before retrying, check the chat history and the [chat folder on Drive](/docs/chats/files/folder).

**The v2 mode — one call.** With `format=v2` the file is uploaded and published in one Bitrix24 call, with no separate steps and no intermediate errors. If the chat allows auto-join — for example, a comment chat or a collab — the call makes the current user a member.

**Base64 and body size.** Base64 encoding increases the request body size by about 33% compared to the original file. The body of this method is limited to 40 MiB, so the maximum original file size is just under 30 MiB.

## See also

- [Chat files](/docs/chats/files)
- [File metadata](/docs/chats/files/file-get)
- [Save files to Drive](/docs/chats/files/save)
- [Chat folder on Drive](/docs/chats/files/folder)
- [Send a message](/docs/chats/messages/send)
