Как получить список стадий с семантикой для объектов CRM
Scope:
crmКто может выполнять метод: любой пользователь с правом на чтение хотя бы одного объекта CRM
Выберите инструмент для разработки с AI-агентом:
- используйте Битрикс24 Вайбкод, чтобы создать приложение для Битрикс24 по описанию задачи без знания языков программирования. Агент напишет код и разместит приложение на сервере без ручной настройки хостинга
- используйте MCP-сервер, чтобы разрабатывать интеграцию через REST API в своем проекте. Агент будет обращаться к официальной REST-документации
Семантика стадии отражает текущее состояние элемента CRM: в работе, успешно завершен или неуспешно. Система использует значение семантики в автоматизации и в построении отчетов.
Стадии любого объекта CRM возвращает один метод — crm.status.list. Он отдает стадии одного справочника, который задается кодом ENTITY_ID в фильтре. В результате получим список стадий с семантикой для выбранного объекта.
Сценарий состоит из двух шагов.
- Определить код справочника
ENTITY_IDдля нужного объекта CRM - Получить стадии этого справочника методом crm.status.list и прочитать семантику каждой стадии
1. Определяем код справочника
Код справочника зависит от объекта CRM и от воронки.
|
Объект CRM |
Код |
Где взять числовую часть |
|
|
Постоянный код, числовой части нет |
|
|
|
|
|
|
|
Постоянный код, числовой части нет |
|
|
|
|
|
|
|
|
|
|
|
|
В коде смарт-процесса используется entityTypeId, а не id из ответа crm.type.list. Это разные числа: у смарт-процесса с id: 7 значение entityTypeId равно 177, поэтому код справочника его основной воронки — DYNAMIC_177_STAGE_7, а не DYNAMIC_7_STAGE_7.
Для лидов код постоянный, поэтому дальше в примерах используем STATUS.
2. Получаем стадии с семантикой
Вызовем метод crm.status.list с параметрами:
filter— укажем полеENTITY_IDсо значениемSTATUS, чтобы получить стадии лидовorder— отсортируем по полюSORTпо возрастанию, чтобы стадии шли в том же порядке, что и в интерфейсе
Как использовать примеры в документации
import { B24Hook } from '@bitrix24/b24jssdk'
const $b24 = B24Hook.fromWebhookUrl(process.env.B24_HOOK)
// B24_HOOK = 'https://your-domain.bitrix24.ru/rest/USER_ID/TOKEN/'
const result = await $b24.actions.v2.call.make({
method: 'crm.status.list',
params: {
order: { SORT: 'ASC' }, // сортируем по возрастанию значения в поле SORT
filter: { ENTITY_ID: 'STATUS' }, // получим стадии для лидов
},
requestId: 'status-list'
});
if (result.isSuccess) {
console.dir(result.getData().result);
} else {
console.error(result.getErrorMessages().join('; '));
}
// 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;
$log = new Logger('b24');
$log->pushHandler(new StreamHandler('php://stdout'));
$sb = (new ServiceBuilderFactory(new EventDispatcher(), $log))
->initFromWebhook('https://your-domain.bitrix24.ru/rest/USER_ID/TOKEN/');
$statuses = $sb->getCRMScope()->status()->list(
['SORT' => 'ASC'], // сортируем по возрастанию значения в поле SORT
['ENTITY_ID' => 'STATUS'] // получим стадии для лидов
)->getStatuses();
from b24pysdk import BitrixWebhook, Client
client = Client(
BitrixWebhook(
domain="your-domain.bitrix24.com",
webhook_token="user_id/webhook_key",
)
)
result = client.crm.status.list(
order={"SORT": "ASC"}, # сортируем по возрастанию значения в поле SORT
filter={"ENTITY_ID": "STATUS"}, # получим стадии для лидов
).response.result
В результате получим массив с объектами, где каждый объект — это описание стадии. Ответ сокращен, показаны первая и две последние стадии.
{
"result": [
{
"ID": "1",
"ENTITY_ID": "STATUS",
"STATUS_ID": "NEW",
"NAME": "Не обработан",
"NAME_INIT": "Не обработан",
"SORT": "10",
"SYSTEM": "Y",
"CATEGORY_ID": null,
"COLOR": "#00FFFF",
"SEMANTICS": null,
"EXTRA": {
"SEMANTICS": "process",
"COLOR": "#00FFFF"
}
},
{
"ID": "15",
"ENTITY_ID": "STATUS",
"STATUS_ID": "CONVERTED",
"NAME": "Сконвертирован",
"NAME_INIT": "Сконвертирован",
"SORT": "50",
"SYSTEM": "Y",
"CATEGORY_ID": null,
"COLOR": "#37B44A",
"SEMANTICS": "S",
"EXTRA": {
"SEMANTICS": "success",
"COLOR": "#37B44A"
}
},
{
"ID": "17",
"ENTITY_ID": "STATUS",
"STATUS_ID": "JUNK",
"NAME": "Некачественный лид",
"NAME_INIT": "Некачественный лид",
"SORT": "60",
"SYSTEM": "Y",
"CATEGORY_ID": null,
"COLOR": "#F54819",
"SEMANTICS": "F",
"EXTRA": {
"SEMANTICS": "failure",
"COLOR": "#F54819"
}
}
],
"total": 6
}
Читаем семантику из ответа
Метод возвращает семантику в двух разных полях, и какое из них заполнено — зависит от объекта CRM.
-
EXTRA.SEMANTICS— текстовая семантика:process,successилиfailure. Метод добавляет объектEXTRAтолько для лидов, сделок и предложений, то есть для кодовSTATUS,DEAL_STAGE,DEAL_STAGE_{categoryId}иQUOTE_STATUS -
SEMANTICS— краткая семантика:null,SилиF. Это поле заполнено у всех объектов CRM, включая счета, документы и смарт-процессы
Код, который читает только EXTRA.SEMANTICS, для счетов, документов и смарт-процессов получит пустое значение на каждой стадии. Успешные и неуспешные финалы у них ошибочно попадут в группу «в работе».
Чтобы код работал для любого объекта CRM, читайте EXTRA.SEMANTICS, а при его отсутствии переводите краткое значение SEMANTICS в текстовое по таблице.
|
|
|
Состояние элемента CRM |
|
|
|
Элемент в работе |
|
|
|
Работа с элементом завершилась успешно |
|
|
|
Работа с элементом завершилась неуспешно |
Для сравнения — ответ для основной воронки смарт-процесса с entityTypeId: 177, то есть с фильтром ENTITY_ID: DYNAMIC_177_STAGE_7. Ключа EXTRA в объектах нет, семантика есть только в поле SEMANTICS.
{
"result": [
{
"ID": "263",
"ENTITY_ID": "DYNAMIC_177_STAGE_7",
"STATUS_ID": "DT177_7:NEW",
"NAME": "Начало",
"NAME_INIT": "Начало",
"SORT": "10",
"SYSTEM": "Y",
"CATEGORY_ID": "7",
"COLOR": "#22B9FF",
"SEMANTICS": null
},
{
"ID": "269",
"ENTITY_ID": "DYNAMIC_177_STAGE_7",
"STATUS_ID": "DT177_7:SUCCESS",
"NAME": "Успех",
"NAME_INIT": "Успех",
"SORT": "40",
"SYSTEM": "Y",
"CATEGORY_ID": "7",
"COLOR": "#00ff00",
"SEMANTICS": "S"
},
{
"ID": "271",
"ENTITY_ID": "DYNAMIC_177_STAGE_7",
"STATUS_ID": "DT177_7:FAIL",
"NAME": "Провал",
"NAME_INIT": "Провал",
"SORT": "50",
"SYSTEM": "Y",
"CATEGORY_ID": "7",
"COLOR": "#ff0000",
"SEMANTICS": "F"
}
],
"total": 5
}
Проверим результат
Сценарий выполнен, если в ответе есть стадии и у каждой определилась семантика.
-
в ответе непустой массив
result, а полеtotalсовпадает с количеством стадий выбранной воронки в интерфейсе Битрикс24 -
ровно одна стадия имеет
SEMANTICS:S. Так устроена любая воронка: успешный финал в ней один. Неуспешных финалов со значениемFможет быть несколько, у остальных стадий значениеnull -
значения
NAMEсовпадают с названиями стадий в канбане объекта CRM. Стадии лидов открываются в разделе CRM → Лиды → Канбан, стадии смарт-процесса — в его канбане на вкладке нужной воронки
Если объект в ответе один и его NAME совпадает с названием стадии, а ENTITY_ID — с кодом из шага 1, семантику можно проверить точечно: у финальной успешной стадии SEMANTICS равно S.
Ошибки и диагностика
Если метод вернул ошибку, проверьте данные запроса.
|
Код |
Причина и действие |
|
|
У пользователя нет прав на чтение объектов CRM. Проверьте, от имени какого пользователя создан вебхук |
|
|
В |
|
|
В |
Метод может вернуть пустой result без ошибки. Это значит, что справочника с таким кодом в Битрикс24 нет.
-
проверьте числовую часть кода. Для смарт-процессов частая причина —
idвместоentityTypeId: кодDYNAMIC_7_STAGE_7вернет пустой список, аDYNAMIC_177_STAGE_7— стадии -
проверьте идентификатор воронки. Воронку могли удалить или у объекта ее вообще нет — актуальный список возвращает метод crm.category.list
Метод ничего не изменяет, поэтому повторять вызов после ошибки можно сколько угодно раз.
Что важно учитывать
-
метод игнорирует параметр
selectи всегда возвращает полный набор полей стадии. Сокращать выборку на стороне API не получится, отбирайте нужные поля уже в своем коде -
у каждой воронки свой справочник стадий. Стадии всех воронок одного объекта одним вызовом не получить — переберите воронки из метода crm.category.list и вызовите crm.status.list для каждого кода
-
STATUS_IDуникален только внутри своего справочника. У стадий разных воронок совпадают и коды, и названия, поэтому храните стадию вместе с ееENTITY_ID -
поле
SEMANTICSв фильтре принимает только строку. Значениеnullв фильтре не задать — стадии в работе отбирайте уже в своем коде -
у объекта «Документ компании» отдельный код справочника —
SMART_B2E_DOC_STAGE_{categoryId}, егоentityTypeIdравен39. Он работает по тем же правилам, что и остальные коды с числовой частью
Пример кода
Код выводит таблицы со списком стадий для лидов и для основной воронки смарт-процесса. Семантика определяется универсально, поэтому таблица собирается одинаково для объекта с EXTRA и без него.
Замените DYNAMIC_177_STAGE_7 на код справочника своего объекта из шага 1.
import { B24Hook } from '@bitrix24/b24jssdk'
const $b24 = B24Hook.fromWebhookUrl(process.env.B24_HOOK)
// B24_HOOK = 'https://your-domain.bitrix24.ru/rest/USER_ID/TOKEN/'
// Краткая семантика в текстовую: этих значений всего три
const SEMANTICS_MAP = { S: 'success', F: 'failure' };
/**
* Загружает все стадии для заданного ENTITY_ID
* @param {string} entityId — код справочника, например, 'STATUS' или 'DYNAMIC_177_STAGE_7'
* @returns {Promise<Array>} — массив всех стадий
*/
async function loadStatuses(entityId) {
const result = await $b24.actions.v2.call.make({
method: 'crm.status.list',
params: {
filter: { ENTITY_ID: entityId },
order: { SORT: 'ASC' }
},
requestId: 'status-list'
});
if (!result.isSuccess) {
throw new Error(result.getErrorMessages().join('; '));
}
return result.getData().result;
}
/**
* Возвращает семантику стадии для любого объекта CRM.
* EXTRA есть только у лидов, сделок и предложений, поэтому для остальных
* объектов переводим краткое значение SEMANTICS в текстовое
*/
function getSemantics(item) {
return item.EXTRA?.SEMANTICS || SEMANTICS_MAP[item.SEMANTICS] || 'process';
}
/**
* Группирует стадии по семантике
*/
function groupStatusesBySemantics(statuses) {
const groups = { success: [], process: [], failure: [] };
statuses.forEach(item => {
const name = item.NAME || item.STATUS_ID;
groups[getSemantics(item)].push(name);
});
return groups;
}
/**
* Форматирует группы для console.table
*/
function formatForConsoleTable(groups) {
const { success, process, failure } = groups;
const maxLen = Math.max(success.length, process.length, failure.length);
const pad = (arr, len) => [...arr, ...Array(len - arr.length).fill('')];
return Array(maxLen).fill().map((_, i) => ({
'Успех': pad(success, maxLen)[i],
'В работе': pad(process, maxLen)[i],
'Неуспех': pad(failure, maxLen)[i]
}));
}
// Запрашиваем стадии: у лидов семантика в EXTRA, у смарт-процесса — в SEMANTICS
Promise.all([
loadStatuses('STATUS').then(data => ({ type: 'Лиды', data })),
loadStatuses('DYNAMIC_177_STAGE_7').then(data => ({ type: 'Смарт-процесс', data }))
]).then(results => {
results.forEach(({ type, data }) => {
console.group(type);
const groups = groupStatusesBySemantics(data);
console.table(formatForConsoleTable(groups));
console.groupEnd();
});
}).catch(err => {
console.error('Ошибка загрузки:', err);
});
<?php
// composer require bitrix24/b24phpsdk:"^3.0"
require_once 'vendor/autoload.php';
use Bitrix24\SDK\Services\ServiceBuilderFactory;
use Bitrix24\SDK\Services\ServiceBuilder;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
$log = new Logger('b24');
$log->pushHandler(new StreamHandler('php://stdout'));
$sb = (new ServiceBuilderFactory(new EventDispatcher(), $log))
->initFromWebhook('https://your-domain.bitrix24.ru/rest/USER_ID/TOKEN/');
// Краткая семантика в текстовую: этих значений всего три
const SEMANTICS_MAP = ['S' => 'success', 'F' => 'failure'];
/**
* Получает все стадии для заданного ENTITY_ID
*/
function loadStatuses(ServiceBuilder $sb, string $entityId): array {
return $sb->getCRMScope()->status()->list(
['SORT' => 'ASC'],
['ENTITY_ID' => $entityId]
)->getStatuses();
}
/**
* Возвращает семантику стадии для любого объекта CRM.
* EXTRA есть только у лидов, сделок и предложений, поэтому для остальных
* объектов переводим краткое значение SEMANTICS в текстовое
*/
function getSemantics($item): string {
$extra = $item->EXTRA['SEMANTICS'] ?? '';
if ($extra !== '') {
return $extra;
}
return SEMANTICS_MAP[$item->SEMANTICS] ?? 'process';
}
/**
* Группирует стадии по семантике
*/
function groupStatusesBySemantics(array $statuses): array {
$groups = ['success' => [], 'process' => [], 'failure' => []];
foreach ($statuses as $item) {
$name = $item->NAME ?? $item->STATUS_ID;
$groups[getSemantics($item)][] = $name;
}
return $groups;
}
/**
* Форматирует строки таблицы
*/
function buildTableRows(array $groups): array {
$max = max(count($groups['success']), count($groups['process']), count($groups['failure']));
$success = array_pad($groups['success'], $max, '');
$process = array_pad($groups['process'], $max, '');
$failure = array_pad($groups['failure'], $max, '');
$rows = [];
for ($i = 0; $i < $max; $i++) {
$rows[] = [
htmlspecialchars($success[$i]),
htmlspecialchars($process[$i]),
htmlspecialchars($failure[$i])
];
}
return $rows;
}
// У лидов семантика в EXTRA, у смарт-процесса — в SEMANTICS
$entities = [
['title' => 'Стадии лидов', 'entityId' => 'STATUS'],
['title' => 'Стадии смарт-процесса', 'entityId' => 'DYNAMIC_177_STAGE_7']
];
foreach ($entities as $entity) {
try {
$statuses = loadStatuses($sb, $entity['entityId']);
if (empty($statuses)) {
echo "<p>Нет стадий для " . htmlspecialchars($entity['title']) . "</p>\n";
continue;
}
$rows = buildTableRows(groupStatusesBySemantics($statuses));
echo "<h2>" . htmlspecialchars($entity['title']) . "</h2>\n";
echo "<table border=\"1\" style=\"border-collapse: collapse; width: 100%;\">\n";
echo "<thead><tr>
<th style=\"padding: 8px; background: #d4edda;\">Успех</th>
<th style=\"padding: 8px; background: #fff3cd;\">В работе</th>
<th style=\"padding: 8px; background: #f8d7da;\">Неуспех</th>
</tr></thead>\n<tbody>";
foreach ($rows as $row) {
echo "<tr>
<td style=\"padding: 6px;\">{$row[0]}</td>
<td style=\"padding: 6px;\">{$row[1]}</td>
<td style=\"padding: 6px;\">{$row[2]}</td>
</tr>\n";
}
echo "</tbody></table><br>\n";
} catch (\Throwable $e) {
echo "<p style=\"color: red;\">Ошибка: " . htmlspecialchars($e->getMessage()) . "</p>\n";
}
}
from b24pysdk import BitrixWebhook, Client
from b24pysdk.errors import BitrixAPIError
# Краткая семантика в текстовую: этих значений всего три
SEMANTICS_MAP = {"S": "success", "F": "failure"}
def load_statuses(client, entity_id: str) -> list:
return client.crm.status.list(
filter={"ENTITY_ID": entity_id},
order={"SORT": "ASC"},
).response.result
def get_semantics(item: dict) -> str:
"""Возвращает семантику стадии для любого объекта CRM.
EXTRA есть только у лидов, сделок и предложений, поэтому для остальных
объектов переводим краткое значение SEMANTICS в текстовое.
"""
extra = (item.get("EXTRA") or {}).get("SEMANTICS")
if extra:
return extra
return SEMANTICS_MAP.get(item.get("SEMANTICS"), "process")
def group_statuses_by_semantics(statuses: list) -> dict:
groups = {"success": [], "process": [], "failure": []}
for item in statuses:
name = item.get("NAME") or item.get("STATUS_ID")
groups[get_semantics(item)].append(name)
return groups
def build_table_rows(groups: dict) -> list:
max_len = max(len(groups["success"]), len(groups["process"]), len(groups["failure"]))
success = groups["success"] + [""] * (max_len - len(groups["success"]))
process = groups["process"] + [""] * (max_len - len(groups["process"]))
failure = groups["failure"] + [""] * (max_len - len(groups["failure"]))
return [[success[i], process[i], failure[i]] for i in range(max_len)]
client = Client(
BitrixWebhook(
domain="your-domain.bitrix24.com",
webhook_token="user_id/webhook_key",
)
)
# У лидов семантика в EXTRA, у смарт-процесса — в SEMANTICS
entities = [
{"title": "Стадии лидов", "entity_id": "STATUS"},
{"title": "Стадии смарт-процесса", "entity_id": "DYNAMIC_177_STAGE_7"},
]
for entity in entities:
try:
statuses = load_statuses(client, entity["entity_id"])
except BitrixAPIError as error:
print(f"Ошибка загрузки: {error}")
continue
if not statuses:
print(f"Нет стадий для {entity['title']}")
continue
print(entity["title"])
print("Успех\tВ работе\tНеуспех")
for row in build_table_rows(group_statuses_by_semantics(statuses)):
print("\t".join(row))