[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"docs-entities\u002Ftask-comments\u002Ffile-download":3,"docs-tabs-entities\u002Ftask-comments\u002Ffile-download":6},{"content":4,"lastmod":5},"## Download a task comment attachment\n\n`GET \u002Fv1\u002Ftasks\u002F:taskId\u002Fcomments\u002F:id\u002Ffiles\u002F:fileId\u002Fdownload`\n\nDownloads 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.\n\n`HEAD` is not supported at this address and returns `404`. Use `GET` to download the file.\n\nThis 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.\n\n## Parameters\n\n| Parameter | Type | Required | Description |\n|----------|-----|:-----:|----------|\n| `taskId` (path) | number | yes | Task ID. Get it from [`GET \u002Fv1\u002Ftasks`](\u002Fdocs\u002Fentities\u002Ftasks) |\n| `id` (path) | number | yes | Comment ID. Get it from [`GET \u002Fv1\u002Ftasks\u002F:taskId\u002Fcomments`](.\u002Flist.md) or from the [`POST \u002Fv1\u002Ftasks\u002F:taskId\u002Fcomments`](.\u002Fcreate.md) response |\n| `fileId` (path) | number | yes | The `attachments[].fileId` value from the comment response — [`GET \u002Fv1\u002Ftasks\u002F:taskId\u002Fcomments\u002F:id`](.\u002Fget.md). The same full path is already in `attachments[].downloadUrl` — use it instead of assembling the address piece by piece |\n\n\n## Examples\n\n### curl — personal key\n\n```bash\n# Save under the original name from the response header\ncurl -OJ -H \"X-Api-Key: YOUR_API_KEY\" \\\n  https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Ftasks\u002F3711\u002Fcomments\u002F9393\u002Ffiles\u002F6687\u002Fdownload\n\n# Set the file name explicitly\ncurl -H \"X-Api-Key: YOUR_API_KEY\" \\\n  https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Ftasks\u002F3711\u002Fcomments\u002F9393\u002Ffiles\u002F6687\u002Fdownload \\\n  -o attachment.jpg\n```\n\n### curl — OAuth application\n\n```bash\ncurl -OJ \\\n  -H \"X-Api-Key: YOUR_APP_KEY\" \\\n  -H \"Authorization: Bearer USER_SESSION_TOKEN\" \\\n  https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Ftasks\u002F3711\u002Fcomments\u002F9393\u002Ffiles\u002F6687\u002Fdownload\n```\n\n### JavaScript — personal key\n\n```javascript\nconst res = await fetch(\n  'https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Ftasks\u002F3711\u002Fcomments\u002F9393\u002Ffiles\u002F6687\u002Fdownload',\n  { headers: { 'X-Api-Key': 'YOUR_API_KEY' } },\n)\n\nif (!res.ok) {\n  const { error } = await res.json()\n  throw new Error(`${error.code}: ${error.message}`)\n}\n\nconst bytes = await res.arrayBuffer()\nconsole.log('bytes received:', bytes.byteLength)\n```\n\n### JavaScript — OAuth application\n\n```javascript\nconst res = await fetch(\n  'https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Ftasks\u002F3711\u002Fcomments\u002F9393\u002Ffiles\u002F6687\u002Fdownload',\n  {\n    headers: {\n      'X-Api-Key': 'YOUR_APP_KEY',\n      'Authorization': 'Bearer USER_SESSION_TOKEN',\n    },\n  },\n)\n\nconst bytes = await res.arrayBuffer()\n```\n\n## Response headers\n\nOn 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.\n\n| Header | Example value | Description |\n|-----------|-----------------|----------|\n| `Content-Type` | `image\u002Fjpeg` | Content type as Bitrix24 returned it |\n| `Content-Disposition` | `attachment; filename=\"ava555.jpg\"` | File name to save under |\n| `Content-Length` | `405559` | Size in bytes when Bitrix24 reported it |\n\n## Error response example\n\n404 — the file does not belong to this comment:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"NOT_FOUND\",\n    \"message\": \"File 6687 does not belong to comment 9395\"\n  }\n}\n```\n\n## Errors\n\n| HTTP | Code | Description |\n|------|-----|----------|\n| 400 | `INVALID_PARAMS` | `taskId`, `id` or `fileId` is not a positive integer. No request reaches Bitrix24 in this case |\n| 401 | `TOKEN_MISSING` | The key has no configured Bitrix24 tokens |\n| 403 | `SCOPE_DENIED` | The key lacks the `task` scope |\n| 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 |\n| 404 | `NOT_FOUND` | The file exists but belongs to another comment, or it has no download address |\n| 422 | `BITRIX_ERROR` | Bitrix24 refused one of the calls behind this operation and named no more specific condition |\n| 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 |\n| 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` |\n| 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 |\n| 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 |\n\nThe full list of common API errors — [Errors](\u002Fdocs\u002Ferrors).\n\n## Known specifics\n\n- **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.\n- **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 `fileId` is the response for that same comment, not a number taken from somewhere else.\n- **The path in `downloadUrl` arrives 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.\n\n## See also\n\n- [Get comment](.\u002Fget.md)\n- [List comments](.\u002Flist.md)\n- [Comment fields](.\u002Ffields.md)\n- [Task comments](\u002Fdocs\u002Fentities\u002Ftask-comments)\n- [Tasks](\u002Fdocs\u002Fentities\u002Ftasks)\n","2026-10-27",{},1794739427694]