Как создать дело CRM из входящего письма
Scope:
Кто может выполнять методы: чтобы пройти сценарий целиком, нужно самое строгое из перечисленных прав — доступ к почтовому ящику, где находится письмо, и доступ к CRM
- mail.mailbox.list — любой пользователь
- mail.message.list — любой пользователь
- mail.message.createcrmactivity — пользователь с доступом к почтовому ящику, где находится письмо, и доступом к CRM
- mail.message.get — пользователь с доступом к почтовому ящику, где находится письмо
Выберите инструмент для разработки с AI-агентом:
- используйте Битрикс24 Вайбкод, чтобы создать приложение для Битрикс24 по описанию задачи без знания языков программирования. Агент напишет код и разместит приложение на сервере без ручной настройки хостинга
- используйте MCP-сервер, чтобы разрабатывать интеграцию через REST API в своем проекте. Агент будет обращаться к официальной REST-документации
Входящее письмо можно превратить в дело CRM. Для этого нужно найти письмо в доступном почтовом ящике и передать его идентификатор в метод создания дела.
Метод mail.message.createcrmactivity не принимает идентификатор лида, сделки, контакта или компании. Он создает дело CRM из письма, а связь с объектом CRM определяется по данным письма и настройкам CRM.
Сценарий состоит из четырех шагов.
- Получить почтовые ящики методом mail.mailbox.list
- Найти входящее письмо методом mail.message.list
- Создать дело CRM методом mail.message.createcrmactivity
- Проверить связь письма методом mail.message.get
В результате у письма появится привязка в поле bindings, а в CRM будет создано дело из письма.
Что нужно до начала
Перед запуском сценария проверьте, что:
- входящий вебхук создан со scope
mail - у пользователя вебхука есть доступ к почтовому ящику с входящим письмом
- CRM включена и настроена, у пользователя вебхука есть доступ к CRM
- письмо доступно текущему пользователю и не удалено
- путь вебхука хранится в переменной окружения и содержит сегмент
/rest/api/
Методы почты относятся к REST 3.0. Особенности вызова методов и формат JSON-запроса описаны в обзоре REST 3.0. Для серверных JS-примеров используйте $b24.actions.v3, для Python укажите prefer_version=3. PHP SDK не поддерживает вызовы через /rest/api/, поэтому PHP-пример отправляет прямой HTTP-запрос.
Дальше в примерах используются письмо с темой «Договор» и период с 1 по 31 августа 2026 года. В вашем Битрикс24 значения будут другими: выберите поисковую строку и период так, чтобы метод mail.message.list нашел нужное входящее письмо.
1. Получим почтовые ящики
Метод mail.mailbox.list возвращает почтовые ящики текущего пользователя.
Вызовем метод с параметром:
pagination— настройки постраничной навигации. В примере запрашиваем первую страницу и ограничиваем ответ 20 ящиками
Как использовать примеры в документации
import { B24Hook, Text } from '@bitrix24/b24jssdk'
const $b24 = B24Hook.fromWebhookUrl(process.env.B24_HOOK)
// B24_HOOK = 'https://your-domain.bitrix24.ru/rest/api/USER_ID/TOKEN/'
async function callMethod(method, params) {
const response = await $b24.actions.v3.call.make({
method,
params,
requestId: Text.getUuidRfc4122()
})
if (!response.isSuccess) {
throw new Error(response.getErrorMessages().join('; '))
}
return response.getData().result
}
const mailboxesResult = await callMethod('mail.mailbox.list', {
pagination: {
page: 1,
limit: 20,
offset: 0
}
})
const mailbox = mailboxesResult.items[0]
if (!mailbox) {
throw new Error('Нет доступных почтовых ящиков')
}
const mailboxId = mailbox.id
<?php
$webhook = getenv('B24_HOOK');
// B24_HOOK = 'https://your-domain.bitrix24.ru/rest/api/USER_ID/TOKEN/'
function callMethod(string $webhook, string $method, array $params)
{
$ch = curl_init($webhook . $method);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Accept: application/json'],
CURLOPT_POSTFIELDS => json_encode($params, JSON_UNESCAPED_UNICODE),
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
if ($response === false)
{
throw new RuntimeException(curl_error($ch));
}
$data = json_decode($response, true);
if (isset($data['error']))
{
throw new RuntimeException($data['error']['message']);
}
return $data['result'];
}
$mailboxesResult = callMethod($webhook, 'mail.mailbox.list', [
'pagination' => [
'page' => 1,
'limit' => 20,
'offset' => 0,
],
]);
$mailbox = $mailboxesResult['items'][0] ?? null;
if (!$mailbox)
{
throw new RuntimeException('Нет доступных почтовых ящиков');
}
$mailboxId = $mailbox['id'];
import os
from b24pysdk import BitrixWebhook, Client
client = Client(
BitrixWebhook(
domain="your-domain.bitrix24.ru",
webhook_token=os.environ["B24_HOOK_TOKEN"],
),
prefer_version=3,
)
# B24_HOOK_TOKEN = 'user_id/webhook_key'
mailboxes_result = client.mail.mailbox.list(
pagination={
"page": 1,
"limit": 20,
"offset": 0,
},
).response.result
if not mailboxes_result["items"]:
raise RuntimeError("Нет доступных почтовых ящиков")
mailbox_id = mailboxes_result["items"][0]["id"]
В результате получили список почтовых ящиков. Для следующего шага сохраните id ящика, в котором нужно найти входящее письмо.
{
"result": {
"items": [
{
"id": 1,
"name": "Рабочая почта",
"email": "user@example.com",
"senderName": "Иван Петров"
}
]
}
}
2. Найдем входящее письмо
Метод mail.message.list возвращает письма по условиям. В примере ищем письмо в выбранном ящике по теме и периоду.
Используем метод с параметрами:
mailboxId— идентификатор почтового ящика из шага 1searchQuery— строка поиска по письмам. В примере ищем письма по словудоговорdateFromиdateTo— границы периода, в котором нужно найти письмоpagination— настройки постраничной навигации. В примере запрашиваем первую страницу и ограничиваем ответ 20 письмами
const messagesResult = await callMethod('mail.message.list', {
mailboxId,
searchQuery: 'договор',
dateFrom: '2026-08-01T00:00:00+03:00',
dateTo: '2026-08-31T23:59:59+03:00',
pagination: {
page: 1,
limit: 20,
offset: 0
}
})
const message = messagesResult.items[0]
if (!message) {
throw new Error('Письмо не найдено')
}
const messageId = message.id
$messagesResult = callMethod($webhook, 'mail.message.list', [
'mailboxId' => $mailboxId,
'searchQuery' => 'договор',
'dateFrom' => '2026-08-01T00:00:00+03:00',
'dateTo' => '2026-08-31T23:59:59+03:00',
'pagination' => [
'page' => 1,
'limit' => 20,
'offset' => 0,
],
]);
$message = $messagesResult['items'][0] ?? null;
if (!$message)
{
throw new RuntimeException('Письмо не найдено');
}
$messageId = $message['id'];
messages_result = client.mail.message.list(
mailbox_id=mailbox_id,
search_query="договор",
date_from="2026-08-01T00:00:00+03:00",
date_to="2026-08-31T23:59:59+03:00",
pagination={
"page": 1,
"limit": 20,
"offset": 0,
},
).response.result
if not messages_result["items"]:
raise RuntimeError("Письмо не найдено")
message_id = messages_result["items"][0]["id"]
В результате получили список писем. Для следующего шага сохраните id нужного письма в переменной messageId.
{
"result": {
"items": [
{
"id": 15,
"mailboxId": 1,
"mailboxEmail": "user@example.com",
"subject": "Договор",
"from": "client@example.com",
"to": "user@example.com",
"date": "2026-08-15T10:00:00+03:00",
"bindings": []
}
]
}
}
3. Создадим дело CRM
Метод mail.message.createcrmactivity создает дело CRM из письма.
Используем метод с параметром:
messageId— идентификатор письма, который сохранили из ответа mail.message.list на шаге 2
const createResult = await callMethod('mail.message.createcrmactivity', {
messageId
})
console.log(createResult)
$createResult = callMethod($webhook, 'mail.message.createcrmactivity', [
'messageId' => $messageId,
]);
print_r($createResult);
create_result = client.mail.message.createcrmactivity(
message_id=message_id,
).response.result
print(create_result)
Успешный ответ содержит объект с result: true.
{
"result": {
"result": true
}
}
4. Проверим связь письма
Метод mail.message.get возвращает письмо по идентификатору.
Используем метод с параметрами:
id— идентификатор письмаmessageId, который сохранили из ответа mail.message.list на шаге 2select— список полей, которые нужно получить. Запросите полеbindings, чтобы увидеть созданную связь
const messageResult = await callMethod('mail.message.get', {
id: messageId,
select: [
'id',
'subject',
'from',
'to',
'bindings',
'url'
]
})
console.log(messageResult.item.bindings)
$messageResult = callMethod($webhook, 'mail.message.get', [
'id' => $messageId,
'select' => [
'id',
'subject',
'from',
'to',
'bindings',
'url',
],
]);
print_r($messageResult['item']['bindings']);
message_result = client.mail.message.get(
bitrix_id=message_id,
select=[
"id",
"subject",
"from",
"to",
"bindings",
"url",
],
).response.result
print(message_result["item"]["bindings"])
Связь с CRM отображается в массиве bindings. Ответ сокращен до полей, которые нужны для проверки.
{
"result": {
"item": {
"id": 15,
"subject": "Договор",
"from": "client@example.com",
"to": "user@example.com",
"url": "/mail/message/15",
"bindings": [
{
"type": "crm",
"entityTypeId": 3,
"entityId": 125
}
]
}
}
}
Проверим результат
Откройте письмо в почте Битрикс24. У письма должна появиться связь с CRM.
Через REST сценарий выполнен, если метод mail.message.get возвращает непустой массив bindings и в нем есть объект с type: "crm".
Ошибки и диагностика
Если метод вернул ошибку, проверьте данные запроса.
|
Код |
Причина и действие |
|
|
Вебхук или приложение не имеет scope |
|
|
Пользователь не имеет доступа к почтовому ящику или письму. Проверьте пользователя вебхука |
|
|
В |
|
|
Письмо не найдено. Проверьте |
|
|
Условия поиска письма не прошли проверку. Проверьте формат |
Если метод mail.message.createcrmactivity вернул объект с result: true, но bindings пустой, проверьте настройки CRM и данные письма:
- у пользователя вебхука есть доступ к CRM
- CRM-трекер или обработка писем в CRM настроены для адреса из письма
- адрес отправителя или получателя письма совпадает с email в лиде, контакте или компании
- письмо не удалено и доступно в активном подключении почтового ящика
Метод не принимает целевой объект CRM вручную, поэтому связь зависит от обработки письма в CRM.
Что важно учитывать
Учитывайте ограничения сценария:
mail.message.createcrmactivityсоздает дело CRM из существующего письма и не отправляет новое письмо- параметр
messageIdметодаmail.message.createcrmactivityберется из ответа mail.message.list или mail.message.get - целевой объект CRM нельзя передать параметром: у
mail.message.createcrmactivityнет полей для идентификатора лида, сделки, контакта или компании - повторный вызов
mail.message.createcrmactivityдля того же письма может вернуть ошибку или не изменить уже созданную связь, проверяйтеbindingsперед повтором - связь можно удалить методом mail.message.removecrmactivity
Пример кода
Код объединяет все шаги: получает почтовый ящик, ищет письмо, создает дело CRM и проверяет bindings. Замените поисковую строку и период на свои значения.
import { B24Hook, Text } from '@bitrix24/b24jssdk'
const $b24 = B24Hook.fromWebhookUrl(process.env.B24_HOOK)
async function callMethod(method, params) {
const response = await $b24.actions.v3.call.make({
method,
params,
requestId: Text.getUuidRfc4122()
})
if (!response.isSuccess) {
throw new Error(response.getErrorMessages().join('; '))
}
return response.getData().result
}
const mailboxes = await callMethod('mail.mailbox.list', {
pagination: { page: 1, limit: 20, offset: 0 }
})
const mailbox = mailboxes.items[0]
if (!mailbox) {
throw new Error('Нет доступных почтовых ящиков')
}
const mailboxId = mailbox.id
const messages = await callMethod('mail.message.list', {
mailboxId,
searchQuery: 'договор',
dateFrom: '2026-08-01T00:00:00+03:00',
dateTo: '2026-08-31T23:59:59+03:00',
pagination: { page: 1, limit: 20, offset: 0 }
})
const sourceMessage = messages.items[0]
if (!sourceMessage) {
throw new Error('Письмо не найдено')
}
const messageId = sourceMessage.id
await callMethod('mail.message.createcrmactivity', { messageId })
const message = await callMethod('mail.message.get', {
id: messageId,
select: ['id', 'subject', 'from', 'to', 'bindings', 'url']
})
console.log(message.item.bindings)
<?php
$webhook = getenv('B24_HOOK');
function callMethod(string $webhook, string $method, array $params)
{
$ch = curl_init($webhook . $method);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Accept: application/json'],
CURLOPT_POSTFIELDS => json_encode($params, JSON_UNESCAPED_UNICODE),
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
if ($response === false)
{
throw new RuntimeException(curl_error($ch));
}
$data = json_decode($response, true);
if (isset($data['error']))
{
throw new RuntimeException($data['error']['message']);
}
return $data['result'];
}
$mailboxes = callMethod($webhook, 'mail.mailbox.list', [
'pagination' => ['page' => 1, 'limit' => 20, 'offset' => 0],
]);
$mailbox = $mailboxes['items'][0] ?? null;
if (!$mailbox)
{
throw new RuntimeException('Нет доступных почтовых ящиков');
}
$mailboxId = $mailbox['id'];
$messages = callMethod($webhook, 'mail.message.list', [
'mailboxId' => $mailboxId,
'searchQuery' => 'договор',
'dateFrom' => '2026-08-01T00:00:00+03:00',
'dateTo' => '2026-08-31T23:59:59+03:00',
'pagination' => ['page' => 1, 'limit' => 20, 'offset' => 0],
]);
$sourceMessage = $messages['items'][0] ?? null;
if (!$sourceMessage)
{
throw new RuntimeException('Письмо не найдено');
}
$messageId = $sourceMessage['id'];
callMethod($webhook, 'mail.message.createcrmactivity', [
'messageId' => $messageId,
]);
$message = callMethod($webhook, 'mail.message.get', [
'id' => $messageId,
'select' => ['id', 'subject', 'from', 'to', 'bindings', 'url'],
]);
print_r($message['item']['bindings']);
import os
from b24pysdk import BitrixWebhook, Client
client = Client(
BitrixWebhook(
domain="your-domain.bitrix24.ru",
webhook_token=os.environ["B24_HOOK_TOKEN"],
),
prefer_version=3,
)
mailboxes = client.mail.mailbox.list(
pagination={"page": 1, "limit": 20, "offset": 0},
).response.result
if not mailboxes["items"]:
raise RuntimeError("Нет доступных почтовых ящиков")
mailbox_id = mailboxes["items"][0]["id"]
messages = client.mail.message.list(
mailbox_id=mailbox_id,
search_query="договор",
date_from="2026-08-01T00:00:00+03:00",
date_to="2026-08-31T23:59:59+03:00",
pagination={"page": 1, "limit": 20, "offset": 0},
).response.result
if not messages["items"]:
raise RuntimeError("Письмо не найдено")
message_id = messages["items"][0]["id"]
create_result = client.mail.message.createcrmactivity(
message_id=message_id,
).response.result
message = client.mail.message.get(
bitrix_id=message_id,
select=["id", "subject", "from", "to", "bindings", "url"],
).response.result
print(message["item"]["bindings"])