How to Retrieve a List of Vendors
Scope:
crmWho can execute the methods: to complete the entire scenario, the strictest of the listed rights is required — permission to read contacts or companies in CRM
- crm.item.list — a user with permission to read items of a CRM object
- crm.category.list — any user
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
There is no separate "vendor" object in CRM. Vendors are contacts and companies from a system pipeline with a special code:
-
CATALOG_CONTRACTOR_CONTACT— for a contact -
CATALOG_CONTRACTOR_COMPANY— for a company
The identifier of this pipeline is different in every Bitrix24 and cannot be hardcoded as a constant. That is why we first request the identifier by the pipeline code and then filter the items by it.
As a result of the scenario, we get a list of vendors with their identifiers. These identifiers are accepted by the inventory management methods — for example, catalog.documentcontractor.add links a vendor to an inventory document.
The scenario consists of two steps.
- Retrieve the
idof the system vendor pipeline using the crm.category.list method - Retrieve the items of that pipeline using the crm.item.list method
Before You Start
-
The webhook is created on behalf of a user who has permission to read contacts and companies in CRM
-
The
crmscope is selected in the webhook permissions -
Inventory management is enabled in Bitrix24: the system vendor pipelines are created together with it
-
The webhook user has access to the vendor pipeline. Step 1 returns only the pipelines the user is allowed to read, and step 2 — only the items visible to them
-
The webhook URL grants full access within its scope. Retain the URL in an environment variable and never publish it in open code
-
You have decided which records you retrieve: contacts or companies
|
What we pass |
Contact |
Company |
|
|
|
|
|
System pipeline code |
|
|
|
Name fields in |
|
|
The examples below request contacts. What to replace for companies is described in the Key Considerations section.
1. Retrieve the Vendor Pipeline ID
We will use the crm.category.list method with the following parameters:
-
entityTypeId— the CRM object type identifier, a required parameter. We will specify3— a contact -
filter[code]— a filter by pipeline code. We will specifyCATALOG_CONTRACTOR_CONTACT. Without the filter, the method returns all contact pipelines, including the general one
How to Use Examples in Documentation
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 result = await $b24.actions.v2.call.make({
method: 'crm.category.list',
params: {
entityTypeId: 3,
filter: {
code: 'CATALOG_CONTRACTOR_CONTACT'
}
}
});
console.log(result.getData().result);
from b24pysdk import BitrixWebhook, Client
client = Client(
BitrixWebhook(
domain="your-domain.bitrix24.com",
webhook_token="user_id/webhook_key",
)
)
# the b24pysdk wrapper accepts only entity_type_id, so we select the pipeline by code in the response
result = client.crm.category.list(
entity_type_id=3, # 3 — a contact
).response.result
categories = [
category
for category in result.get("categories", [])
if category.get("code") == "CATALOG_CONTRACTOR_CONTACT"
]
print(categories)
// composer require bitrix24/b24phpsdk:"^3.0"
require_once 'vendor/autoload.php';
use Bitrix24\SDK\Services\ServiceBuilderFactory;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
$logger = new Logger('b24');
$logger->pushHandler(new StreamHandler('php://stdout'));
$serviceBuilder = (new ServiceBuilderFactory(new EventDispatcher(), $logger))
->initFromWebhook('https://your-domain.bitrix24.com/rest/USER_ID/TOKEN/');
// crm.category.list has no wrapper in the SDK — we call the method directly
$result = $serviceBuilder->core->call(
'crm.category.list',
[
'entityTypeId' => 3, // 3 — a contact
'filter' => [
'code' => 'CATALOG_CONTRACTOR_CONTACT' // The system vendor pipeline code
]
]
);
As a result, we get the pipeline identifier. In the example, id: 15. In your Bitrix24 the value will be different — do not carry 15 over into working code, request the identifier with this step.
{
"result": {
"categories": [
{
"id": 15,
"name": "Vendor Contacts",
"sort": 500,
"entityTypeId": 3,
"isDefault": "N",
"isSystem": "Y",
"code": "CATALOG_CONTRACTOR_CONTACT"
}
]
},
"total": 1
}
The isSystem: Y flag confirms that the pipeline was created by the system, not by a user. Retain the id of the first item of the categories array — in step 2 it becomes the value of the categoryId filter.
Before calling step 2, check that the categories array is not empty. If categoryId: null is passed in the filter, the crm.item.list method returns not vendors but the contacts of the general pipeline.
2. Retrieve the List of Vendors
We will filter the items using the crm.item.list method with the following parameters:
-
entityTypeId— the CRM object type identifier, a required parameter. We will specify3— a contact -
filter[categoryId]— the system pipeline identifier from step 1. In the example,15 -
select— a list of fields to return. We will specifyid,name,lastName, andcategoryId. The full set of object fields is returned by the crm.item.fields method with the sameentityTypeId
const result = await $b24.actions.v2.call.make({
method: 'crm.item.list',
params: {
entityTypeId: 3,
select: ['id', 'name', 'lastName', 'categoryId'],
filter: {
categoryId: 15
}
}
});
console.log(result.getData().result);
result = client.crm.item.list(
entity_type_id=3,
select=["id", "name", "lastName", "categoryId"],
filter={
"categoryId": 15,
},
).response.result
$result = $serviceBuilder->getCRMScope()->item()->list(
3,
[],
[
'categoryId' => 15
],
['id', 'name', 'lastName', 'categoryId']
);
As a result, we get a list of vendor contacts.
{
"result": {
"items": [
{
"id": 2185,
"name": "Stefan",
"lastName": null,
"categoryId": 15
},
{
"id": 2443,
"name": "Klaus",
"lastName": "Weber",
"categoryId": 15
}
]
},
"total": 2
}
The lastName field can be empty: only the first name is required for a contact. When printing the list, join name and lastName with a space and trim the extra spaces.
Verify the Result
The scenario is complete if the categoryId field of every item of the items array matches the id of the pipeline from step 1.
-
The
totalfield shows how many vendors were found in total. Per call, the method returns no more than 50 items. Iftotalis greater than 50, the response holds only the first page — retrieve the rest with repeated calls using thestartparameter:50,100, and so on -
In the interface, the same list opens in the CRM → Contacts section. Switch to the pipeline with the title from the
namefield of step 1 — by default it is "Vendor Contacts". The number of items in it matchestotal
If total equals zero, the method worked correctly and there are simply no vendors in Bitrix24. A vendor can be created following the How to Create a Vendor in CRM scenario.
Errors and Diagnostics
If the method returned an error, check the request data.
|
Code |
Cause and Action |
|
|
|
|
|
In step 1, a CRM object that has no pipelines is passed. Vendors exist only for contacts and companies |
|
|
In step 2, the |
|
|
The webhook is created on behalf of an external user. The scenario is available to Bitrix24 employees only |
An empty categories array in step 1 is not a method error. There are two reasons for it:
-
Inventory management is not enabled in Bitrix24, so there are no system vendor pipelines
-
The webhook user has no access to the vendor pipeline
To tell the reasons apart, run step 1 with an administrator webhook. If the administrator sees the pipeline and the original webhook does not, the cause is the permissions of its user. If the administrator does not see it either, inventory management is not enabled.
An empty items array in step 2 with a non-empty step 1 means that the pipeline has no items visible to the user. Check that categoryId is taken from the response of step 1 and not hardcoded as a number from another Bitrix24.
Both methods only read data, so after an error the scenario can be repeated from any step.
Key Considerations
-
To retrieve vendor companies, replace
entityTypeIdwith4, the pipeline code withCATALOG_CONTRACTOR_COMPANY, and thenameandlastNamefields inselectwithtitle.entityTypeIdand the pipeline code have to be changed together: withentityTypeId:4, the contact code returns an emptycategoriesarray -
Contacts and companies are different CRM objects and cannot be retrieved in a single call. To build a combined list of vendors, run the scenario twice and merge the results in your code
-
The vendor pipeline cannot be created or deleted via REST: crm.category.add forbids adding system pipelines, and crm.category.delete responds to a deletion with the
REMOVING_DISABLEDerror
Code Example
The code goes through both steps and prints the list of vendors. The only things to replace are the webhook URL and, for companies, the values of entityTypeId and the pipeline code in the first lines of the example.
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 entityTypeId = 3; // 3 — a contact; for a company specify 4
const categoryCode = 'CATALOG_CONTRACTOR_CONTACT'; // for a company specify CATALOG_CONTRACTOR_COMPANY
const resultCategory = await $b24.actions.v2.call.make({
method: 'crm.category.list',
params: {
entityTypeId: entityTypeId,
filter: { code: categoryCode }
}
});
if (!resultCategory.isSuccess) {
console.error(resultCategory.getErrorMessages().join('; '));
} else {
const categories = resultCategory.getData().result.categories || [];
if (!categories.length) {
console.error('Vendor pipeline not found');
} else {
const categoryId = categories[0].id;
const resultItems = await $b24.actions.v2.call.make({
method: 'crm.item.list',
params: {
entityTypeId: entityTypeId,
select: ['id', 'name', 'lastName', 'categoryId'],
filter: { categoryId: categoryId },
order: { id: 'DESC' }
}
});
if (!resultItems.isSuccess) {
console.error(resultItems.getErrorMessages().join('; '));
} else {
console.log(resultItems.getData().result);
}
}
}
from b24pysdk import BitrixWebhook, Client
from b24pysdk.errors import BitrixAPIError
client = Client(
BitrixWebhook(
domain="your-domain.bitrix24.com",
webhook_token="user_id/webhook_key",
)
)
entity_type_id = 3 # 3 — a contact; for a company specify 4
category_code = (
"CATALOG_CONTRACTOR_CONTACT"
if entity_type_id == 3
else "CATALOG_CONTRACTOR_COMPANY"
)
try:
categories_response = client.crm.category.list(
entity_type_id=entity_type_id,
).response.result.get("categories", [])
categories = [
category
for category in categories_response
if category.get("code") == category_code
]
except BitrixAPIError as error:
print(error)
else:
if not categories:
print("Vendor pipeline not found")
else:
try:
items_result = client.crm.item.list(
entity_type_id=entity_type_id,
select=["id", "name", "lastName", "categoryId"],
filter={"categoryId": categories[0]["id"]},
order={"id": "DESC"},
).response.result
except BitrixAPIError as error:
print(error)
else:
print(items_result)
<?php
// composer require bitrix24/b24phpsdk:"^3.0"
require_once 'vendor/autoload.php';
use Bitrix24\SDK\Services\ServiceBuilderFactory;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
$logger = new Logger('b24');
$logger->pushHandler(new StreamHandler('php://stdout'));
$serviceBuilder = (new ServiceBuilderFactory(new EventDispatcher(), $logger))
->initFromWebhook('https://your-domain.bitrix24.com/rest/USER_ID/TOKEN/');
$entityTypeId = 3; // 3 — a contact; for a company specify 4
$categoryCode = 'CATALOG_CONTRACTOR_CONTACT'; // for a company specify CATALOG_CONTRACTOR_COMPANY
try {
$resultCategory = $serviceBuilder->core->call(
'crm.category.list',
[
'entityTypeId' => $entityTypeId,
'filter' => [
'code' => $categoryCode
]
]
);
$categories = $resultCategory->getResponseData()->getResult()['categories'] ?? [];
if (empty($categories)) {
echo 'Vendor pipeline not found';
return;
}
$categoryId = $categories[0]['id'];
$resultItems = $serviceBuilder->getCRMScope()->item()->list(
$entityTypeId,
[
'id' => 'DESC'
],
[
'categoryId' => $categoryId
],
['id', 'name', 'lastName', 'categoryId']
);
print_r($resultItems->getItems());
} catch (\Throwable $e) {
echo $e->getMessage();
}