How to add images and documents in VtigerCRM API

How to add images and documents in VtigerCRM API

As I wrote earlier, Vtiger provides a convenient API for getting data from CRM. You can get and filter data by any module with one request, get related records, and so on.

However, there is one drawback in the standard VtigerCRM API - you cannot get, for example, a contact's avatar and related documents.

In this article, I will tell you how you can modify the standard VtigerCRM API by adding the ability to display a picture or related documents attached to the module.

Let's see how we display a picture from a contact or products. In the Vtiger_Record_Model class, we have a getImageDetails method that gives us all the information about the avatar. Based on this information, you can build a URL and get the file we need.

In a similar way, we can get a list of linked documents. The Vtiger_Record_Model class has a getFileDetails method. With its help, we can get an array of documents bound to the record of the module.

Those, avatar or attached files are created in a uniform manner. But what if you want to enable the delivery of files and images only for certain modules, and not for all. Loading the API with unnecessary data and database queries is a bad idea.

I found the following option. Add two fields to the module we need:

  • image_avatar - if we want to display the post's avatar.
  • images_list - if we want to display a list of related documents.

These fields will be systems. Usually for these purposes I make a separate block, I call it "System Information", put all the fields there that I shouldn't see the user and hide it from the system.

This is very easy to do. To do this, go to the modules/Vtiger/models/Block.php file

At the beginning of the class (line 14) Vtiger_Block_Model add a constant


public const SYSTEM_INFORMATION_LABEL = 'System Information';


Then in function getAllForModule (after line 118) in foreach($blockObjects as $blockObject) 

find line 

$blockModelList[] = self::getInstanceFromBlockObject($blockObject);

