## Download a timeline comment attachment

`GET /v1/timelines/:commentId/files/:fileRef/download`

Downloads a file attached to a timeline comment. The response is a binary stream with a `Content-Disposition` header.

The endpoint accepts **two different identifiers** in `fileRef`, because a client may hold either one:

- **Attachment ID.** The account interface shows it, and file user-fields return it. This path computes access through the carrier entity — the comment itself — rather than through personal Disk permissions, so it reaches attachments that [Disk file download](/docs/entities/files/download) refuses to serve;
- **Disk object ID.** This is the object key in the `files` field of the [`GET /v1/timelines/:id`](/docs/entities/timelines/get) response. The path goes through personal Disk permissions, so it can be refused where the attachment-ID path would have succeeded.

The comment itself is read first, then the Disk object ID is tried, and the attachment ID only when the comment does not list that reference among its files. You do not have to state which identifier you are passing. This order is cheaper: the documented case costs two Bitrix24 calls instead of three.

## Parameters

| Parameter | In | Type | Req. | Description |
|----------|---|-----|:-----:|----------|
| `commentId` | path | number | yes | Timeline comment ID. Get it from [`GET /v1/timelines`](/docs/entities/timelines/list) |
| `fileRef` | path | number | yes | Attachment ID or Disk object ID. The latter is the object key in the `files` field of the [`GET /v1/timelines/:id`](/docs/entities/timelines/get) response |

The request body is empty.

## Examples

### curl — personal key

```bash
# By Disk object ID — the key from the files field of the comment response
curl -OJ -H "X-Api-Key: YOUR_API_KEY" \
  https://vibecode.bitrix24.com/v1/timelines/67689/files/9747/download

# By attachment ID — when you have one
curl -OJ -H "X-Api-Key: YOUR_API_KEY" \
  https://vibecode.bitrix24.com/v1/timelines/67689/files/551/download
```

### curl — OAuth application

```bash
curl -OJ \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  https://vibecode.bitrix24.com/v1/timelines/67689/files/9747/download
```

### JavaScript — personal key

```javascript
// Take the attachment identifiers from the comment response
const comment = await fetch(
  'https://vibecode.bitrix24.com/v1/timelines/67689',
  { headers: { 'X-Api-Key': 'YOUR_API_KEY' } },
).then(r => r.json())

for (const objectId of Object.keys(comment.data.files ?? {})) {
  const res = await fetch(
    `https://vibecode.bitrix24.com/v1/timelines/67689/files/${objectId}/download`,
    { headers: { 'X-Api-Key': 'YOUR_API_KEY' } },
  )
  if (!res.ok) {
    const { error } = await res.json()
    console.warn(`${objectId}: ${error.code}`)
    continue
  }
  console.log(objectId, 'bytes:', (await res.arrayBuffer()).byteLength)
}
```

### JavaScript — OAuth application

```javascript
const res = await fetch(
  'https://vibecode.bitrix24.com/v1/timelines/67689/files/9747/download',
  {
    headers: {
      'X-Api-Key': 'YOUR_APP_KEY',
      'Authorization': 'Bearer USER_SESSION_TOKEN',
    },
  },
)

const bytes = await res.arrayBuffer()
```

## Response headers

On success the binary file content is returned (HTTP 200). There is no `## Response fields` section — the response body is not JSON.

| Header | Example value | Description |
|-----------|-----------------|----------|
| `Content-Type` | `image/gif` | Content type exactly as Bitrix24 returned it |
| `Content-Disposition` | `attachment; filename="scan.pdf"` | File name to save under |
| `Content-Length` | `43` | Size in bytes when Bitrix24 reported it |

## Error response example

404 — the attachment belongs to a different comment:

```json
{
  "success": false,
  "error": {
    "code": "NOT_FOUND",
    "message": "Attachment 551 does not belong to comment 11111"
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|----------|
| 400 | `INVALID_PARAMS` | `commentId` or `fileRef` is not a positive integer |
| 401 | `TOKEN_MISSING` | The key has no configured Bitrix24 tokens |
| 403 | `SCOPE_DENIED` | The key lacks the `crm` scope |
| 404 | `NOT_FOUND` | The comment does not exist, the attachment belongs to another comment or to a different kind of record (a task, a quote), or its membership in the comment could not be confirmed, the file is not among its files, or the file has no download address |
| 502 | `DOWNLOAD_FAILED` | Bitrix24 did not return the file, or named an address outside the account domain — no request is sent to such an address |

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

## Known specifics

- **The attachment must belong to the named comment.** Otherwise `404`. Without that check the endpoint would allow enumerating the account's attachments.
- **The address is never returned.** The download address contains an authorization code, so the endpoint returns bytes only.
- **The `urlDownload` field from the comment response does not work directly.** That address points at a Disk page rather than at REST and cannot be authorized with a key: requesting it redirects to the sign-in page. Download through this endpoint instead.
- **The two paths grant different access.** If the Disk object ID is refused on permissions and you have the attachment ID, try it: it computes access through the comment rather than through personal storage.
- **The download address is verified, redirects included.** It arrives in the Bitrix24 response, so the endpoint checks it against the account domain and sends no request to a foreign address — an authorization code would otherwise go there. The endpoint walks redirects itself, checking every hop: the chain is bounded in length and in time, and a redirect to an internal address is not followed and answers `502`.
- **A temporary failure is not disguised as `404`.** When Bitrix24 answers with a request limit, is unavailable, or the repeated-error guard trips, that is what comes back — `429` or `502`/`503` with a `Retry-After` header, not «the file does not belong to the comment». So a `404` here always means the reference itself is wrong: retrying it is pointless, whereas a `429`/`5xx` should be retried with a delay.

## See also

- [Get a timeline comment](/docs/entities/timelines/get)
- [Download an activity file](/docs/entities/activities/file-download)
- [Download a Disk file](/docs/entities/files/download)
- [Timelines](/docs/entities/timelines)
