Войдите под владельцем
Используйте email и пароль установки. В production пароль показывается один раз командой prod:init.
Инструкции для оператора и руководителя, а рядом — технический контракт разработчика. Здесь объясняется куда нажать, зачем это нужно, что произойдёт и как проверить результат.
Используйте email и пароль установки. В production пароль показывается один раз командой prod:init.
«Боты и каналы» → «Подключить бота» → режим, endpoint и секреты официального API.
Входящее автоматически создаёт контакт, идентификатор канала, диалог и сообщение.
Откройте диалог, заберите его у бота, ответьте, создайте заметку, задачу или сделку.
npm run demo:seed; production её не выполняет.Ищет контакты, сообщения и сделки минимум по двум символам.
Сообщения, счётчики и CRM-изменения приходят без перезагрузки.
Светлая/тёмная тема сохраняется в браузере.
При проблеме проверьте конкретный раздел или connector.
Недоступные действия отключаются, а API повторно проверяет права.
Данные изолированы через workspace_id.

Полная текущая выборка.
Только режим HUMAN — бот остановлен.
Разговоры с новыми входящими.
Канал, бот и состояние видны вместе со списком.
Новые, непрочитанные или имя.
Снимает выбор, не читая следующий случайно.
Диалог читается, когда он открыт. Новые сообщения в уже открытом разговоре сразу считаются прочитанными. Без выбранного диалога счётчик сохраняется.
Отвечает бот. Для ответа человеком нажмите «Перехватить».
Бот не получает входящие; работает оператор.
Автоответ запрещён, но разговор ещё не активен у оператора.
deliverToBot и отправлять через BotCRM.Имя, телефон, email, город, аватар и channel identities.
Командные метки и значения от ботов.
Внутренний контекст не отправляется клиенту.
Склеивайте только подтверждённые дубли, не людей с похожим именем.
«+ Контакт» создаёт ручную карточку с контактами, тегами и JSON-переменными. Обычно карточка появляется автоматически после первого события.
CSV/JSON переносит существующую базу. Экспорт доступен руководителю, администратору и владельцу. Используйте UTF‑8 и проверьте уникальность телефонов/email.
lead_score, plan, risk_level или свой корректный ключ.lead_scoreИспользуется в API и не меняется после создания.
Показывается в интерфейсе и может меняться.
У определения есть область, тип, источник истины и возможность фильтрации. Удаление определения не стирает сохранённые значения.
Например, создайте «Горячие лиды из Telegram».
Канал = Telegram И Оценка интереса ≥ 80.
Система покажет всего, доступных и исключённых.
Сегмент живой, а список фиксируется при запуске кампании.

Нажмите настройку рядом с выбором текущей воронки.
Например: «Продажи», «Поддержка», «Онбординг».
Укажите название, цвет и при необходимости финал WON/LOST.
Он определяет колонки и расчёт конверсии.
Создайте сделку из доски или контакта, задайте название и сумму. Перетаскивание использует optimistic locking: конфликт не перезапишет чужое более свежее изменение.
Контент и ограничения зависят от транспорта.
Проверьте доступных и причины исключений.
Вставляйте {{first_name}} через подсказку; токен выделяется и объясняется.
Изображения и ряды callback/URL-кнопок проверяются по capabilities.
Отправьте реальному контакту того же канала и проверьте подстановки.
Сейчас или по времени выбранной зоны с показом текущего времени.
Выполнение можно поставить на паузу и продолжить. Отмена прекращает ожидающие отправки. Временные ошибки повторяются, постоянные попадают в failed/suppression.
Выберите «Контакт обновлён», поле «Оценка интереса», оператор «≥», значение 80. Затем тег «Горячий», стадию «Квалификация» и задачу на 15 минут.
{
"name": "Горячий лид → квалификация",
"enabled": true,
"triggerType": "contact.updated",
"conditionTree": {
"match": "all",
"conditions": [
{ "field": "attributes.lead_score", "operator": "gte", "value": 80 }
]
},
"actions": [
{ "type": "add_tag", "tag": "Горячий" },
{ "type": "move_deal", "stage": "qualification" },
{ "type": "create_task", "title": "Связаться в течение 15 минут", "dueMinutes": 15 }
],
"maxDepth": 5
}Последняя проверка успешна.
Таймаут, токен или неполная конфигурация.
Сохранён, но ещё не проверен.
Например, Telegram через BotFather.
Gateway отдаёт webhook BotCRM; Mirror оставляет transport у бота.
Туда приходят события и control.returned.
После сохранения их можно только заменить.
В Gateway зарегистрируйте его у провайдера.
Health-check покажет конкретную ошибку.