and change it to following:

    $blockModel = self::getInstanceFromBlockObject($blockObject);

    if ($blockModel->label === self::SYSTEM_INFORMATION_LABEL) {



$blockModelList[] = $blockModel;


Thus, if the block is called System Information, you can completely hide it from users. If necessary, you can add an additional condition and show the block only to the administrator.

Let's move on to the next step. Let's create a separate class that will pass us a list of files and images. Let's create a special service. Create a file modules/Vtiger/services/ImageReceiver.php with the following content:



class Vtiger_ImageReceiver_Service
public const AVATAR_FIELD = 'image_avatar';

public const DOCUMENTS_FIELD = 'images_list';

protected string $column;

protected ?string $value;

protected int $record_id;

public function __construct(int $record_id, string $column, ?string $value)
$this->record_id = $record_id;
$this->column = $column;
$this->value = $value;

public function getAvatar(): array
if ($this->record_id < 1) {
return [];
/** @var Contacts_Record_Model $recordModel */
$recordModel = Vtiger_Record_Model::getInstanceById($this->record_id);
if (!$recordModel) {
return [];
return $recordModel->getImageDetails();

public function getImages(): array
if ($this->record_id < 1) {
return [];
/** @var Vtiger_Record_Model $recordModel */
$recordModel = Vtiger_Record_Model::getInstanceById($this->record_id);
if (!$recordModel) {
return [];
return $recordModel->getFileDetails();


Now we just need to connect this class to Vtiger. Go to the file include/Webservices/DataTransform.php and at the very beginning of this file we include our class by adding the line:

require_once 'modules/Vtiger/services/ImageReceiver.php';


And then in the sanitizeDataWithColumn function at the very end, before the line


return $newRow;


add following:

if (array_key_exists(Vtiger_ImageReceiver_Service::AVATAR_FIELD, $row)) {
$receiverService = new Vtiger_ImageReceiver_Service((int) $row[$meta->getIdColumn()], Vtiger_ImageReceiver_Service::AVATAR_FIELD, $row[Vtiger_ImageReceiver_Service::AVATAR_FIELD]);
$newRow['avatar'] = $receiverService->getAvatar();
if (array_key_exists(Vtiger_ImageReceiver_Service::DOCUMENTS_FIELD, $row)) {
$receiverService = new Vtiger_ImageReceiver_Service((int) $row[$meta->getIdColumn()], Vtiger_ImageReceiver_Service::DOCUMENTS_FIELD, $row[Vtiger_ImageReceiver_Service::DOCUMENTS_FIELD]);
$newRow['images'] = $receiverService->getImages();


Thus, if you now want to get a list of contacts, the following array will be added to the API output:


"avatar": [


          "id": "74",

          "orgname": "2021-06-28_10-30.png",

          "path": "storage\/2021\/August\/week1\/74",

          "name": "2021-06-28_10-30.png",

          "url": "http:\/\/crm.kindergou.test\/public.php?fid=74&key=2021-06-28_10-30.png"



"images": [


          "0": "74",

          "attachmentsid": "74",

          "1": "2021-06-28_10-30.png",

          "name": "2021-06-28_10-30.png",

          "2": "",

          "description": "",

          "3": "image\/png",

          "type": "image\/png",

          "4": "storage\/2021\/August\/week1\/",

          "path": "storage\/2021\/August\/week1\/",

          "5": "89061811daaad0ac66dd2dfa0d331903.png",

          "storedname": "89061811daaad0ac66dd2dfa0d331903.png",

          "6": null,

          "subject": null,

          "7": "70",

          "crmid": "70",

          "8": "74"




It should be borne in mind here that this will only work in displaying the list of module entries. If you want to get images when requesting a record by id, then add the code in the same way to the filterAndSanitize function:


if (array_key_exists(Vtiger_ImageReceiver_Service::AVATAR_FIELD, $row)) {
$receiverService = new Vtiger_ImageReceiver_Service((int) vtws_getCRMEntityId($row['id']), Vtiger_ImageReceiver_Service::AVATAR_FIELD, $row[Vtiger_ImageReceiver_Service::AVATAR_FIELD]);
$row['avatar'] = $receiverService->getAvatar();
if (array_key_exists(Vtiger_ImageReceiver_Service::DOCUMENTS_FIELD, $row)) {
$receiverService = new Vtiger_ImageReceiver_Service((int) vtws_getCRMEntityId($row['id']), Vtiger_ImageReceiver_Service::DOCUMENTS_FIELD, $row[Vtiger_ImageReceiver_Service::DOCUMENTS_FIELD]);
$row['images'] = $receiverService->getImages();

As a bonus, I attached script for adding field images_list to Contacts module. Just create file in scripts folder and run it:


* Скрипт добавляет поле в модуль через API тигры.
* Удобно тем, что можно задать понятное название поле вместо стандартного cf_3443
* Также используется для создания одинаковых полей между девом и продом. Один и тот же скрипт запускается на деве и проде.
* Скрипты для удобство класть в vtiger/scripts/
* Перед запуском проверить, чтобы у файла были полномочия на запуск от сервере апаче
* Перед запуском проверить путь к файлам, которые добавлены через require_once
* Для правильного пути добавлена функция chdir('../')

$Vtiger_Utils_Log = true;
$module = Vtiger_Module::getInstance('Contacts'); // Имя модуля из таблицы vtiger_tab
if ($module) {
$block = Vtiger_Block::getInstance('LBL_CUSTOM_INFORMATION', $module); // Название блока из таблицы vtiger_blocks
if ($block) {
$field = Vtiger_Field::getInstance('images_list', $module); // Название поля без пробелов через нижнее подчеркивание
if (!$field) {
$field = new Vtiger_Field();
$field->name = 'images_list'; // Название поля без пробелов через нижнее подчеркивание
$field->table = $module->basetable;
$field->label = 'LBL_IMAGES_LIST'; // Лейбл на английском. Переводить на русский через файлы-переводов.
$field->column = 'images_list'; // Название поля без пробелов через нижнее подчеркивание
$field->columntype = 'VARCHAR(255)'; // Посмотреть тип у похожих полей в таблице vtiger_навание-модуля

// Посмотреть тип у похожих полей в таблице vtiger_field
// 1 - текстовое поле
// 15 - поле-список)
$field->uitype = 1;

$field->displaytype = 1; // Посмотреть тип у похожих полей в таблице vtiger_field

// Посмотреть тип у похожих полей в таблице vtiger_field
// V~O~LE~100 - текстовое поле
// V~O - поле список
$field->typeofdata = 'V~O~LE~100';

$field->presence = 2; // Посмотреть тип у похожих полей в таблице vtiger_field
$field->quickcreate = 1; // Посмотреть тип у похожих полей в таблице vtiger_field
$field->generatedtype = 2; // Посмотреть тип у похожих полей в таблице vtiger_field

//Для создания поля-списка раскомментировать эти две строки и прописать значения на английском. Переводить на русский через файлы-переводов.
//$pickListValues = array('Active', 'Inactive', 'Standby');

echo "Поле успешно добавлено.";
} else {
echo "Не найден блок. Сверьте название блока в таблице vtiger_blocks.";
} else {
echo "Не найден модуль. Сверьте название модуля в таблице vtiger_tab.";


If you have any questions or additional needs for the revision of the VtigerCRM API, please contact.

Popular Posts

My most popular posts

Maximum productivity on remote job

Maximum productivity on remote job

I started my own business and intentionally did my best to work from anywhere in the world. Sometimes I sit with my office with a large 27-inch monitor in my apartment in Cheboksary. Sometimes I’m in the office or in some cafe in another city.

Hello! I am Sergey Emelyanov and I am hardworker
Business PHP

Hello! I am Sergey Emelyanov and I am hardworker

I am a programmer. I am an entrepreneur in my heart. I started making money from the age of 11, in the harsh 90s, handing over glassware to a local store and exchanging it for sweets. I earned so much that was enough for various snacks.

Hire Professional CRM developer for $25 per hour

I will make time for your project. Knowledge of Vtiger CRM, SuiteCRM, Laravel, and Vue.js. I offer cooperation options that will help you take advantage of external experience, optimize costs and reduce risks. Full transparency of all stages of work and accounting for time costs. Pay only development working hours after accepting the task. Accept PayPal and Payoneer payment systems. How to hire professional developer? Just fill in the form