For AI agents: markdown of this page — /docs-content-en/entities/task-comments/file-download.md documentation index — /llms.txt
Download a task comment attachment
GET /v1/tasks/:taskId/comments/:id/files/:fileId/download
Downloads a file attached to a task comment. The response is the file content as bytes rather than JSON, with the file name in a response header.
This endpoint exists because the file address is never exposed: it carries an authorization code, so requesting such an address from a third-party application returns the sign-in page with code 200 instead of the file. The endpoint adds the authorization itself. The task scope is enough — there is no need to extend the key's permissions to all of Drive, and the endpoint returns only a file from a comment the key can already read.
Parameters
| Parameter | Type | Req. | Description |
|---|---|---|---|
taskId (path) |
number | yes | Task ID. Get it from GET /v1/tasks |
id (path) |
number | yes | Comment ID. Get it from GET /v1/tasks/:taskId/comments or from the POST /v1/tasks/:taskId/comments response |
fileId (path) |
number | yes | The attachments[].fileId value from the comment response — GET /v1/tasks/:taskId/comments/:id. The same full path is already in attachments[].downloadUrl — use it instead of assembling the address piece by piece |
Examples
curl — personal key
# Save under the original name from the response header
curl -OJ -H "X-Api-Key: YOUR_API_KEY" \
https://vibecode.bitrix24.com/v1/tasks/3711/comments/9393/files/6687/download
# Set the file name explicitly
curl -H "X-Api-Key: YOUR_API_KEY" \
https://vibecode.bitrix24.com/v1/tasks/3711/comments/9393/files/6687/download \
-o attachment.jpg
curl — OAuth app
curl -OJ \
-H "X-Api-Key: YOUR_APP_KEY" \
-H "Authorization: Bearer USER_SESSION_TOKEN" \
https://vibecode.bitrix24.com/v1/tasks/3711/comments/9393/files/6687/download
JavaScript — personal key
const res = await fetch(
'https://vibecode.bitrix24.com/v1/tasks/3711/comments/9393/files/6687/download',
{ headers: { 'X-Api-Key': 'YOUR_API_KEY' } },
)
if (!res.ok) {
const { error } = await res.json()
throw new Error(`${error.code}: ${error.message}`)
}
const bytes = await res.arrayBuffer()
console.log('bytes received:', bytes.byteLength)
JavaScript — OAuth app
const res = await fetch(
'https://vibecode.bitrix24.com/v1/tasks/3711/comments/9393/files/6687/download',
{
headers: {
'X-Api-Key': 'YOUR_APP_KEY',
'Authorization': 'Bearer USER_SESSION_TOKEN',
},
},
)
const bytes = await res.arrayBuffer()
Response headers
On success the file content is returned with code 200. The response body is the file bytes rather than JSON, so the headers are described instead of response fields.
| Header | Example value | Description |
|---|---|---|
Content-Type |
image/jpeg |
Content type as Bitrix24 returned it |
Content-Disposition |
attachment; filename="ava555.jpg" |
File name to save under |
Content-Length |
405559 |
Size in bytes when Bitrix24 reported it |
Error response example
404 — the file does not belong to this comment:
{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "File 6687 does not belong to comment 9395"
}
}
Errors
| HTTP | Code | Description |
|---|---|---|
| 400 | INVALID_PARAMS |
taskId, id or fileId is not a positive integer. No request reaches Bitrix24 in this case |
| 401 | TOKEN_MISSING |
The key has no configured Bitrix24 tokens |
| 403 | SCOPE_DENIED |
The key lacks the task scope |
| 404 | NOT_FOUND |
No comment with this id exists in the named task. The same response is returned when the task itself does not exist |
| 404 | NOT_FOUND |
The file exists but belongs to another comment, or it has no download address |
| 422 | BITRIX_ERROR |
Bitrix24 refused one of the calls behind this operation and named no more specific condition |
| 429 | RATE_LIMITED |
This code comes from two sources. The first is the operation's own limit, 30 requests per minute per key: take the effective value from the X-RateLimit-Limit header, and the remaining count and the reset time from X-RateLimit-Remaining and X-RateLimit-Reset — they arrive with every response of this operation, refusals included, and the refusal itself also carries Retry-After with the seconds until the window resets. The second is Bitrix24 rejecting a call under its own rate limit: that response carries the Retry-After: 2 header |
| 503 | BITRIX_TIMEOUT |
Bitrix24 accepted a call behind this operation but did not answer within the time limit. This operation is a read, so retrying is safe. Header Retry-After: 10 |
| 502 | DOWNLOAD_FAILED |
The request for the file bytes returned a non-2xx status (including when Bitrix24 is temporarily unavailable for the file delivery itself), or Bitrix24 named an address outside the account domain — no request is sent to such an address |
| 502 | BITRIX_UNAVAILABLE |
Bitrix24 answered a REST call behind this operation — the comment lookup or the file details request — with a 5xx. The status is the same as for DOWNLOAD_FAILED; tell them apart by error.code. Both outcomes are temporary and safe to retry |
The full list of common API errors — Errors.
Known specifics
- The file must belong to the named comment. Otherwise you get a
404, even when the file exists and is reachable in another comment. The check is not pedantry: without it a numeric match on the identifier would hand over someone else's file. - A matching number means nothing. Chat comments and records in the comments block of the old card keep their identifiers in separate spaces, and those spaces overlap numerically. So the reliable source of
fileIdis the response for that same comment, not a number taken from somewhere else. - The path in
downloadUrlarrives without a domain. The field points at this operation rather than at the file in Bitrix24. Join the path with the same API base address you called — the response does not repeat the domain.