Lumi.CRM ← на сайт

Lumi.CRM — руководство по интеграции

MAX — основной мессенджер для переписки с кандидатами в Lumi.CRM. Личный MAX доступен при включении для компании. Дополнительные каналы — Telegram и WhatsApp.

Личный MAX использует неофициальный протокол через мост PyMax. Он поддерживает текстовые диалоги и поиск по телефону с ограничениями MAX; вложения открывают в MAX. Уведомления через MAX-бота подключаются отдельно по ссылке и кнопке «Начать».

Порядок карточек не расширяет публичный API. Отправка по API-ключу допускает только провайдеров telegram, hh, wazzup; личный max_userbot для неё закрыт. Приоритет авто-приветствий и сохранённый канал уведомлений не меняются.

Документ для внешних разработчиков: как подключить свою систему к Lumi.

Всё, что здесь описано, проверено по коду: разделы 3-4 и 7-9 — на 13.09.2026, раздел 5 (переписка) — на 21.09.2026, разделы 5.7 (Авито) и 6 (карточка кандидата) — на 23.09.2026. Если чего-то нет в этом документе — скорее всего, этого нет и в продукте.


1. Что можно уже сегодня

Задача Способ Направление
Узнавать о событиях в Lumi (новый кандидат, смена статуса, найм, табель…) Вебхуки Lumi → вы
Присылать в Lumi заявки и анкеты из своей формы Приём заявок по API-ключу вы → Lumi
Читать переписку с кандидатами и отвечать в неё API переписки по API-ключу (§5) оба
Вести чаты Авито (читать, отвечать, отмечать прочитанным) API Авито-чатов по API-ключу (§5.7) оба
Читать и дополнять карточку кандидата, двигать по воронке API карточки по API-ключу (§6) оба
Поставить форму отклика на свой сайт Встраиваемый виджет вы → Lumi

2. Чего пока нельзя

Полноценного REST API для чтения и изменения данных нет. По API-ключу доступны четыре области: приём заявок (§4), переписка с кандидатами (§5), чаты Авито (§5.7) и карточка конкретного кандидата (§6). Списков нет: получить перечень всех кандидатов, вакансий или сотрудников из внешней системы нельзя — карточка открывается по известному candidate_id, который вы получаете из диалога или события. Вакансии и сотрудники под ключ не отдаются вовсе.

Вебхук не заменяет опрос. События о новых сообщениях теперь есть (§3.2), но доставка — не гарантия: если ваш приёмник был недоступен дольше пары попыток, событие потеряется. Рабочая схема — вебхук для скорости реакции плюс опрос updated_since (§5.2, §5.7) раз в 15–30 секунд как страховка. Не стройте логику на одних вебхуках.

Пишем об этом прямо, чтобы вы не закладывали в план то, чего нет. Если нужен доступ к данным — напишите нам, какие сущности и операции требуются: это делается, но по запросу, а не «уже есть».


3. Вебхуки: получать события

Самый полный механизм интеграции. Lumi сама вызывает ваш HTTPS-адрес, когда происходит событие.

3.1. Подписка

Подписка создаётся в Lumi (интерфейс настроек или POST /api/v1/webhooks из авторизованной сессии). Указываются адрес и список интересующих событий. В ответ выдаётся секрет — он нужен для проверки подписи и больше нигде не показывается.

3.2. Какие события бывают

Отправляются четыре:

candidate.hired          кандидата приняли на работу
employee.created         заведён сотрудник
message.received         пришло входящее сообщение от кандидата
candidate.stage_changed  кандидата перевели на другой этап воронки

Подписка на что-либо ещё отбивается ошибкой 400 — намеренно. Остальной набор (candidate.created, vacancy.closed, timesheet.approved и другие) задуман, но пока не подключён; принять на него подписку молча значило бы дать вам адрес, на который никогда ничего не придёт. Нужно конкретное событие из неподключённых — напишите, добавим адресно.

message.received — data события:

