How to Transfer a File from a CRM Field to a Task
Who can execute the methods: to complete the entire scenario, you need permission to read CRM items, add files to a Drive folder, and edit the task
- crm.item.get — a user with permission to read items of a CRM object
- disk.folder.uploadFile — a user with the Add permission for the Drive folder
- tasks.task.files.attach — the task creator or a user with permission to edit the task and read the file
If you are developing integrations for Bitrix24 using AI tools (Codex, Claude Code, Cursor), connect to the MCP server so that the assistant can utilize the official REST documentation.
A manager attached a contract to a deal, and the assignee will work with that file in a task. A single call cannot copy the file between the two objects: deals and tasks store files differently.
A deal file field is a field of the File type: it holds a CRM file ID. Tasks work differently. A task stores its attachments in the UF_TASK_WEBDAV_FILES field of the File (Drive) type, and that field accepts only a Drive object ID.
These are two separate sets of identifiers. A CRM file ID means nothing to Drive: Drive methods will return an error for it, or a different file that happens to share the same number.
CRM has no File (Drive) fields, so there is no ready-made Drive object ID to take from a deal. The file has to be transferred manually: download it from the deal, upload it to Drive, and attach it to the task from there.
The scenario consists of four steps.
- Get the file link using the crm.item.get method
- Download the file from that link with a regular HTTP request
- Upload the file to Drive using the disk.folder.uploadFile method
- Attach the file to the task using the tasks.task.files.attach method
The scenario is shown for a single file.
Before You Start
Prepare the scenario data:
- A CRM item with a filled file field. You will need the
entityTypeIdof the object, theidof the item, and the field name, such asufCrm_1688736288. The examples use a deal, whoseentityTypeIdis2. The crm.item.fields method returns the list of fields, and file fields have thefiletype - A task to attach the file to. You will need the task
ID, which the tasks.task.list method returns - A Drive folder for the upload. You will need the folder
ID. The disk.storage.getlist and disk.storage.getchildren methods return storages and their contents - REST access. A webhook or an application with the
crm,disk, andtaskpermissions
How to Use Examples in Documentation
1. Get the File Link from the CRM Item
Read the deal with the crm.item.get method and the following parameters:
entityTypeId— specify2, the identifier of the deal typeid— specify6533, the identifier of the deal that holds the file
import { B24Hook } from '@bitrix24/b24jssdk'
const $b24 = B24Hook.fromWebhookUrl(process.env.B24_HOOK)
// B24_HOOK = 'https://your-domain.bitrix24.com/rest/USER_ID/TOKEN/'
const response = await $b24.actions.v2.call.make({
method: 'crm.item.get',
params: {
entityTypeId: 2,
id: 6533
},
requestId: 'crm-item-get'
})
if (!response.isSuccess) {
throw new Error(response.getErrorMessages().join('; '))
}
const item = response.getData().result.item
const file = item.ufCrm_1688736288
from b24pysdk import BitrixWebhook, Client
token = BitrixWebhook(
domain="your-domain.bitrix24.com",
webhook_token="user_id/webhook_key",
)
client = Client(token)
# Called through the SDK core: the typed wrapper does not return custom fields
item = token.call_method("crm.item.get", {
"entityTypeId": 2,
"id": 6533,
})["result"]["item"]
file = item["ufCrm_1688736288"]
require_once 'vendor/autoload.php';
use Bitrix24\SDK\Services\ServiceBuilderFactory;
use Symfony\Component\EventDispatcher\EventDispatcher;
$serviceBuilder = (new ServiceBuilderFactory(new EventDispatcher(), $log))
->initFromWebhook('https://your-domain.bitrix24.com/rest/USER_ID/TOKEN/');
// Called through the SDK core: the typed wrapper does not return custom fields
$item = $serviceBuilder->core->call(
'crm.item.get',
['entityTypeId' => 2, 'id' => 6533]
)->getResponseData()->getResult()['item'];
$file = $item['ufCrm_1688736288'];
The file field returns three values:
id— the CRM file identifier. Drive has no object with this number, so it must not be passed to Drive methodsurl— a link for the Bitrix24 interface. It opens in the browser of a signed-in user and is not suitable for an integrationurlMachine— a link for integrations. It carries an access token and a file signature, so the file can be downloaded with a regular HTTP request. This is the link used in the next step
{
"result": {
"item": {
"id": 6533,
"title": "Equipment supply",
"ufCrm_1688736288": {
"id": 27807,
"url": "https://your-domain.bitrix24.com/bitrix/services/main/ajax.php?action=crm.controller.item.getFile&SITE_ID=s1&entityTypeId=2&id=6533&fieldName=UF_CRM_1688736288&fileId=27807",
"urlMachine": "https://your-domain.bitrix24.com/rest/1/xxxxxxxxxxxxxxxx/crm.controller.item.getFile/?token=xxxxxxxx"
}
}
}
}
If the field is multiple, an array of such objects arrives instead of a single object, and steps 2, 3, and 4 are repeated for each file.
2. Download the File
This step calls no method: the file is retrieved with a regular HTTP request to the urlMachine link.
The request needs no separate authorization, because the token is already inside the link. Pass the link as a whole and do not split it into parts, since the set of parameters may change. The link is usually absolute, but if it starts with / in the response, prepend your Bitrix24 address to it.
Next, the file has to be encoded in Base64, because Drive accepts it in that form. Encoding is covered in the How to Upload Files article.
const fileResponse = await fetch(file.urlMachine)
if (!fileResponse.ok) {
throw new Error('Failed to download the file: ' + fileResponse.status)
}
const buffer = Buffer.from(await fileResponse.arrayBuffer())
const fileContent = buffer.toString('base64')
import base64
import requests
file_response = requests.get(file["urlMachine"], timeout=30)
file_response.raise_for_status()
file_content = base64.b64encode(file_response.content).decode()
$binary = file_get_contents($file['urlMachine']);
if ($binary === false) {
throw new RuntimeException('Failed to download the file');
}
$fileContent = base64_encode($binary);
The original file name is returned in the Content-Disposition header. In the examples below the name is set manually, so the file will be named as specified in data.NAME on Drive and in the task, not as it was named in the deal. To keep the original name, take it from the header and pass it in the next step.
3. Upload the File to Drive
Upload the file with the disk.folder.uploadFile method and the following parameters:
id— specify1739, the identifier of the Drive folderdata— pass the name inNAME. The file is saved on Drive with this namefileContent— pass an array of the file name and the Base64 string from the previous step
const uploadResponse = await $b24.actions.v2.call.make({
method: 'disk.folder.uploadFile',
params: {
id: 1739,
data: {
NAME: 'contract.pdf'
},
fileContent: [
'contract.pdf',
fileContent
]
},
requestId: 'disk-uploadfile'
})
if (!uploadResponse.isSuccess) {
throw new Error(uploadResponse.getErrorMessages().join('; '))
}
const diskFile = uploadResponse.getData().result
disk_file = client.disk.folder.uploadfile(
bitrix_id=1739,
data={
"NAME": "contract.pdf",
},
file_content=[
"contract.pdf",
file_content,
],
).response.result
$diskFile = $serviceBuilder->getDiskScope()->folder()->uploadFile(
1739,
['NAME' => 'contract.pdf'],
['contract.pdf', $fileContent]
)->getFile();
The response returns two different identifiers, and it is important not to mix them up:
ID— the Drive object identifier. This is the value passed to the taskFILE_ID— the internal file identifier. It must not be passed to fields of the File (Drive) type
{
"result": {
"ID": 6687,
"NAME": "contract.pdf",
"TYPE": "file",
"PARENT_ID": "1739",
"FILE_ID": 28073,
"SIZE": "405559"
}
}
4. Attach the File to the Task
Attach the file with the tasks.task.files.attach method and the following parameters:
taskId— specify3709, the identifier of the taskfileId— specify6687, theIDvalue from the previous response
const attachResponse = await $b24.actions.v2.call.make({
method: 'tasks.task.files.attach',
params: {
taskId: 3709,
fileId: diskFile.ID
},
requestId: 'task-files-attach'
})
if (!attachResponse.isSuccess) {
throw new Error(attachResponse.getErrorMessages().join('; '))
}
const attachment = attachResponse.getData().result
attachment = client.tasks.task.files.attach(
task_id=3709,
file_id=disk_file["ID"],
).response.result
$attachment = $serviceBuilder->core->call(
'tasks.task.files.attach',
[
'taskId' => 3709,
'fileId' => $diskFile['ID']
]
)->getResponseData()->getResult();
The method returns attachmentId, the identifier of the link between the Drive file and the task.
{
"result": {
"attachmentId": 423
}
}
Check the Result
The file should appear in the task. There are two ways to verify this.
In the Bitrix24 interface, open the task — the file will be in the attachments block.
Through REST, pass attachmentId to the id parameter of the disk.attachedObject.get method.
const checkResponse = await $b24.actions.v2.call.make({
method: 'disk.attachedObject.get',
params: {
id: attachment.attachmentId
},
requestId: 'disk-attached-object-get'
})
console.log(checkResponse.getData().result)
check = token.call_method(
"disk.attachedObject.get",
{
"id": attachment["attachmentId"],
},
)["result"]
print(check)
$check = $serviceBuilder->core->call(
'disk.attachedObject.get',
['id' => $attachment['attachmentId']]
)->getResponseData()->getResult();
print_r($check);
The scenario is successful if:
ENTITY_TYPEequalstasks_taskENTITY_IDequals the task identifierOBJECT_IDequals theIDof the file on Drive from the third stepNAMEcontains the name under which the file was saved on Drive
{
"result": {
"ID": "423",
"OBJECT_ID": "6687",
"MODULE_ID": "tasks",
"ENTITY_TYPE": "tasks_task",
"ENTITY_ID": "3709",
"NAME": "contract.pdf",
"SIZE": "405559"
}
}
Errors and Diagnostics
If the method returns an error, check the request data.
|
Code |
Cause and action |
|
|
No item with this |
|
|
The |
|
The file field returns |
The field is not filled for this item. Take an item that has a file |
|
The file field is missing from the response |
The field name is incorrect. Check the name with the crm.item.fields method |
|
An HTML sign-in page is downloaded instead of the file |
The |
|
|
The folder with the specified |
|
|
The file name was not passed in |
|
|
The folder already contains a file with this name. Use a different name or pass |
|
|
The file could not be saved. Check free space in Drive and the Base64 data |
|
|
|
|
|
The value passed is not the |
If there was no error but the file is not in the task, check the following:
taskIdpoints to an existing task. With an invalid identifier, the tasks.task.files.attach method still returnsattachmentId, but the file does not appear in the taskfileIdholds the Drive objectIDfrom the disk.folder.uploadFile response, notFILE_ID- the file was uploaded to a folder the user has access to
Repeat the scenario from the step that returned the error. If the file is already uploaded to Drive, do not upload it again: correct taskId or fileId and repeat only the tasks.task.files.attach call.
Key Considerations
- A file in a CRM field is not a Drive object. It cannot be passed to a task directly or as a Base64 string: the
UF_TASK_WEBDAV_FILESfield accepts only a Drive object identifier - If you attach the file by writing to the
UF_TASK_WEBDAV_FILESfield instead of calling tasks.task.files.attach — for example when creating a task with the tasks.task.add method — pass the value in then<identifier>format, such as["n6687"]. The field does not accept a number without the prefix - The
urlMachinelink carries an access token and a file signature. Do not write it to logs, do not store it in a database, and do not pass it to third parties - The scenario works with objects other than deals. For another CRM object, change
entityTypeIdand take the file field name from crm.item.fields. For smart processes, the field name includes the process number, such asufCrm_7_1739432938 - The file is copied, not moved. It remains in the deal, and a separate object appears on Drive. If the original file in the deal is later replaced, the copy in the task is not updated
Code Example
The code goes through all four steps: it reads the CRM item, downloads the file, uploads it to Drive, and attaches it to the task.
Replace the webhook, the identifiers of the item, folder, and task, and the file field name.
import { B24Hook } from '@bitrix24/b24jssdk'
const $b24 = B24Hook.fromWebhookUrl(process.env.B24_HOOK)
const entityTypeId = 2
const itemId = 6533
const fieldName = 'ufCrm_1688736288'
const folderId = 1739
const taskId = 3709
const fileName = 'contract.pdf'
async function call(method, params, requestId) {
const response = await $b24.actions.v2.call.make({ method, params, requestId })
if (!response.isSuccess) {
throw new Error(method + ': ' + response.getErrorMessages().join('; '))
}
return response.getData().result
}
// 1. Read the CRM item and take the link for integrations
const { item } = await call('crm.item.get', { entityTypeId, id: itemId }, 'crm-item-get')
const file = item[fieldName]
if (!file || !file.urlMachine) {
throw new Error('Field ' + fieldName + ' has no file or urlMachine link')
}
// 2. Download the file and encode it in Base64
const fileResponse = await fetch(file.urlMachine)
if (!fileResponse.ok) {
throw new Error('Failed to download the file: ' + fileResponse.status)
}
const fileContent = Buffer.from(await fileResponse.arrayBuffer()).toString('base64')
// 3. Upload the file to Drive
const diskFile = await call('disk.folder.uploadFile', {
id: folderId,
data: { NAME: fileName },
fileContent: [fileName, fileContent]
}, 'disk-uploadfile')
// 4. Attach the Drive file to the task
const attachment = await call('tasks.task.files.attach', {
taskId: taskId,
fileId: diskFile.ID
}, 'task-files-attach')
console.log('File attached, attachmentId:', attachment.attachmentId)
import base64
import requests
from b24pysdk import BitrixWebhook, Client
token = BitrixWebhook(
domain="your-domain.bitrix24.com",
webhook_token="user_id/webhook_key",
)
client = Client(token)
entity_type_id = 2
item_id = 6533
field_name = "ufCrm_1688736288"
folder_id = 1739
task_id = 3709
file_name = "contract.pdf"
# 1. Read the CRM item and take the link for integrations
item = token.call_method("crm.item.get", {
"entityTypeId": entity_type_id,
"id": item_id,
})["result"]["item"]
file = item.get(field_name)
if not file or not file.get("urlMachine"):
raise ValueError(f"Field {field_name} has no file or urlMachine link")
# 2. Download the file and encode it in Base64
file_response = requests.get(file["urlMachine"], timeout=30)
file_response.raise_for_status()
file_content = base64.b64encode(file_response.content).decode()
# 3. Upload the file to Drive
disk_file = client.disk.folder.uploadfile(
bitrix_id=folder_id,
data={"NAME": file_name},
file_content=[file_name, file_content],
).response.result
# 4. Attach the Drive file to the task
attachment = client.tasks.task.files.attach(
task_id=task_id,
file_id=disk_file["ID"],
).response.result
print("File attached, attachmentId:", attachment["attachmentId"])
require_once 'vendor/autoload.php';
use Bitrix24\SDK\Services\ServiceBuilderFactory;
use Symfony\Component\EventDispatcher\EventDispatcher;
$serviceBuilder = (new ServiceBuilderFactory(new EventDispatcher(), $log))
->initFromWebhook('https://your-domain.bitrix24.com/rest/USER_ID/TOKEN/');
$entityTypeId = 2;
$itemId = 6533;
$fieldName = 'ufCrm_1688736288';
$folderId = 1739;
$taskId = 3709;
$fileName = 'contract.pdf';
// 1. Read the CRM item and take the link for integrations
$item = $serviceBuilder->core->call(
'crm.item.get',
['entityTypeId' => $entityTypeId, 'id' => $itemId]
)->getResponseData()->getResult()['item'];
$file = $item[$fieldName] ?? null;
if (!$file || empty($file['urlMachine'])) {
throw new RuntimeException('Field ' . $fieldName . ' has no file or urlMachine link');
}
// 2. Download the file and encode it in Base64
$binary = file_get_contents($file['urlMachine']);
if ($binary === false) {
throw new RuntimeException('Failed to download the file');
}
$fileContent = base64_encode($binary);
// 3. Upload the file to Drive
$diskFile = $serviceBuilder->getDiskScope()->folder()->uploadFile(
$folderId,
['NAME' => $fileName],
[$fileName, $fileContent]
)->getFile();
// 4. Attach the Drive file to the task
$attachment = $serviceBuilder->core->call(
'tasks.task.files.attach',
['taskId' => $taskId, 'fileId' => $diskFile['ID']]
)->getResponseData()->getResult();
echo 'File attached, attachmentId: ' . $attachment['attachmentId'];