Имя, email, роль и состояние доступа.
Группы сотрудников отдельно от ролей.
Показываются полностью один раз, затем только отзываются.
TOTP и отзыв отдельных активных сессий.
Доступно OWNER/ADMIN.
Не менее 12 символов; не отправляйте в общем чате.
Оператору не нужны настройки ботов или запуск кампаний.
Пользователь входит и подтверждает TOTP шестизначным кодом.
needs_human=true.lead_score=95.Webhook канала указывает на BotCRM. Платформа проверяет, сохраняет и вызывает endpoint бота только в BOT.
Бот принимает update сам, копирует входящие в /events и отвечает через /messages/send.
/price, создаёт CRM-данные и прекращает отвечать после перехвата. Выберите язык.Получите TELEGRAM_BOT_TOKEN. Для этого примера не ставьте webhook — используется polling.
Telegram → Mirror / SDK, имя, slug и token.
Настройки → Сервисные токены. Скопируйте сразу.
TELEGRAM_BOT_TOKEN=123456:telegram-secret
BOTCRM_URL=https://crm.example.ru
BOTCRM_WORKSPACE_ID=ws_demo
BOTCRM_BOT_ID=sales_assistant
BOTCRM_SERVICE_TOKEN=botcrm-service-secretОтправьте /price, заберите диалог и отправьте ещё сообщение: бот не должен ответить.
npm install @botcrm/sdk
# Для Telegram long polling нужен Node.js 22+ (fetch уже встроен)import { BotCrmClient } from "@botcrm/sdk";
const telegramToken = required("TELEGRAM_BOT_TOKEN");
const crm = new BotCrmClient({
baseUrl: required("BOTCRM_URL"),
workspaceId: required("BOTCRM_WORKSPACE_ID"),
botId: required("BOTCRM_BOT_ID"),
serviceToken: required("BOTCRM_SERVICE_TOKEN")
});
async function telegram(method: string, body: unknown) {
const response = await fetch(
`https://api.telegram.org/bot${telegramToken}/${method}`,
{ method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) }
);
const result = await response.json();
if (!response.ok || !result.ok) throw new Error(JSON.stringify(result));
return result.result;
}
let offset = 0;
while (true) {
const updates = await telegram("getUpdates", { offset, timeout: 25, allowed_updates: ["message"] });
for (const update of updates) {
offset = update.update_id + 1;
const message = update.message;
if (!message?.text || !message.from) continue;
const accepted = await crm.incoming({
eventId: `telegram:${update.update_id}`,
channel: "telegram",
chatId: String(message.chat.id),
userId: String(message.from.id),
externalMessageId: String(message.message_id),
text: message.text,
profile: {
name: [message.from.first_name, message.from.last_name].filter(Boolean).join(" "),
},
attributes: { source: "telegram", last_command: message.text }
});
// false означает, что диалог забрал оператор или он поставлен на паузу.
if (!accepted.deliverToBot || !accepted.conversationId) continue;
const answer = message.text === "/price"
? "Тариф Pro стоит 4 900 ₽ в месяц. Напишите /manager, если нужна консультация."
: "Привет! Команды: /price и /manager";
await crm.send({
conversationId: accepted.conversationId,
text: answer,
actor: "bot",
idempotencyKey: `reply:${update.update_id}`
});
}
}
function required(name: string) {
const value = process.env[name];
if (!value) throw new Error(`Missing ${name}`);
return value;
}event_id или Idempotency-Key. Новый ключ создаст новое сообщение.| Канал | Обязательные поля | Что проверить |
|---|---|---|
| Telegram | botToken, webhookSecret | Токен от BotFather; HTTPS webhook в Gateway. |
| VK | accessToken, API 5.199, confirmation secret/code | Callback API сообщества и подтверждение сервера. |
| WhatsApp Cloud | accessToken, phoneNumberId, app secret, verify token | Webhook Meta, подписка messages, одобренные templates. |
| Avito | accessToken, accountId, outbound URL, signing secret | Официальный доступ Messenger API. |
| Generic REST | outbound URL, signing secret | Ваш endpoint принимает нормализованные события. |
Зарегистрируйте показанный BotCRM webhook у провайдера. Endpoint вашего бота получает уже проверенные события.
Ваш бот сохраняет polling/webhook, но копирует события в API и соблюдает deliverToBot.
localhost работает только на вашем ПК. Для Gateway нужен публичный HTTPS-домен; для локальной проверки используйте Mirror/polling./api/* в API, поэтому отдельный открытый порт не нужен.POST https://crm.example.ru/api/v1/events
Authorization: Bearer <service-token>
X-Service-Token: <service-token>
X-Workspace-Id: ws_demo
Idempotency-Key: telegram:482991204
Content-Type: application/jsonAuthorizationBearer service token. Заголовок X-Service-Token поддерживается для SDK и внутренних интеграций.
X-Workspace-IdОбязательная граница данных. Токен также привязан к workspace; несовпадение отклоняется.
Idempotency-KeyСтабильный ключ операции. Повтор того же запроса не создаёт вторую отправку.
Cookie sessionТолько для браузерной панели. Не копируйте пользовательские cookies в бот.
POST /api/v1/events. Gateway создаёт тот же объект автоматически после проверки канального webhook.{
"event_id": "telegram:482991204",
"schema_version": "1.0",
"occurred_at": "2026-08-23T10:15:00.000Z",
"workspace_id": "ws_demo",
"bot_id": "sales_assistant",
"channel": "telegram",
"external_chat_id": "84120931",
"external_user_id": "84120931",
"type": "message.received",
"message": {
"external_id": "502",
"text": "Хочу узнать стоимость"
},
"profile": {
"name": "Анна",
"city": "Москва",
"avatar_file_id": "telegram-file-id"
},
"attributes": {
"intent": "pricing",
"lead_score": 70
}
}{
"duplicate": false,
"contactId": "a60d55b4-...",
"conversationId": "55c3df22-...",
"deliverToBot": true
}event_idГлобально стабильный ID исходного update. При retry не меняется.
occurred_atВремя у источника в ISO 8601 UTC. Оно определяет порядок сообщений.
external_chat_idДиалог у канала; не путать с внутренним UUID conversation.
profileИмя, username, телефон, email, город и avatar. Передавайте только известные данные.
attributesПроизвольные типизированные данные. Новые ключи появляются в реестре переменных.
deliverToBotfalse означает HUMAN/PAUSED: сохраните событие, но не запускайте ответ бота.
message.received, message.sent, button.clicked, статусы доставки и control.returned. Неизвестный raw payload сохраняйте только для диагностики и с ограниченным retention.POST /api/v1/messages/send
X-Workspace-Id: ws_demo
X-Service-Token: <service-token>
Idempotency-Key: reply:telegram:502
Content-Type: application/json
{
"conversationId": "55c3df22-...",
"actor": "bot",
"text": "Тариф Pro стоит 4 900 ₽ в месяц"
}# 1. Зарезервировать загрузку
POST /api/v1/media/uploads
{
"filename": "proposal.pdf",
"mimeType": "application/pdf",
"size": 184230
}
# 2. Загрузить байты по выданному uploadUrl
PUT <uploadUrl>
Content-Type: application/pdf
<binary body>
# 3. Подтвердить загрузку
POST /api/v1/media/uploads/<uploadId>/complete
# 4. Передать uploadId при отправке
POST /api/v1/messages/send
{
"conversationId": "...",
"actor": "bot",
"text": "Отправляю предложение",
"attachmentIds": ["<uploadId>"]
}Передайте имя, MIME и размер; получите upload URL/id.
Отправьте файл по выданному URL, не кодируйте большое изображение в JSON.
POST /media/uploads/:id/complete проверит объект.
Добавьте его в message. Worker адаптирует формат канала.
Не каждый канал присылает delivered/read. UI показывает только реально поддерживаемый уровень. failed содержит безопасную причину и признак временной ошибки.
GET /api/v1/conversations/{conversationId}/control
PATCH /api/v1/conversations/{conversationId}/control
Content-Type: application/json
{
"mode": "HUMAN",
"expectedVersion": 7,
"reason": "operator_claim"
}События доставляются endpoint бота.
Отвечает оператор; бот получает deliverToBot=false.
Автоматические ответы приостановлены.
control.returned. Бот может восстановить контекст, но не должен повторять старый ответ.import { verifyBotCrmWebhook } from "@botcrm/sdk";
app.post("/botcrm-events", express.raw({ type: "application/json" }), (req, res) => {
const signature = req.header("x-botcrm-signature");
if (!verifyBotCrmWebhook(req.body, signature, process.env.BOTCRM_WEBHOOK_SECRET!)) {
return res.status(401).send("invalid signature");
}
const event = JSON.parse(req.body.toString("utf8"));
// Сначала быстро подтвердите приём, тяжёлую работу отправьте в очередь.
res.status(202).json({ accepted: true, eventId: event.event_id });
});| Заголовок | Назначение |
|---|---|
x-botcrm-event-id | Стабильный ID бизнес-события. |
x-botcrm-delivery-id | ID конкретной попытки доставки. |
x-botcrm-timestamp | Unix timestamp; отклоняйте слишком старые запросы. |
x-botcrm-signature | sha256=HMAC_SHA256(rawBody, signingSecret). |
| Ситуация | Поведение интеграции |
|---|---|
| Повтор входящего webhook | Тот же event_id; API вернёт duplicate: true. |
| Повтор отправки | Тот же Idempotency-Key; не генерировать его заново при retry. |
| HTTP 429 | Ждать Retry-After, не крутить цикл немедленно. |
| HTTP 5xx / timeout | Exponential backoff с jitter и ограничением попыток. |
| Обычный HTTP 4xx | Постоянная ошибка: исправить payload/доступ, не повторять бесконечно. |
| События пришли не по порядку | Сортировать сообщения по occurred_at и стабильному sequence/id. |
| Endpoint недоступен | После попыток запись попадает в DLQ и видна в журнале. |
| Канал | Сильные стороны | Ограничения, которые важно учесть |
|---|---|---|
| Telegram Bot API | Текст, медиа, inline-кнопки, reply, edit/delete | Лимиты на чат и массовую отправку; бот не пишет первым произвольному человеку. |
| VK Community | Сообщения сообщества, вложения, клавиатура | Нужны права сообщества, confirmation и актуальная версия API. |
| WhatsApp Cloud | Телефонная identity, статусы доставки, templates | Вне сервисного окна — только одобренный шаблон; ограничения бизнеса Meta. |
| Avito Messenger | Переписка по объявлениям | Только аккаунты с официально выданным API-доступом. |
| Generic | Любой сайт, приложение или собственный transport | Delivery/read/edit доступны только если их реализует ваш адаптер. |
message.receivedmessage.sentcontact.updatedconversation.createddeal.createddeal.stage_changedbutton.clickedconversation.inactivecampaign.deliveredcampaign.failedequalsnot_equalscontainsnot_containsgtgteltlteexistsnot_existsinset_attributeadd_tagmove_dealassign_usercreate_taskset_controlsend_messagewebhooksuppress_contactПравило принимает до 20 условий и от 1 до 10 действий. maxDepth задаётся от 1 до 10. Внешний webhook разрешается только доменам из AUTOMATION_WEBHOOK_ALLOWLIST и подписывается AUTOMATION_WEBHOOK_SECRET.
| Возможность | Владелец | Админ | Руководитель | Оператор |
|---|---|---|---|---|
| Диалоги и ответы | Да | Да | Да | Да |
| Контакты, сделки, задачи | Да | Да | Да | По назначению |
| Сегменты и просмотр аналитики | Да | Да | Да | Ограниченно |
| Запуск рассылки и автоматизации | Да | Да | Да | Нет |
| Каналы, пользователи, команды, service token | Да | Да | Нет | Нет |
| Удаление workspace / передача владения | Да | Нет | Нет | Нет |
Сервисный аккаунт не входит в интерфейс и получает только явно предусмотренный API-доступ через отзываемый токен.
/api/v1/auth/*login, me, logout, MFA, sessions.
/api/v1/eventsНормализованные входящие события.
/api/v1/messages/sendТекст, buttons, media и idempotency.
/api/v1/contacts/*upsert, поля, tags, import/export, merge.
/api/v1/conversations/*read, control, notes и tasks.
/api/v1/pipelines/*pipelines, stages, deals.
/api/v1/segments/*Фильтры, preview и сохранение.
/api/v1/campaigns/*draft, test, schedule, pause, results.
/api/v1/automations/*rules, test и runs.
/api/v1/admin/*users, teams, service tokens.
/healthLiveness/readiness и connector health.
/api-docsИнтерактивная схема текущей версии.
A/AAAA для crm.example.ru и media.crm.example.ru должны указывать на сервер до первого запуска.
git clone <your-repository-url> botcrm
cd botcrm
npm ci
npm run prod:init -- --domain crm.example.ru --storage-domain media.crm.example.ru --email owner@example.ru --owner-name "Иван" --workspace-name "Моя команда" --timezone Europe/MoscowДомены, email ACME, workspace, timezone и независимые длинные секреты. Не переносите dev-пароль.
npm run prod:check
npm run prod:confignpm run prod:up
npm run prod:ps
npm run prod:logsОткройте https://crm.example.ru, /docs, /api-docs и /api/v1/health. Затем войдите владельцем и включите MFA.
BOTCRM_DOMAINПубличный домен панели и API.
BOTCRM_STORAGE_DOMAINОтдельный HTTPS host для медиа.
BOOTSTRAP_OWNER_*Первый владелец; пароль минимум 16 случайных символов.
MASTER_ENCRYPTION_KEYШифрует credentials каналов. Потеря ключа делает их нечитаемыми.
POSTGRES/REDIS/MINIOНезависимые секреты внутренних сервисов.
TRUST_PROXY=trueКорректный HTTPS, IP и secure cookies за Caddy.
npm run prod:backupСкопируйте архив за пределы VPS и зашифруйте его отдельным ключом.
npm run prod:restore -- --from <backup-directory>Сначала проводите drill на отдельном сервере/volume, не поверх production.
Особенно migrations, новые переменные и изменения channel adapters.
База, object storage, .env.production и master encryption key.
git pull --ff-only
npm run prod:check
npm run prod:confignpm run prod:migrate
npm run prod:upВход, входящее/исходящее сообщение, realtime, upload, тест кампании и connector health.
event_id, chat/user id и workspace заполнены.deliverToBot: при HUMAN/PAUSED он false./messages/send.deliverToBot до бизнес-логики.event_id.Idempotency-Key./complete перед отправкой.AUTOMATION_WEBHOOK_ALLOWLIST.AUTOMATION_WEBHOOK_SECRET и проверяйте подпись у получателя.APP_URL, NEXT_PUBLIC_API_URL, trusted proxy и HTTPS.Подключите тестового бота, отправьте событие, перехватите разговор и верните управление. Это проверит transport, realtime, права и очередь одной короткой цепочкой.