{
  "thread_id": "8f3c…",
  "channel": "avito",
  "provider": "avito",
  "candidate_id": "1a2b…",
  "message_id": "77c1…",
  "sent_at": "2026-09-23T09:14:02+00:00"
}

channel: avito | telegram | whatsapp | hh. Для Авито thread_id — это id чата из §5.7, для остальных — id диалога из §5.2.

Текста сообщения в событии нет — и не будет. Адрес доставки задаёте вы, и однажды это окажется адрес с опечаткой или тестовый туннель; содержимое переписки с человеком туда уходить не должно. Текст забирается отдельным запросом по ключу, который мы выдали и можем отозвать.

События по личным Telegram и WhatsApp сотрудников приходят только если диалог привязан к карточке кандидата, то есть является рабочим (§5.1). Чаты Авито приходят все: их ведёт кабинет компании.

candidate.stage_changed — data события:

{
  "candidate_id": "1a2b…",
  "application_id": "3c4d…",
  "vacancy_id": "5e6f…",
  "stage_from_id": "9a8b…",
  "stage_to_id": "7c6d…",
  "stage_to": "Направлен",
  "status": "open",
  "changed_by": "2b3c…"
}

Этап в Lumi принадлежит отклику, а не кандидату: у человека может быть три вакансии в трёх разных местах воронки. Поэтому в событии всегда есть application_id — именно его и надо использовать, если вы отвечаете переводом (§6.3).

3.3. Как выглядит доставка

POST на ваш адрес, тело — JSON события, заголовки:

Заголовок Значение
X-Lumi-Event тип события, например candidate.hired
X-Lumi-Signature sha256=<hex> — HMAC-SHA256 от тела запроса, ключ — ваш секрет

3.4. Проверка подписи — обязательна

Без проверки подписи ваш адрес примет что угодно от кого угодно.

Python:

import hashlib
import hmac

def is_from_lumi(secret: str, raw_body: bytes, header: str) -> bool:
    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    received = (header or "").removeprefix("sha256=")
    # compare_digest, а не ==: обычное сравнение строк утекает время и позволяет
    # подобрать подпись побайтово.
    return hmac.compare_digest(expected, received)

Node.js:

const crypto = require('crypto')

function isFromLumi(secret, rawBody, header) {
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex')
  const received = (header || '').replace(/^sha256=/, '')
  const a = Buffer.from(expected), b = Buffer.from(received)
  return a.length === b.length && crypto.timingSafeEqual(a, b)
}

⚠️ Подпись считается от сырого тела. Если фреймворк уже разобрал JSON и вы сериализуете его обратно — порядок ключей и пробелы изменятся, и подпись не сойдётся. Берите тело до разбора.

3.5. Что важно знать про доставку

3.6. Требования к вашему адресу


4. Приём заявок: присылать данные в Lumi

Для сценария «во внешней форме заполнили анкету — кандидат появился в Lumi».

4.1. Аутентификация

Заголовок X-Api-Key: <ключ тенанта>. Ключ создаётся в настройках Lumi и виден целиком один раз при создании — сохраните сразу.

Ключ принадлежит компании (тенанту) и даёт доступ к её данным. Держите его на сервере: в браузере, мобильном приложении или публичном репозитории ему не место.

4.2. Эндпоинт

POST https://api.lumicrm.ru/api/v1/integrations/form-ingest
X-Api-Key: <ключ>
Content-Type: application/json

Основные поля тела (все строковые, «сырые» — нормализацию делаем мы):

Поле Обязательно Смысл
submitted_at да отметка времени заполнения формы
candidate_name нет ФИО кандидата
candidate_contact нет телефон или другой контакт
citizenship, age нет гражданство, возраст
region, position, facility нет регион, должность, объект — по ним подбирается вакансия
referrer, referrer_contact нет кто привёл кандидата
ready_date, comment нет когда готов выйти, комментарий

4.3. Повторная отправка безопасна

Идентификатор записи вычисляется из submitted_at и телефона кандидата. Поэтому повтор той же заявки (например, ретрай при обрыве связи) не создаст дубль — но только если submitted_at тот же. Не подставляйте туда текущее время при повторе.

