How to Upload a File to a Task
Who can execute the methods: to complete the whole scenario, you need permission to add files to a Drive folder, edit the task, and read the file
- 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
- disk.attachedObject.get — a user with the Read permission for the file
Choose a tool for developing with an AI agent:
- use Alaio Vibecode to build an app for Bitrix24 from a task description without knowing any programming language. The agent writes the code and deploys the app to a server, with no manual hosting setup
- use the MCP server to develop a REST API integration in your own project. The agent refers to the official REST documentation
Bitrix24 has two types of file fields:
- File. This field is not linked to Drive. Files are uploaded directly through a Base64 format string
- File (Drive). This field is linked to Drive. The field stores the Drive object ID. Base64 format is not processed in this field, so the file must first be uploaded to Bitrix24 Drive
The scenario has three steps:
- Upload the file to Drive using disk.folder.uploadFile
- Pass the Drive object's
IDto tasks.task.files.attach to attach it to the task - Verify the file's link to the task using disk.attachedObject.get
The file will appear in an existing task. Verify the attachment after attaching it: the second method returns the attachmentId needed for the third step.
Before You Start
To run the example, you need:
- an inbound webhook with the
diskandtaskscopes, created by a user with permission to add a file to a Drive folder, edit the task, and read the file - the ID
folderIdof the Drive folder where you will upload the file. Retrieve it using disk.storage.getChildren for a folder at the storage root or disk.folder.getChildren for a nested folder. The examples use1739 - the ID
taskIdof an existing task. Retrieve it using tasks.task.list. The examples use3709 - the file to attach, located in the directory where the script runs. The source file name is
avatar.jpg; its name on Drive, fromdata.NAME, isava555.jpg - the file contents as a Base64 string without a
data:*/*;base64,prefix. PassfileContentas an array containing the file name and this string
The webhook runs with the permissions of the user who created it. Its URL grants access to methods within its scopes: store it in server environment variables, not in browser code or a repository. For JS and PHP, set B24_HOOK to the full webhook URL. For Python, set B24_DOMAIN to the Bitrix24 domain and B24_WEBHOOK_TOKEN to a value in the form USER_ID/TOKEN.
The JS example requires Node.js 22 or later and @bitrix24/b24jssdk. The code uses ES modules: save it in a .mjs file or add "type": "module" to package.json. The Python example requires Python 3.9 or later and b24pysdk. The PHP example requires PHP 8.4 or later and bitrix24/b24phpsdk version ^3.0.
Initialize the SDK before calling a method. Put the initialization code and the following snippets for your chosen language in the same script.
How to Use Examples in Documentation
import { readFile } from 'node:fs/promises'
import { B24Hook } from '@bitrix24/b24jssdk'
const $b24 = B24Hook.fromWebhookUrl(process.env.B24_HOOK)
require_once 'vendor/autoload.php';
use Bitrix24\SDK\Services\ServiceBuilderFactory;
use Monolog\Logger;
use Symfony\Component\EventDispatcher\EventDispatcher;
$log = new Logger('b24');
$serviceBuilder = (new ServiceBuilderFactory(new EventDispatcher(), $log))
->initFromWebhook(getenv('B24_HOOK'));
import base64
import os
from pathlib import Path
from b24pysdk import BitrixWebhook, Client
token = BitrixWebhook(
domain=os.environ["B24_DOMAIN"],
webhook_token=os.environ["B24_WEBHOOK_TOKEN"],
)
client = Client(token)
1. Upload the File to Bitrix24 Drive
Use the disk.folder.uploadFile method with the following parameters:
id— specify the value1739, the identifier of the Drive folder where the file is uploadeddata— specify the file name inNAME. The file will be saved in Bitrix24 Drive with this namefileContent— pass the file in the format['file_name.extension', 'file as a Base64-encoded string']
Uploading the file to Drive is required because the UF_TASK_WEBDAV_FILES field in tasks accepts only Drive file IDs.
const fileName = 'avatar.jpg'
const fileBase64 = (await readFile(fileName)).toString('base64')
const uploadResponse = await $b24.actions.v2.call.make({
method: 'disk.folder.uploadFile',
params: {
id: 1739,
data: {
NAME: 'ava555.jpg'
},
fileContent: [
fileName,
fileBase64
]
},
requestId: 'disk-uploadfile'
})
if (!uploadResponse.isSuccess) {
throw new Error(uploadResponse.getErrorMessages().join('; '))
}
const uploadedFile = uploadResponse.getData().result
$fileName = 'avatar.jpg';
$fileBytes = file_get_contents($fileName);
if ($fileBytes === false) {
throw new RuntimeException('Could not read the file');
}
$fileBase64 = base64_encode($fileBytes);
$uploadedFile = $serviceBuilder->getDiskScope()->folder()->uploadFile(
1739,
['NAME' => 'ava555.jpg'],
[
$fileName,
$fileBase64
]
)->getFile();
echo '<PRE>';
print_r($uploadedFile);
echo '</PRE>';
file_name = "avatar.jpg"
file_base64 = base64.b64encode(Path(file_name).read_bytes()).decode("ascii")
uploaded_file = client.disk.folder.uploadfile(
bitrix_id=1739,
data={
"NAME": "ava555.jpg",
},
file_content=[
file_name,
file_base64,
],
).response.result
As a result of uploading the file to Drive, you get two different file ID values:
FILE_ID:28073— the internal file IDID:6687— the Drive object ID. Use this value when working with File (Drive) fields
If you pass FILE_ID instead of ID in a request that updates a File (Drive) field, the file will either not be attached because there is no Drive object with that ID, or a different file will be attached.
{
"result": {
"ID": 6687,
"NAME": "ava555.jpg",
"TYPE": "file",
"PARENT_ID": "1739",
"FILE_ID": 28073,
"SIZE": "405559"
}
}
2. Attach the File to the Task
Use the tasks.task.files.attach method with the following parameters:
taskId— the task ID. To get the ID, use the tasks.task.list methodfileId— pass the Drive object'sIDfrom the previous method's response. In the example response, this is6687
const attachResponse = await $b24.actions.v2.call.make({
method: 'tasks.task.files.attach',
params: {
taskId: 3709,
fileId: Number(uploadedFile.ID)
},
requestId: 'task-files-attach'
})
if (!attachResponse.isSuccess) {
throw new Error(attachResponse.getErrorMessages().join('; '))
}
const attachment = attachResponse.getData().result
// This method has no typed wrapper, so call it through the SDK core
$attachment = $serviceBuilder->core->call(
'tasks.task.files.attach',
[
'taskId' => 3709,
'fileId' => $uploadedFile['ID']
]
)->getResponseData()->getResult();
echo '<PRE>';
print_r($attachment);
echo '</PRE>';
attachment = client.tasks.task.files.attach(
task_id=3709,
file_id=int(uploaded_file["ID"]),
).response.result
The response returns the link ID between the Drive file and the task: 423. To verify the attachment using this ID, use the disk.attachedObject.get method.
{
"result": {
"attachmentId": 423
}
}
Check the Result
Pass attachmentId from the tasks.task.files.attach response to the id parameter of disk.attachedObject.get. The example response contains 423; the code uses the ID of the current attachment.
const checkResponse = await $b24.actions.v2.call.make({
method: 'disk.attachedObject.get',
params: {
id: Number(attachment.attachmentId)
},
requestId: 'disk-attached-object-get'
})
if (!checkResponse.isSuccess) {
throw new Error(checkResponse.getErrorMessages().join('; '))
}
console.log(checkResponse.getData().result)
// This method has no typed wrapper, so call it through the SDK core
$file = $serviceBuilder->core->call(
'disk.attachedObject.get',
[
'id' => $attachment['attachmentId']
]
)->getResponseData()->getResult();
print_r($file);
# This method has no typed wrapper, so use a direct call
file = token.call_method(
"disk.attachedObject.get",
{
"id": attachment["attachmentId"],
},
)["result"]
print(file)
The method returns the attached file data. The scenario is successful if:
IDmatchesattachmentIdfrom the previous stepOBJECT_IDcontains the Drive file identifierENTITY_TYPEequalstasks_taskENTITY_IDequals the task identifierNAMEcontains the attached file name
Open the task in Bitrix24 and check that ava555.jpg appears among the attached files.
{
"result": {
"ID": "423",
"OBJECT_ID": "6687",
"MODULE_ID": "tasks",
"ENTITY_TYPE": "tasks_task",
"ENTITY_ID": "3709",
"NAME": "ava555.jpg",
"SIZE": "405559"
}
}
Errors and Diagnostics
If the method returns an error, check the request data.
|
Error |
Cause and solution |
|
|
The folder with the specified |
|
|
The file name was not passed in |
|
|
The file could not be saved. Check free space in Drive and the Base64 data |
|
|
The webhook user does not have permission to add the file to the folder or read the file |
|
|
An invalid type was passed in |
|
|
The required |
|
|
|
|
An empty result in disk.attachedObject.get |
|
Repeat the scenario from the step that returned the error. If the file has already been uploaded to Drive, do not upload it again: fix taskId or fileId and repeat only the tasks.task.files.attach call.
Things to Consider
If the file is already on Drive, skip the upload and pass its ID to fileId in tasks.task.files.attach. To use a different task, change taskId. In both cases, check that the webhook user has access to the file and task.