4.4. Справочники

Чтобы присылать значения, которые Lumi узнает:

4.5. Про ограничение частоты

У ключа есть поле «запросов в минуту», и оно сохраняется при создании, но сейчас не применяется: запросы не отбиваются по этому лимиту. Не стройте на нём расчёт — и не считайте, что можно слать сколько угодно: ограничение появится, о чём предупредим заранее. Разумный ориентир — не больше нескольких запросов в секунду.


5. Переписка с кандидатами: читать и отвечать

Для сценария «внешний помощник разбирает входящие сообщения и отвечает».

5.1. Что доступно и что закрыто

Доступна рабочая переписка с кандидатами — диалоги, привязанные к карточке кандидата. Это та же переписка, которую видит вся команда внутри Lumi.

Личные диалоги сотрудников не отдаются никогда. Если рекрутёр подключил свой Telegram или свой WhatsApp, в его канале рабочее лежит вперемешку с личным — такие диалоги не появятся в выдаче и писать в них по ключу нельзя. Это не настройка и не права: ограничение жёсткое.

Нужны два отдельных права у ключа (выдаются в настройках Lumi):

Право Что даёт
messaging_read читать диалоги и сообщения
messaging_write отвечать и отмечать прочитанным (включает чтение)

Права на приём заявок (read / write) переписку не открывают — это намеренно: ключ для формы на сайте и ключ для помощника обычно у разных людей.

5.2. Список диалогов

GET https://api.lumicrm.ru/api/v1/public/messaging/threads?updated_since=2026-09-21T08:00:00Z&limit=50
X-Api-Key: <ключ>

updated_since — по нему забирают только новое, не перечитывая всё. Ответ:

{
  "items": [{
    "id": "8f3c…",
    "candidate_id": "1a2b…",
    "candidate_name": "Иванов Пётр",
    "channel": "telegram",
    "provider": "telegram",
    "contact_name": "Пётр",
    "contact_phone": "+79990000000",
    "last_message_preview": "Да, мне актуально",
    "last_message_at": "2026-09-21T09:14:02+00:00",
    "unread_count": 2,
    "can_reply": true
  }],
  "total": 1
}

can_reply: false означает, что диалог идёт через личный аккаунт сотрудника — читать его вы не сможете вовсе, а если он попал в выдачу как рабочий, то ответ по ключу в него закрыт. Не пытайтесь отправлять: вернётся 403.

5.3. Сообщения диалога

GET /api/v1/public/messaging/threads/<id>/messages?limit=100&offset=0

Порядок — от старых к новым, как в интерфейсе. direction: in — написал кандидат, out — написали вы или сотрудник. Поле author различает эти два случая: candidate, employee (живой сотрудник) или bot (вы по ключу) — см. §5.7.1, там же объяснено, почему без него бот путает себя с рекрутёром.

5.4. Ответить

POST /api/v1/public/messaging/threads/<id>/messages
X-Api-Key: <ключ>
Content-Type: application/json

{"content": "Здравствуйте! Подскажите, вам ещё актуально?"}

Сообщение уходит от имени компании, тем же каналом, которым идёт диалог, и появляется в интерфейсе Lumi у рекрутёра — он видит переписку целиком, включая то, что написал ваш помощник. В журнале действий отмечается сотрудник, создавший ключ.

Ограничения: до 4000 символов; в WhatsApp нельзя писать первым — только в ответ на сообщение кандидата (иначе номер блокируют, это правило площадки, а не наше).

5.5. Отметить прочитанным

POST /api/v1/public/messaging/threads/<id>/read

Обнуляет счётчик непрочитанных. Вызывайте после того, как ваш помощник разобрал диалог, — иначе рекрутёр видит непрочитанные, которых уже нет.

5.6. Коды ответов

Код Что значит
401 нет заголовка X-Api-Key, ключ битый, отозван или истёк
403 у ключа нет нужного права; либо попытка писать в личный канал сотрудника
404 диалога нет, он чужой или это личная переписка — снаружи это неразличимо намеренно

5.7. Чаты Авито

Авито живёт отдельно от Telegram/WhatsApp: у него своя пара ручек и свой thread_id. Права — те же messaging_read / messaging_write: для вас это один и тот же разговор с одним и тем же человеком, второй ключ заводить не нужно.

Отдаются все чаты кабинетов компании, включая «чат-онли» — диалоги без карточки кандидата (человек написал по объявлению, но не откликался). У таких candidate_id: null, а имя собеседника — в interlocutor_name.

GET https://api.lumicrm.ru/api/v1/public/messaging/avito/chats?updated_since=2026-09-23T08:00:00Z&limit=50
X-Api-Key: <ключ>

Ещё фильтры: candidate_id и awaiting_only=true (только те, где ждут вашего ответа). Ответ:

{
  "items": [{
    "id": "8f3c…",
    "avito_chat_id": "u2i-abc…",
    "candidate_id": "1a2b…",
    "candidate_name": "Иванов Пётр",
    "interlocutor_name": "Пётр",
    "vacancy_id": "5e6f…",
    "vacancy_title": "Грузчик на склад",
    "facility_name": "Склад №3, Подольск",
    "avito_item_id": "4358129901",
    "avito_account_id": "9b1c…",
    "avito_account_label": "Кабинет СПб",
    "last_message_preview": "Да, мне актуально",
    "last_message_at": "2026-09-23T09:14:02+00:00",
    "unread_count": 0,
    "awaiting_reply": true,
    "awaiting_since": "2026-09-23T09:14:02+00:00",
    "last_read_at": null
  }],
  "total": 1
}

⚠️ unread_count в Авито не показатель — он почти всегда 0. Счётчик достался от ранней версии и никогда не считался как следует. Ориентируйтесь на awaiting_reply (последним писал кандидат, ответа не было) и awaiting_since — это ровно то, что видит рекрутёр в интерфейсе.

vacancy_title — вакансия, к которой привязан чат, facility_name — объект (площадка) из карточки кандидата, avito_account_label — кабинет Авито, который ведёт диалог (у компании их может быть несколько).

GET  /api/v1/public/messaging/avito/chats/<id>/messages?limit=100&offset=0
POST /api/v1/public/messaging/avito/chats/<id>/messages   {"content": "…"}
POST /api/v1/public/messaging/avito/chats/<id>/read

Отправка возвращает {"id": "…", "sent_at": "…"} — идентификатор созданного сообщения. «Прочитано» обнуляет счётчик и ставит last_read_at.

5.7.1. Как отличить свой ответ от ответа рекрутёра

У каждого сообщения есть поле author:

candidate   написал кандидат
employee    написал живой сотрудник из интерфейса Lumi
bot         написали вы по API-ключу

Это важнее, чем кажется. Ответ по ключу уходит от имени сотрудника, создавшего ключ, — без поля author ваша собственная реплика выглядела бы как «коллега уже ответил». То же поле есть и в сообщениях Telegram/WhatsApp (§5.3).


6. Карточка кандидата

Для сценария «в разговоре выяснили возраст, гражданство и живой телефон — запишите их в Lumi, а не спрашивайте второй раз голосом рекрутёра».

Нужны отдельные права: candidates_read (чтение) и candidates_write (правка, этап, комментарий; включает чтение). Права на переписку карточку не открывают и наоборот — это намеренно: доверить помощнику диалог не значит доверить ему анкеты людей.

6.1. Прочитать карточку

GET /api/v1/public/candidates/<candidate_id>
X-Api-Key: <ключ>
{
  "id": "1a2b…",
  "first_name": "Пётр", "last_name": "Иванов", "middle_name": null,
  "phone": "+79990000000",
  "additional_phones": [],
  "email": null,
  "source": "avito",
  "status": "active",
  "location": "Подольск",
  "citizenship": "РФ",
  "date_of_birth": "1990-06-01",
  "age": 36,
  "facility_id": "7f8a…",
  "facility_name": "Склад №3, Подольск",
  "notes": "Готов выйти с понедельника",
  "applications": [{
    "application_id": "3c4d…",
    "vacancy_id": "5e6f…",
    "vacancy_title": "Грузчик на склад",
    "stage": "Отклик",
    "status": "open"
  }],
  "created_at": "2026-09-20T07:00:00+00:00",
  "updated_at": "2026-09-23T09:20:11+00:00"
}

Отдаются только поля, нужные для работы с диалогом. Паспорт, медкнижка, документы, зарплатные ожидания и вложения по ключу не отдаются — ни под каким правом.

age считается от даты рождения; хранится именно дата, поэтому её и присылайте при обновлении.

6.2. Дополнить карточку

PATCH /api/v1/public/candidates/<candidate_id>
Content-Type: application/json

{"phone": "+79991112233", "citizenship": "РФ", "location": "Подольск",
 "date_of_birth": "1990-06-01", "notes": "Готов выйти с понедельника"}

Меняются только эти пять полей — переназначить ответственного менеджера, сменить статус или закрыть отказ по ключу нельзя. Присылайте лишь то, что реально узнали: отсутствующее поле не трогается, а null в поле его очистит.

Ответ — та же карточка, уже обновлённая. Изменения попадают в историю карточки, автором отмечается сотрудник, создавший ключ.

6.3. Перевести на этап воронки

POST /api/v1/public/candidates/<candidate_id>/stage

{"stage_name": "Направлен", "application_id": "3c4d…"}

Передавайте application_id. Этап принадлежит отклику: если у кандидата несколько вакансий, а вы его не указали, переедет последний тронутый отклик — не обязательно тот, о котором шёл разговор. Идентификаторы всех откликов есть в карточке (§6.1) и в событии смены этапа (§3.2).

stage_name — имя этапа ровно как в воронке компании («Отклик», «Направлен», «СБ»). Возможные ответы:

Код Что значит
404 у компании нет этапа с таким именем
409 у кандидата нет ни одного отклика — двигать нечего, сначала нужна вакансия
422 этап требует заполненных полей (например, «Направлен» — должность, компанию, объект). Это защита от неполных карточек, а не сбой: заполните недостающее через §6.2 и повторите

6.4. Оставить комментарий

POST /api/v1/public/candidates/<candidate_id>/comment

{"text": "Просил перезвонить после 18:00, работает до 17:30"}

Запись появляется в ленте активности карточки — там же, где «этап изменён» и «отклик», в общей хронологии. Это не поле notes: комментарии накапливаются, а notes перезаписывается. Ответ — 201 и {"id", "created_at"}.


7. Виджет формы на вашем сайте

Если нужна просто форма отклика на сайте, а не своя интеграция:

<script src="https://app.lumicrm.ru/embed/form.js" data-form-id="<id формы>" defer></script>

Форма создаётся в Lumi, идентификатор берётся там же. Отклики приходят прямо в воронку — писать код не нужно.


8. Частые вопросы

Событие не приходит. Посмотрите историю доставок в Lumi: если попытка была и получила ошибку — проблема на вашей стороне (адрес, сертификат, таймаут). Если попытки нет вовсе — подписка не включает этот тип события.

Подпись не сходится. В 9 случаях из 10 подпись считают от повторно сериализованного JSON. Считайте от сырого тела.

Можно ли получать события только по одному клиенту? Подписка живёт внутри компании и присылает события только её данных. Разделения по отдельным воронкам или объектам сейчас нет.

Что с историей? Вебхуки присылают только то, что произошло после подписки. Выгрузки прошлого через них не получить.


9. Куда писать

Вопросы по интеграции и запросы на недостающие эндпоинты — в поддержку Lumi. Опишите сценарий целиком («хотим по найму сотрудника заводить его в 1С»), а не только техническое пожелание: часть задач решается уже существующими событиями.

Вопросы по интеграции — в поддержку Lumi.CRM. Опишите сценарий целиком: часть задач решается уже готовыми событиями, и тогда писать код не придётся.