mirror of
https://github.com/eggent-ai/eggent.git
synced 2026-05-13 15:46:00 +00:00
chore: add bundled skills bitrix24 and sendforsign
This commit is contained in:
436
bundled-skills/bitrix24/Bitrix24_Skills.md
Normal file
436
bundled-skills/bitrix24/Bitrix24_Skills.md
Normal file
@@ -0,0 +1,436 @@
|
||||
# OpenClaw Skills для Битрикс24
|
||||
|
||||
## Архитектура скиллов
|
||||
|
||||
OpenClaw подключается к порталу Битрикс24 через REST API под правами текущего пользователя. Скиллы сгруппированы по функциональным доменам и используют реальные API-методы Битрикс24.
|
||||
|
||||
---
|
||||
|
||||
## Группа 1: Коммуникации (im.*)
|
||||
|
||||
### Скилл: `bitrix24-messaging`
|
||||
|
||||
**Описание:** Чтение и отправка сообщений в личных и групповых чатах, каналах. Работа от имени пользователя.
|
||||
|
||||
**Возможности:**
|
||||
- Отправка сообщений в 1-1 чаты, групповые чаты, каналы
|
||||
- Чтение последних диалогов и истории сообщений
|
||||
- Поиск по сообщениям в чатах
|
||||
- Отправка уведомлений (системных и персональных)
|
||||
- Создание новых чатов и управление участниками
|
||||
- Отслеживание статуса «вам пишут»
|
||||
|
||||
**API-методы:**
|
||||
|
||||
| Метод | Описание | Использование |
|
||||
|-------|----------|---------------|
|
||||
| `im.message.add` | Отправка сообщения от пользователя | Основной метод отправки |
|
||||
| `im.recent.list` | Список последних диалогов | Дайджест, обзор чатов |
|
||||
| `im.recent.get` | Получить список чатов | Быстрый обзор |
|
||||
| `im.dialog.get` | Информация о чате | Контекст диалога |
|
||||
| `im.dialog.messages.search` | Поиск сообщений в чате | Поиск по переписке |
|
||||
| `im.dialog.users.list` | Участники диалога | Кто в чате |
|
||||
| `im.chat.add` | Создать новый чат | Создание рабочих чатов |
|
||||
| `im.chat.user.add` | Добавить пользователя в чат | Управление участниками |
|
||||
| `im.chat.user.delete` | Удалить из чата | Управление участниками |
|
||||
| `im.notify.system.add` | Системное уведомление | Алерты и напоминания |
|
||||
| `im.notify.personal.add` | Персональное уведомление | Личные напоминания |
|
||||
| `im.dialog.read.all` | Прочитать все чаты | Массовое прочтение |
|
||||
| `im.message.share` | Создать сущность из сообщения | Задача/событие из чата |
|
||||
| `im.dialog.writing` | Статус «вам пишут» | UX |
|
||||
|
||||
**Ограничения прав:**
|
||||
- Видит только чаты, в которых участвует пользователь
|
||||
- Не может читать чужие личные переписки
|
||||
- Каналы — только те, на которые подписан
|
||||
|
||||
**Примеры использования в сценариях:**
|
||||
- Утренний дайджест пропущенных сообщений
|
||||
- Маршрутизация запросов между отделами
|
||||
- Публикация объявлений в каналах
|
||||
- Отправка напоминаний коллегам
|
||||
|
||||
---
|
||||
|
||||
### Скилл: `bitrix24-users`
|
||||
|
||||
**Описание:** Работа со структурой компании, поиск пользователей, информация о подразделениях.
|
||||
|
||||
**Возможности:**
|
||||
- Получение информации о текущем пользователе
|
||||
- Поиск сотрудников по имени, должности, подразделению
|
||||
- Получение структуры подразделений
|
||||
- Список коллег и подчинённых
|
||||
- Руководители подразделений
|
||||
|
||||
**API-методы:**
|
||||
|
||||
| Метод | Описание | Использование |
|
||||
|-------|----------|---------------|
|
||||
| `user.current` | Текущий пользователь | Контекст текущего юзера |
|
||||
| `im.user.get` | Данные пользователя | Информация о коллеге |
|
||||
| `im.search.user.list` | Поиск пользователей | Найти сотрудника |
|
||||
| `im.department.employees.get` | Сотрудники отдела | Список команды |
|
||||
| `im.department.managers.get` | Руководители отдела | Кто руководитель |
|
||||
| `im.department.colleagues.list` | Коллеги/подчинённые | Для руководителей |
|
||||
| `im.department.get` | Данные подразделения | Структура |
|
||||
| `department.get` | Список подразделений | Оргструктура |
|
||||
| `im.user.status.get` | Статус пользователя | Онлайн/офлайн |
|
||||
| `profile` | Базовая информация | Быстрый профиль |
|
||||
|
||||
---
|
||||
|
||||
## Группа 2: Задачи и проекты (tasks.*)
|
||||
|
||||
### Скилл: `bitrix24-tasks`
|
||||
|
||||
**Описание:** Полная работа с задачами — создание, обновление, завершение, комментирование. Доступ к своим задачам, задачам подчинённых и задачам в проектах/группах.
|
||||
|
||||
**Возможности:**
|
||||
- Создание задач с полным набором параметров (ответственный, дедлайн, приоритет, проект)
|
||||
- Обновление и завершение задач
|
||||
- Делегирование задач другому пользователю
|
||||
- Работа с чек-листами задач
|
||||
- Комментирование задач
|
||||
- Просмотр истории изменений
|
||||
- Фильтрация и сортировка задач
|
||||
- Добавление в избранное
|
||||
|
||||
**API-методы:**
|
||||
|
||||
| Метод | Описание | Использование |
|
||||
|-------|----------|---------------|
|
||||
| `tasks.task.add` | Создать задачу | Основной метод создания |
|
||||
| `tasks.task.update` | Обновить задачу | Изменение полей |
|
||||
| `tasks.task.delete` | Удалить задачу | Удаление |
|
||||
| `tasks.task.complete` | Завершить задачу | Закрытие |
|
||||
| `tasks.task.renew` | Возобновить задачу | Переоткрытие |
|
||||
| `tasks.task.delegate` | Делегировать задачу | Смена ответственного |
|
||||
| `tasks.task.list` | Список задач с фильтрами | Отчёты, обзоры, стендапы |
|
||||
| `tasks.task.getFields` | Поля задачи | Структура данных |
|
||||
| `tasks.task.favorite.add` | В избранное | Закладки |
|
||||
| `task.commentitem.add` | Добавить комментарий | Обсуждение в задаче |
|
||||
| `task.commentitem.getlist` | Список комментариев | Чтение обсуждений |
|
||||
| `task.checklistitem.add` | Пункт чек-листа | Детализация |
|
||||
| `task.checklistitem.getlist` | Список чек-листа | Прогресс |
|
||||
| `task.checklistitem.update` | Обновить чек-лист | Отметка выполнения |
|
||||
| `task.logitem.list` | История изменений | Аудит, ретро |
|
||||
|
||||
**Ключевые поля задачи:**
|
||||
- `TITLE` — название
|
||||
- `DESCRIPTION` — описание
|
||||
- `RESPONSIBLE_ID` — ответственный
|
||||
- `CREATED_BY` — постановщик
|
||||
- `DEADLINE` — крайний срок
|
||||
- `PRIORITY` — приоритет (0=низкий, 1=средний, 2=высокий)
|
||||
- `GROUP_ID` — проект/группа
|
||||
- `PARENT_ID` — родительская задача (подзадачи)
|
||||
- `STATUS` — статус (2=ждёт, 3=в работе, 4=ожидает контроля, 5=завершена)
|
||||
- `TAGS` — теги
|
||||
- `ACCOMPLICES` — соисполнители
|
||||
- `AUDITORS` — наблюдатели
|
||||
|
||||
**Ограничения прав:**
|
||||
- Свои задачи: полный доступ
|
||||
- Задачи подчинённых: чтение и редактирование
|
||||
- Задачи в проектах/группах: в соответствии с ролью в группе
|
||||
- Чужие задачи вне проектов: нет доступа
|
||||
|
||||
**Примеры использования в сценариях:**
|
||||
- Стендап-бот для проектов
|
||||
- Декомпозиция задач на подзадачи
|
||||
- Контроль просроченных задач
|
||||
- Создание задач из обсуждений в чатах
|
||||
- Еженедельные отчёты по прогрессу
|
||||
|
||||
---
|
||||
|
||||
### Скилл: `bitrix24-projects`
|
||||
|
||||
**Описание:** Работа с проектами (рабочими группами/коллабами) — управление участниками и контекст проекта.
|
||||
|
||||
**Возможности:**
|
||||
- Добавление участников в проект
|
||||
- Приглашение пользователей в группу
|
||||
- Контекст проекта для задач
|
||||
|
||||
**API-методы:**
|
||||
|
||||
| Метод | Описание | Использование |
|
||||
|-------|----------|---------------|
|
||||
| `sonet_group.user.add` | Добавить в группу | Управление командой |
|
||||
| `sonet_group.user.invite` | Пригласить в группу | Приглашения |
|
||||
|
||||
---
|
||||
|
||||
## Группа 3: CRM (crm.*)
|
||||
|
||||
### Скилл: `bitrix24-crm-deals`
|
||||
|
||||
**Описание:** Работа со сделками — создание, обновление, фильтрация, анализ воронки.
|
||||
|
||||
**Возможности:**
|
||||
- Создание и обновление сделок
|
||||
- Получение списка сделок с фильтрами и сортировкой
|
||||
- Получение полей сделки (включая пользовательские)
|
||||
- Связывание сделок с контактами
|
||||
- Работа с направлениями (воронками) сделок
|
||||
|
||||
**API-методы:**
|
||||
|
||||
| Метод | Описание | Использование |
|
||||
|-------|----------|---------------|
|
||||
| `crm.deal.add` | Создать сделку | Новая сделка |
|
||||
| `crm.deal.update` | Обновить сделку | Смена стадии, суммы |
|
||||
| `crm.deal.get` | Получить сделку | Детали сделки |
|
||||
| `crm.deal.list` | Список сделок | Воронка, отчёты |
|
||||
| `crm.deal.fields` | Поля сделки | Структура данных |
|
||||
| `crm.deal.contact.add` | Привязать контакт | Связи |
|
||||
| `crm.deal.contact.items.get` | Контакты сделки | Кто участвует |
|
||||
| `crm.deal.userfield.list` | Пользовательские поля | Кастомные данные |
|
||||
| `crm.dealcategory.fields` | Поля направлений | Воронки |
|
||||
| `crm.category.fields` | Информация о воронках | Настройки |
|
||||
|
||||
---
|
||||
|
||||
### Скилл: `bitrix24-crm-contacts`
|
||||
|
||||
**Описание:** Работа с контактами и компаниями в CRM.
|
||||
|
||||
**Возможности:**
|
||||
- Создание и обновление контактов
|
||||
- Поиск и фильтрация контактов
|
||||
- Работа с компаниями
|
||||
- Связи контакт-компания
|
||||
|
||||
**API-методы:**
|
||||
|
||||
| Метод | Описание | Использование |
|
||||
|-------|----------|---------------|
|
||||
| `crm.contact.add` | Создать контакт | Новый контакт |
|
||||
| `crm.contact.list` | Список контактов | Поиск, фильтрация |
|
||||
| `crm.contact.fields` | Поля контакта | Структура |
|
||||
| `crm.contact.company.fields` | Связь контакт-компания | Связи |
|
||||
| `crm.company.fields` | Поля компании | Структура |
|
||||
|
||||
---
|
||||
|
||||
### Скилл: `bitrix24-crm-leads`
|
||||
|
||||
**Описание:** Работа с лидами — создание, квалификация, конвертация.
|
||||
|
||||
**Возможности:**
|
||||
- Создание и обновление лидов
|
||||
- Фильтрация и квалификация
|
||||
- Связи лид-контакт
|
||||
|
||||
**API-методы:**
|
||||
|
||||
| Метод | Описание | Использование |
|
||||
|-------|----------|---------------|
|
||||
| `crm.lead.add` | Создать лид | Новый лид |
|
||||
| `crm.lead.list` | Список лидов | Квалификация, отчёты |
|
||||
| `crm.lead.fields` | Поля лида | Структура |
|
||||
| `crm.lead.contact.items.get` | Контакты лида | Связи |
|
||||
| `crm.lead.userfield.list` | Пользовательские поля | Кастомные данные |
|
||||
|
||||
---
|
||||
|
||||
### Скилл: `bitrix24-crm-activities`
|
||||
|
||||
**Описание:** Работа с делами и таймлайном CRM — звонки, встречи, комментарии, универсальные дела.
|
||||
|
||||
**Возможности:**
|
||||
- Создание дел (звонки, письма, встречи)
|
||||
- Универсальные дела (todo) в таймлайне
|
||||
- Комментарии в таймлайне
|
||||
- Лог-записи
|
||||
- Обновление дедлайнов и описаний
|
||||
|
||||
**API-методы:**
|
||||
|
||||
| Метод | Описание | Использование |
|
||||
|-------|----------|---------------|
|
||||
| `crm.activity.add` | Создать дело | Звонок, встреча, письмо |
|
||||
| `crm.activity.list` | Список дел | Обзор активностей |
|
||||
| `crm.activity.todo.add` | Универсальное дело | Todo в таймлайне |
|
||||
| `crm.activity.todo.update` | Обновить todo | Редактирование |
|
||||
| `crm.activity.todo.updateDeadline` | Сменить дедлайн | Перенос сроков |
|
||||
| `crm.activity.todo.updateDescription` | Обновить описание | Уточнение |
|
||||
| `crm.timeline.comment.add` | Комментарий в таймлайне | Заметки к сделке |
|
||||
| `crm.timeline.comment.update` | Обновить комментарий | Правки |
|
||||
| `crm.timeline.logmessage.add` | Лог-запись | Логирование событий |
|
||||
| `crm.livefeedmessage.add` | Сообщение в ленту CRM | Оповещения |
|
||||
| `crm.item.list` | Универсальные элементы | Смарт-процессы |
|
||||
| `crm.item.delete` | Удалить элемент | Очистка |
|
||||
|
||||
**Ограничения прав CRM:**
|
||||
- Доступ определяется ролью пользователя в CRM
|
||||
- Менеджер видит только свои сделки/лиды
|
||||
- Руководитель отдела — сделки отдела
|
||||
- Директор — всё
|
||||
- Пользовательские роли через настройки прав
|
||||
|
||||
---
|
||||
|
||||
## Группа 4: Календарь (calendar.*)
|
||||
|
||||
### Скилл: `bitrix24-calendar`
|
||||
|
||||
**Описание:** Управление событиями календаря — создание встреч, просмотр расписания, поиск свободных слотов.
|
||||
|
||||
**Возможности:**
|
||||
- Создание событий в календаре (встречи, напоминания)
|
||||
- Получение списка событий за период
|
||||
- Получение ближайших событий
|
||||
- Обновление и удаление событий
|
||||
- Работа с ресурсами (переговорные и т.д.)
|
||||
- Настройки календаря
|
||||
|
||||
**API-методы:**
|
||||
|
||||
| Метод | Описание | Использование |
|
||||
|-------|----------|---------------|
|
||||
| `calendar.event.add` | Создать событие | Новая встреча |
|
||||
| `calendar.event.get` | Список событий | Расписание на день/неделю |
|
||||
| `calendar.event.get.nearest` | Ближайшие события | Что скоро |
|
||||
| `calendar.event.update` | Обновить событие | Перенос, изменение |
|
||||
| `calendar.event.delete` | Удалить событие | Отмена |
|
||||
| `calendar.section.get` | Список календарей | Какие календари есть |
|
||||
| `calendar.section.add` | Создать календарь | Новый календарь |
|
||||
| `calendar.resource.list` | Список ресурсов | Переговорные |
|
||||
| `calendar.resource.update` | Обновить ресурс | Управление ресурсами |
|
||||
| `calendar.settings.get` | Настройки | Конфигурация |
|
||||
| `calendar.user.settings.get` | Пользовательские настройки | Персонализация |
|
||||
|
||||
**Ключевые поля события:**
|
||||
- `name` — название
|
||||
- `description` — описание
|
||||
- `from` / `to` — начало и конец
|
||||
- `attendees` — участники (массив ID пользователей)
|
||||
- `location` — место/переговорная
|
||||
- `importance` — важность
|
||||
- `is_meeting` — встреча с участниками
|
||||
- `rrule` — правило повторения
|
||||
|
||||
**Примеры использования в сценариях:**
|
||||
- Поиск свободных слотов для встречи
|
||||
- Утренний брифинг с расписанием дня
|
||||
- Автоматическое создание встреч после согласования
|
||||
- Напоминания о предстоящих событиях
|
||||
|
||||
---
|
||||
|
||||
## Группа 5: Файлы (disk.*)
|
||||
|
||||
### Скилл: `bitrix24-files`
|
||||
|
||||
**Описание:** Работа с файлами на Диске Битрикс24 — чтение, загрузка, поиск, организация.
|
||||
|
||||
**Возможности:**
|
||||
- Загрузка файлов в хранилище и папки
|
||||
- Чтение содержимого папок
|
||||
- Копирование и перемещение файлов
|
||||
- Создание папок
|
||||
- Получение публичных ссылок
|
||||
- Работа с прикреплёнными файлами из чатов
|
||||
|
||||
**API-методы:**
|
||||
|
||||
| Метод | Описание | Использование |
|
||||
|-------|----------|---------------|
|
||||
| `disk.storage.getchildren` | Файлы в корне хранилища | Обзор файлов |
|
||||
| `disk.storage.uploadfile` | Загрузить в корень | Новый файл |
|
||||
| `disk.storage.addfolder` | Создать папку в корне | Организация |
|
||||
| `disk.folder.getchildren` | Содержимое папки | Обзор |
|
||||
| `disk.folder.uploadfile` | Загрузить в папку | Новый файл |
|
||||
| `disk.folder.addsubfolder` | Создать подпапку | Структура |
|
||||
| `disk.folder.get` | Информация о папке | Метаданные |
|
||||
| `disk.folder.getexternallink` | Публичная ссылка | Поделиться |
|
||||
| `disk.folder.moveto` | Переместить папку | Реорганизация |
|
||||
| `disk.file.get` | Информация о файле | Метаданные |
|
||||
| `disk.file.copyto` | Копировать файл | Дублирование |
|
||||
| `disk.file.rename` | Переименовать | Переименование |
|
||||
| `disk.file.delete` | Удалить файл | Очистка |
|
||||
| `disk.attachedObject.get` | Прикреплённый файл | Файлы из чатов |
|
||||
|
||||
**Примеры использования в сценариях:**
|
||||
- Поиск документов по описанию
|
||||
- Каталог файлов проекта
|
||||
- Автоматическая рассылка отчётов
|
||||
- Загрузка сгенерированных файлов (КП, отчёты)
|
||||
|
||||
---
|
||||
|
||||
## Сводная таблица скиллов
|
||||
|
||||
| Скилл | Группа | Кол-во методов | Основные сценарии |
|
||||
|-------|--------|---------------|-------------------|
|
||||
| `bitrix24-messaging` | Коммуникации | 14 | Дайджесты, рассылки, маршрутизация |
|
||||
| `bitrix24-users` | Коммуникации | 10 | Поиск коллег, оргструктура |
|
||||
| `bitrix24-tasks` | Задачи | 15 | Стендапы, контроль, автоматизация |
|
||||
| `bitrix24-projects` | Задачи | 2 | Управление командой проекта |
|
||||
| `bitrix24-crm-deals` | CRM | 10 | Воронка, сделки, отчёты |
|
||||
| `bitrix24-crm-contacts` | CRM | 5 | Клиентская база |
|
||||
| `bitrix24-crm-leads` | CRM | 5 | Квалификация, лидогенерация |
|
||||
| `bitrix24-crm-activities` | CRM | 12 | Дела, таймлайн, логирование |
|
||||
| `bitrix24-calendar` | Календарь | 11 | Встречи, расписание, ресурсы |
|
||||
| `bitrix24-files` | Файлы | 14 | Документы, отчёты, хранилище |
|
||||
|
||||
**Итого: 10 скиллов, ~98 API-методов**
|
||||
|
||||
---
|
||||
|
||||
## Рекомендуемая группировка для конечного пользователя
|
||||
|
||||
Для простоты восприятия скиллы можно объединить в 5 пользовательских блоков:
|
||||
|
||||
1. **Чаты и коммуникации** = `bitrix24-messaging` + `bitrix24-users`
|
||||
2. **Задачи и проекты** = `bitrix24-tasks` + `bitrix24-projects`
|
||||
3. **CRM** = `bitrix24-crm-deals` + `bitrix24-crm-contacts` + `bitrix24-crm-leads` + `bitrix24-crm-activities`
|
||||
4. **Календарь** = `bitrix24-calendar`
|
||||
5. **Файлы** = `bitrix24-files`
|
||||
|
||||
---
|
||||
|
||||
## Матрица «Сценарий → Скиллы»
|
||||
|
||||
| Сценарий | Чаты | Задачи | CRM | Календарь | Файлы |
|
||||
|----------|------|--------|-----|-----------|-------|
|
||||
| Утренний брифинг по сделкам | ✅ | | ✅ | ✅ | |
|
||||
| Подготовка к встрече с клиентом | ✅ | | ✅ | | ✅ |
|
||||
| Обогащение CRM из переписки | ✅ | | ✅ | | |
|
||||
| Анализ воронки продаж | | | ✅ | | |
|
||||
| Напоминания о забытых клиентах | ✅ | | ✅ | | |
|
||||
| Генерация КП | ✅ | | ✅ | | ✅ |
|
||||
| Дашборд руководителя | ✅ | ✅ | ✅ | ✅ | |
|
||||
| Контроль задач подчинённых | ✅ | ✅ | | | |
|
||||
| Еженедельный отчёт | ✅ | ✅ | ✅ | | ✅ |
|
||||
| Распределение задач | ✅ | ✅ | | | |
|
||||
| Стендап-бот | ✅ | ✅ | | | |
|
||||
| Задачи из обсуждений | ✅ | ✅ | | | |
|
||||
| Декомпозиция задач | | ✅ | | | |
|
||||
| Статус проекта | ✅ | ✅ | | | ✅ |
|
||||
| Ретроспектива | ✅ | ✅ | | | |
|
||||
| Дайджест пропущенных | ✅ | | | | |
|
||||
| Мониторинг каналов | ✅ | ✅ | | | |
|
||||
| Маршрутизация сообщений | ✅ | | | | |
|
||||
| Объявление в канале | ✅ | | | | |
|
||||
| Планирование встречи | ✅ | | | ✅ | |
|
||||
| Подготовка к совещанию | ✅ | ✅ | | ✅ | ✅ |
|
||||
| Протокол встречи | ✅ | ✅ | | ✅ | |
|
||||
| Онбординг | ✅ | ✅ | | ✅ | |
|
||||
| Сбор обратной связи | ✅ | | | | |
|
||||
| Дни рождения | ✅ | | | ✅ | |
|
||||
| Контент-план | ✅ | ✅ | | | |
|
||||
| Рассылка по CRM | ✅ | | ✅ | | |
|
||||
| Поиск документа | | | | | ✅ |
|
||||
| Каталог файлов проекта | | ✅ | | | ✅ |
|
||||
| Рассылка отчётов | ✅ | | | | ✅ |
|
||||
| Квалификация лидов | | | ✅ | | |
|
||||
| Отчёт по воронке | | | ✅ | | ✅ |
|
||||
| Мониторинг дублей | | | ✅ | | |
|
||||
| Персональный брифинг | ✅ | ✅ | ✅ | ✅ | |
|
||||
| Второй мозг | ✅ | | | | ✅ |
|
||||
| Трекер привычек | ✅ | | | ✅ | |
|
||||
| AI-секретарь | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
593
bundled-skills/bitrix24/SKILL.md
Normal file
593
bundled-skills/bitrix24/SKILL.md
Normal file
@@ -0,0 +1,593 @@
|
||||
---
|
||||
name: bitrix24
|
||||
description: >
|
||||
Work with Bitrix24 (Битрикс24) via REST API and MCP documentation server. Triggers on:
|
||||
CRM — "сделки", "контакты", "лиды", "воронка", "клиенты", "deals", "contacts", "leads", "pipeline";
|
||||
Tasks — "задачи", "мои задачи", "просроченные", "создай задачу", "tasks", "overdue", "to-do";
|
||||
Calendar — "расписание", "встречи", "календарь", "schedule", "meetings", "events";
|
||||
Chat — "чаты", "сообщения", "уведомления", "написать", "notifications", "messages";
|
||||
Channels — "каналы", "канал", "объявления", "подписчики", "channels", "announcements", "subscribers";
|
||||
Open Lines — "открытые линии", "поддержка", "обращения", "клиентские чаты", "операторы",
|
||||
"омниканал", "виджет чата", "open lines", "support", "customer chat", "helpdesk", "operator";
|
||||
Projects — "проекты", "рабочие группы", "projects", "workgroups";
|
||||
Time — "рабочее время", "кто на работе", "учёт времени", "timeman", "work status";
|
||||
Drive — "файлы", "документы", "диск", "files", "documents", "drive";
|
||||
Structure — "сотрудники", "отделы", "структура", "подчинённые", "departments", "employees", "org structure";
|
||||
Feed — "лента", "новости", "объявления", "feed", "announcements";
|
||||
Scenarios — "утренний брифинг", "morning briefing", "еженедельный отчёт", "weekly report",
|
||||
"статус команды", "что у меня сегодня", "итоги дня", "план на день", "воронка продаж",
|
||||
"расскажи про клиента", "подготовь к встрече", "как работает отдел".
|
||||
metadata:
|
||||
openclaw:
|
||||
requires:
|
||||
bins:
|
||||
- python3
|
||||
mcp:
|
||||
- url: https://mcp-dev.bitrix24.tech/mcp
|
||||
transport: streamable_http
|
||||
tools:
|
||||
- bitrix-search
|
||||
- bitrix-app-development-doc-details
|
||||
- bitrix-method-details
|
||||
- bitrix-article-details
|
||||
- bitrix-event-details
|
||||
emoji: "B24"
|
||||
homepage: https://github.com/rsvbitrix/bitrix24-skill
|
||||
aliases:
|
||||
- Bitrix24
|
||||
- bitrix24
|
||||
- Bitrix
|
||||
- bitrix
|
||||
- b24
|
||||
- Битрикс24
|
||||
- битрикс24
|
||||
- Битрикс
|
||||
- битрикс
|
||||
tags:
|
||||
- bitrix24
|
||||
- bitrix
|
||||
- b24
|
||||
- crm
|
||||
- tasks
|
||||
- calendar
|
||||
- drive
|
||||
- chat
|
||||
- messenger
|
||||
- im
|
||||
- webhook
|
||||
- oauth
|
||||
- mcp
|
||||
- Битрикс24
|
||||
- CRM
|
||||
- задачи
|
||||
- чат
|
||||
- проекты
|
||||
- группы
|
||||
- лента
|
||||
- рабочее время
|
||||
- timeman
|
||||
- socialnetwork
|
||||
- feed
|
||||
- projects
|
||||
- workgroups
|
||||
- org structure
|
||||
- smart process
|
||||
- смарт-процесс
|
||||
- products
|
||||
- товары
|
||||
- каталог
|
||||
- quotes
|
||||
- предложения
|
||||
- invoices
|
||||
- счета
|
||||
- open lines
|
||||
- openlines
|
||||
- imopenlines
|
||||
- открытые линии
|
||||
- поддержка
|
||||
- обращения
|
||||
- операторы
|
||||
- омниканал
|
||||
- helpdesk
|
||||
- landing
|
||||
- sites
|
||||
- сайты
|
||||
- лендинги
|
||||
---
|
||||
|
||||
# Bitrix24
|
||||
|
||||
## STOP — Read These Rules Before Doing Anything
|
||||
|
||||
You are talking to a business person (company director), NOT a developer. They do not know what an API is. They do not want to see technical details. Every violation of these rules makes the user angry.
|
||||
|
||||
### Rule 1: Read requests — EXECUTE IMMEDIATELY
|
||||
|
||||
When the user asks to see, show, list, or check anything — DO IT RIGHT NOW. Do not ask questions. Do not ask for confirmation. Do not offer choices. Just call the Bitrix24 methods and show the result.
|
||||
|
||||
User says "дай расписание на среду" → you IMMEDIATELY:
|
||||
1. Call `user.current` to get user ID and timezone
|
||||
2. Call `calendar.event.get` for that date (read `references/calendar.md` for exact syntax)
|
||||
3. Call `tasks.task.list` with deadline filter for that date (read `references/tasks.md`)
|
||||
4. Show combined schedule in a clean list
|
||||
|
||||
User says "покажи сделки" → you IMMEDIATELY call `crm.deal.list` and show results.
|
||||
|
||||
User says "мои задачи" → you IMMEDIATELY call `tasks.task.list` and show results.
|
||||
|
||||
### Rule 2: NEVER show technical details
|
||||
|
||||
These words are FORBIDDEN in your replies to the user:
|
||||
API, REST, webhook, MCP, endpoint, scope, token, curl, JSON, method, parameter, SDK, OAuth, calendar.event.get, tasks.task.list, crm.deal.list, bitrix24_call.py, config.json
|
||||
|
||||
WRONG replies (NEVER do this):
|
||||
- "Используем ваш webhook URL bitrix24.team/rest/5/..." — FORBIDDEN
|
||||
- "Вызовем calendar.get или calendar.event.get..." — FORBIDDEN
|
||||
- "Пришлите экспорт календаря (ICS/CSV)..." — FORBIDDEN
|
||||
- "Подтвердите часовой пояс..." — FORBIDDEN
|
||||
- "Подтвердите источник данных..." — FORBIDDEN
|
||||
- "Как предпочитаете продолжить?" — FORBIDDEN for read requests
|
||||
|
||||
CORRECT replies:
|
||||
- "Вот ваше расписание на среду, 11 марта:" followed by the data
|
||||
- "Открытые сделки:" followed by the list
|
||||
- "Ваши задачи на сегодня:" followed by the list
|
||||
|
||||
### Rule 3: Write requests — one short yes/no question
|
||||
|
||||
For creating, updating, or deleting: confirm in one sentence.
|
||||
- CORRECT: "Создать сделку «Поставка» на 500 000 ₽?"
|
||||
- WRONG: "Вызовем crm.deal.add с параметрами..."
|
||||
|
||||
### Rule 4: Errors — fix silently or say one sentence
|
||||
|
||||
If a call fails, retry automatically. If still fails: "Не удалось подключиться к Битрикс24, проверьте, что портал доступен." Nothing else.
|
||||
|
||||
### Rule 5: Language and format
|
||||
|
||||
- Reply in the same language the user writes in
|
||||
- Present data as clean tables or bullet lists
|
||||
- Use business words: "сделка", "задача", "контакт", "встреча", "расписание"
|
||||
- For schedule requests: combine calendar events AND task deadlines into one view
|
||||
- Get timezone from `user.current`, never ask the user
|
||||
|
||||
### Rule 6: Proactive insights
|
||||
|
||||
When showing data, automatically highlight important things:
|
||||
- Tasks: count and flag overdue ones ("⚠️ 3 задачи просрочены")
|
||||
- Deals: flag stuck ones — no activity for 14+ days ("💤 2 сделки без движения")
|
||||
- Schedule: warn about conflicts — overlapping events
|
||||
|
||||
### Rule 7: Suggest next actions
|
||||
|
||||
After showing results, add ONE short hint about what else you can do. Keep it to one line.
|
||||
- After schedule: "Могу перенести встречу или добавить задачу."
|
||||
- After tasks: "Могу отметить задачу выполненной или создать новую."
|
||||
- After deals: "Могу показать детали по сделке или создать новую."
|
||||
- After contacts: "Могу найти сделки этого контакта или добавить задачу."
|
||||
|
||||
### Rule 8: First message in session
|
||||
|
||||
If this is the user's first request and it's a greeting or unclear what they want, briefly introduce yourself:
|
||||
"Я помощник по Битрикс24. Могу показать расписание, задачи, сделки, контакты или отчёт по команде. Что интересно?"
|
||||
|
||||
## Ready-Made Scenarios
|
||||
|
||||
Use these when the user's request matches. Execute ALL calls, then present combined result.
|
||||
|
||||
### Morning briefing ("что у меня сегодня?", "утренний брифинг", "дай обзор")
|
||||
|
||||
Use batch call for speed:
|
||||
```bash
|
||||
python3 scripts/bitrix24_batch.py \
|
||||
--cmd 'calendar=calendar.event.get.nearest?type=user&ownerId=<ID>&forCurrentUser=Y&days=1' \
|
||||
--cmd 'tasks=tasks.task.list?filter[RESPONSIBLE_ID]=<ID>&filter[!STATUS]=5&filter[<=DEADLINE]=<today_end>' \
|
||||
--cmd 'deals=crm.deal.list?filter[ASSIGNED_BY_ID]=<ID>&filter[STAGE_SEMANTIC_ID]=P&select[]=ID&select[]=TITLE&select[]=OPPORTUNITY&select[]=STAGE_ID' \
|
||||
--json
|
||||
```
|
||||
|
||||
Present as:
|
||||
- 📅 Встречи сегодня (from calendar)
|
||||
- ✅ Задачи на сегодня + просроченные (from tasks, flag overdue)
|
||||
- 💰 Активные сделки (from deals, flag stuck)
|
||||
|
||||
### Weekly report ("итоги недели", "еженедельный отчёт")
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_batch.py \
|
||||
--cmd 'done=tasks.task.list?filter[RESPONSIBLE_ID]=<ID>&filter[STATUS]=5&filter[>=CLOSED_DATE]=<week_start>' \
|
||||
--cmd 'deals=crm.deal.list?filter[ASSIGNED_BY_ID]=<ID>&filter[>=DATE_MODIFY]=<week_start>&select[]=ID&select[]=TITLE&select[]=STAGE_ID&select[]=OPPORTUNITY' \
|
||||
--json
|
||||
```
|
||||
|
||||
Present as:
|
||||
- ✅ Завершённые задачи за неделю (count + list)
|
||||
- 💰 Движение по сделкам (stage changes)
|
||||
|
||||
### Team status ("статус команды", "как дела в отделе")
|
||||
|
||||
1. Get department: `department.get` with user's department
|
||||
2. Get employees: `im.department.employees.get`
|
||||
3. Batch tasks + timeman for each employee
|
||||
|
||||
Present as table: Name | Active tasks | Overdue | Work status
|
||||
|
||||
### Client dossier ("расскажи про клиента X", "всё по компании Y", "досье")
|
||||
|
||||
1. Find contact/company by name → `crm.contact.list` filter `%LAST_NAME` or `crm.company.list` filter `%TITLE`
|
||||
2. Batch:
|
||||
```bash
|
||||
python3 scripts/bitrix24_batch.py \
|
||||
--cmd 'deals=crm.deal.list?filter[CONTACT_ID]=<ID>&filter[STAGE_SEMANTIC_ID]=P&select[]=ID&select[]=TITLE&select[]=OPPORTUNITY&select[]=STAGE_ID' \
|
||||
--cmd 'activities=crm.activity.list?filter[OWNER_TYPE_ID]=3&filter[OWNER_ID]=<ID>&select[]=ID&select[]=SUBJECT&select[]=DEADLINE&order[DEADLINE]=desc' \
|
||||
--json
|
||||
```
|
||||
|
||||
Present as:
|
||||
- 👤 Контакт — имя, компания, телефон, email
|
||||
- 💰 Сделки — список с суммами и стадиями
|
||||
- 📋 Последние действия — звонки, письма, встречи
|
||||
- 💡 Подсказка: "Могу создать задачу по этому клиенту или запланировать звонок."
|
||||
|
||||
### Meeting prep ("подготовь к встрече", "что за встреча в 14:00")
|
||||
|
||||
1. Get today's events → `calendar.event.get` for today
|
||||
2. Find the matching event by time or name
|
||||
3. Get attendee info → `user.get` for each attendee ID
|
||||
4. Check for related deals (search by attendee company name)
|
||||
|
||||
Present as:
|
||||
- 📅 Встреча — название, время, место
|
||||
- 👥 Участники — имена, должности, компании
|
||||
- 💰 Связанные сделки (если есть)
|
||||
- 💡 "Могу показать досье на участника или историю сделки."
|
||||
|
||||
### Day results ("итоги дня", "что я сделал", "мой отчёт за день")
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_batch.py \
|
||||
--cmd 'tasks=tasks.task.list?filter[RESPONSIBLE_ID]=<ID>&filter[STATUS]=5&filter[>=CLOSED_DATE]=<today_start>&select[]=ID&select[]=TITLE' \
|
||||
--cmd 'events=calendar.event.get?type=user&ownerId=<ID>&from=<today_start>&to=<today_end>' \
|
||||
--json
|
||||
```
|
||||
Also call `crm.stagehistory.list` with `filter[>=CREATED_TIME]=<today_start>` for deal movements.
|
||||
|
||||
Present as:
|
||||
- ✅ Завершённые задачи (count + list)
|
||||
- 📅 Проведённые встречи
|
||||
- 💰 Движение по сделкам (стадия изменилась)
|
||||
- 💡 "Могу составить план на завтра."
|
||||
|
||||
### Sales pipeline ("воронка", "как работает отдел продаж", "продажи")
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_batch.py \
|
||||
--cmd 'active=crm.deal.list?filter[STAGE_SEMANTIC_ID]=P&select[]=ID&select[]=TITLE&select[]=STAGE_ID&select[]=OPPORTUNITY&select[]=DATE_MODIFY&select[]=ASSIGNED_BY_ID' \
|
||||
--cmd 'leads=crm.lead.list?filter[>=DATE_CREATE]=<week_start>&select[]=ID&select[]=TITLE&select[]=SOURCE_ID&select[]=DATE_CREATE' \
|
||||
--json
|
||||
```
|
||||
|
||||
Present as:
|
||||
- 📊 Воронка — сделки по стадиям с суммами
|
||||
- 💤 Зависшие — без движения 14+ дней
|
||||
- 🆕 Новые лиды за неделю
|
||||
- 💡 "Могу показать детали по сделке или назначить задачу менеджеру."
|
||||
|
||||
### Cross-domain search ("найди...", "кто отвечает за...", "все по теме...")
|
||||
|
||||
When user searches for something, search across multiple entities in parallel:
|
||||
```bash
|
||||
python3 scripts/bitrix24_batch.py \
|
||||
--cmd 'contacts=crm.contact.list?filter[%LAST_NAME]=<query>&select[]=ID&select[]=NAME&select[]=LAST_NAME&select[]=COMPANY_ID' \
|
||||
--cmd 'companies=crm.company.list?filter[%TITLE]=<query>&select[]=ID&select[]=TITLE' \
|
||||
--cmd 'deals=crm.deal.list?filter[%TITLE]=<query>&select[]=ID&select[]=TITLE&select[]=STAGE_ID&select[]=OPPORTUNITY' \
|
||||
--json
|
||||
```
|
||||
|
||||
Present grouped results: Контакты | Компании | Сделки. If only one match — show full details immediately.
|
||||
|
||||
---
|
||||
|
||||
## Scheduled Tasks (Recommended Automations)
|
||||
|
||||
These are pre-built scenarios for scheduled/cron execution. The user can activate them via OpenClaw scheduled tasks.
|
||||
|
||||
### Day plan (daily, workdays 08:30)
|
||||
|
||||
Build a structured day plan from calendar events and tasks:
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_batch.py \
|
||||
--cmd 'events=calendar.event.get?type=user&ownerId=<ID>&from=<today_start>&to=<today_end>' \
|
||||
--cmd 'tasks=tasks.task.list?filter[RESPONSIBLE_ID]=<ID>&filter[<=DEADLINE]=<today_end>&filter[<REAL_STATUS]=5&select[]=ID&select[]=TITLE&select[]=DEADLINE&select[]=STATUS&order[DEADLINE]=asc' \
|
||||
--json
|
||||
```
|
||||
|
||||
Output format:
|
||||
```
|
||||
📋 План на день — <date>
|
||||
|
||||
📅 Встречи:
|
||||
09:00 – Планёрка
|
||||
14:00 – Звонок с ООО «Рога и копыта»
|
||||
16:30 – Обзор проекта
|
||||
|
||||
✅ Задачи (дедлайн сегодня):
|
||||
• Подготовить КП для клиента
|
||||
• Отправить отчёт
|
||||
|
||||
⚠️ Просроченные:
|
||||
• Согласовать договор (дедлайн был 5 марта)
|
||||
```
|
||||
|
||||
### Morning briefing (daily, workdays 09:00)
|
||||
|
||||
Day plan (above) PLUS active deals summary and new leads from yesterday:
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_batch.py \
|
||||
--cmd 'events=calendar.event.get?type=user&ownerId=<ID>&from=<today_start>&to=<today_end>' \
|
||||
--cmd 'tasks=tasks.task.list?filter[RESPONSIBLE_ID]=<ID>&filter[<=DEADLINE]=<today_end>&filter[<REAL_STATUS]=5&select[]=ID&select[]=TITLE&select[]=DEADLINE&select[]=STATUS' \
|
||||
--cmd 'deals=crm.deal.list?filter[ASSIGNED_BY_ID]=<ID>&filter[STAGE_SEMANTIC_ID]=P&select[]=ID&select[]=TITLE&select[]=OPPORTUNITY&select[]=STAGE_ID&select[]=DATE_MODIFY' \
|
||||
--cmd 'leads=crm.lead.list?filter[>=DATE_CREATE]=<yesterday_start>&select[]=ID&select[]=TITLE&select[]=SOURCE_ID' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Evening summary (daily, workdays 18:00)
|
||||
|
||||
Same as "Day results" scenario. Summarize completed tasks, past meetings, deal movements.
|
||||
|
||||
### Weekly report (Friday 17:00)
|
||||
|
||||
Same as "Weekly report" scenario. Tasks completed + deal pipeline changes for the week.
|
||||
|
||||
### Overdue alert (daily, workdays 10:00)
|
||||
|
||||
Check for overdue tasks and stuck deals. Send ONLY if there are problems (no spam when all is clean):
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_batch.py \
|
||||
--cmd 'overdue=tasks.task.list?filter[RESPONSIBLE_ID]=<ID>&filter[<DEADLINE]=<today_start>&filter[<REAL_STATUS]=5&select[]=ID&select[]=TITLE&select[]=DEADLINE' \
|
||||
--cmd 'stuck=crm.deal.list?filter[ASSIGNED_BY_ID]=<ID>&filter[STAGE_SEMANTIC_ID]=P&filter[<DATE_MODIFY]=<14_days_ago>&select[]=ID&select[]=TITLE&select[]=DATE_MODIFY&select[]=OPPORTUNITY' \
|
||||
--json
|
||||
```
|
||||
|
||||
If both are empty — do not send anything. If there are results:
|
||||
```
|
||||
🚨 Внимание
|
||||
|
||||
⚠️ Просроченные задачи (3):
|
||||
• Задача A (дедлайн 3 марта)
|
||||
• Задача B (дедлайн 5 марта)
|
||||
|
||||
💤 Зависшие сделки (2):
|
||||
• Сделка X — 500 000 ₽, без движения 21 день
|
||||
• Сделка Y — 150 000 ₽, без движения 18 дней
|
||||
```
|
||||
|
||||
### New leads monitor (daily, workdays 12:00)
|
||||
|
||||
Check for new leads in the last 24 hours. Send only if there are new leads:
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.lead.list \
|
||||
--param 'filter[>=DATE_CREATE]=<24h_ago>' \
|
||||
--param 'select[]=ID' \
|
||||
--param 'select[]=TITLE' \
|
||||
--param 'select[]=SOURCE_ID' \
|
||||
--param 'select[]=NAME' \
|
||||
--param 'select[]=LAST_NAME' \
|
||||
--json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Setup
|
||||
|
||||
The only thing needed is a webhook URL. When the user provides one, save it and verify:
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py user.current --url "<webhook>" --json
|
||||
```
|
||||
|
||||
This saves the webhook to config and calls `user.current` to verify it works. It also caches user_id and timezone in the config for faster subsequent calls. After that, all calls use the saved config automatically.
|
||||
|
||||
If the webhook is not configured yet and you need to set it up, read `references/access.md`.
|
||||
|
||||
## Making REST Calls
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py <method> --json
|
||||
```
|
||||
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py user.current --json
|
||||
python3 scripts/bitrix24_call.py crm.deal.list \
|
||||
--param 'select[]=ID' \
|
||||
--param 'select[]=TITLE' \
|
||||
--param 'select[]=STAGE_ID' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Parameters from JSON file
|
||||
|
||||
For complex parameters (nested objects, arrays, multi-file uploads), use `--params-file` instead of multiple `--param` flags. This avoids shell escaping issues:
|
||||
|
||||
```bash
|
||||
echo '{"filter": {">=DATE_CREATE": "2025-01-01", "%TITLE": "client"}, "select": ["ID", "TITLE"]}' > /tmp/params.json
|
||||
python3 scripts/bitrix24_call.py crm.deal.list --params-file /tmp/params.json --json
|
||||
```
|
||||
|
||||
### Auto-pagination
|
||||
|
||||
For `.list` methods, use `--iterate` to automatically collect all pages:
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.deal.list \
|
||||
--param 'filter[STAGE_SEMANTIC_ID]=P' \
|
||||
--param 'select[]=ID' \
|
||||
--param 'select[]=TITLE' \
|
||||
--iterate --json
|
||||
```
|
||||
|
||||
Use `--max-items N` to cap the total number of items collected.
|
||||
|
||||
### Dry-run mode
|
||||
|
||||
Preview what would be called without executing:
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.deal.add \
|
||||
--param 'fields[TITLE]=Test' \
|
||||
--dry-run --json
|
||||
```
|
||||
|
||||
### Operation safety
|
||||
|
||||
Methods are automatically classified by suffix:
|
||||
|
||||
| Type | Suffixes | Required flag |
|
||||
|------|----------|---------------|
|
||||
| Read | `.list`, `.get`, `.current`, `.fields` | — |
|
||||
| Write | `.add`, `.update`, `.set`, `.start`, `.complete`, `.attach`, `.send` | `--confirm-write` |
|
||||
| Destructive | `.delete`, `.remove`, `.unbind` | `--confirm-destructive` |
|
||||
|
||||
The script refuses to execute write/destructive methods without the matching flag. This prevents accidental data changes. When writing scenarios, always include the flag:
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.deal.add \
|
||||
--param 'fields[TITLE]=New deal' \
|
||||
--confirm-write --json
|
||||
```
|
||||
|
||||
If calls fail, read `references/troubleshooting.md` and run `scripts/check_webhook.py --json`.
|
||||
|
||||
## Batch Calls (Multiple Methods in One Request)
|
||||
|
||||
For scenarios that need 2+ methods (schedule, briefing, reports), use batch to reduce HTTP calls:
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_batch.py \
|
||||
--cmd 'tasks=tasks.task.list?filter[RESPONSIBLE_ID]=5' \
|
||||
--cmd 'deals=crm.deal.list?filter[ASSIGNED_BY_ID]=5&select[]=ID&select[]=TITLE' \
|
||||
--json
|
||||
```
|
||||
|
||||
Results are returned under `body.result.result` keyed by command name. Use batch whenever you need data from 2+ domains.
|
||||
|
||||
### Cross-command references ($result)
|
||||
|
||||
In batch, use `$result[name]` to pass the output of one command into another. This allows chaining — e.g., create a company and immediately create a contact linked to it:
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_batch.py \
|
||||
--cmd 'company=crm.company.add?fields[TITLE]=Acme Corp' \
|
||||
--cmd 'contact=crm.contact.add?fields[NAME]=John&fields[COMPANY_ID]=$result[company]' \
|
||||
--cmd 'deal=crm.deal.add?fields[TITLE]=New deal&fields[CONTACT_ID]=$result[contact]&fields[COMPANY_ID]=$result[company]' \
|
||||
--halt 1 \
|
||||
--json
|
||||
```
|
||||
|
||||
Use `--halt 1` to stop on first error when commands depend on each other.
|
||||
|
||||
**Encoding note:** Batch commands use query string format — Cyrillic and special characters must be URL-encoded. For complex values, prefer `--params-file` with the regular `bitrix24_call.py` instead of batch.
|
||||
|
||||
## User ID and Timezone Cache
|
||||
|
||||
After the first `user.current` call, user_id and timezone are saved to config. To use cached values without calling `user.current` again:
|
||||
|
||||
```python
|
||||
from bitrix24_config import get_cached_user
|
||||
user = get_cached_user() # returns {"user_id": 5, "timezone": "Europe/Kaliningrad"} or None
|
||||
```
|
||||
|
||||
If cache is empty, call `user.current` first — it auto-populates the cache.
|
||||
|
||||
## Finding the Right Method
|
||||
|
||||
When the exact method name is unknown, use MCP docs in this order:
|
||||
|
||||
1. `bitrix-search` to find the method, event, or article title.
|
||||
2. `bitrix-method-details` for REST methods.
|
||||
3. `bitrix-event-details` for event docs.
|
||||
4. `bitrix-article-details` for regular documentation articles.
|
||||
5. `bitrix-app-development-doc-details` for OAuth, install callbacks, BX24 SDK topics.
|
||||
|
||||
Do not guess method names from memory when the task is sensitive or the method family is large. Search first.
|
||||
|
||||
Then read the domain reference that matches the task:
|
||||
|
||||
- `references/crm.md`
|
||||
- `references/smartprocess.md`
|
||||
- `references/products.md`
|
||||
- `references/quotes.md`
|
||||
- `references/tasks.md`
|
||||
- `references/chat.md`
|
||||
- `references/channels.md`
|
||||
- `references/openlines.md`
|
||||
- `references/calendar.md`
|
||||
- `references/drive.md`
|
||||
- `references/files.md`
|
||||
- `references/users.md`
|
||||
- `references/projects.md`
|
||||
- `references/feed.md`
|
||||
- `references/timeman.md`
|
||||
- `references/sites.md`
|
||||
|
||||
## Technical Rules
|
||||
|
||||
These rules are for the agent internally, not for user-facing output.
|
||||
|
||||
- Start with `user.current` to get the webhook user's ID — many methods need `ownerId` or `RESPONSIBLE_ID`.
|
||||
- Do not invent method names. There is no `calendar.get`, `tasks.list`, etc. Always use exact names from the reference files or MCP search. When unsure, search MCP first.
|
||||
- Prefer server-side filtering with `filter[...]` and narrow output with `select[]`.
|
||||
- Filter operators are prefixes on the key: `>=DEADLINE`, `!STATUS`, `>OPPORTUNITY`. Not on the value.
|
||||
- Use `*.fields` or user-field discovery methods before writing custom fields.
|
||||
- Expect pagination on list methods via `start` (page size = 50).
|
||||
- Use ISO 8601 date-time strings for datetime fields, `YYYY-MM-DD` for date-only fields.
|
||||
- Treat `ACCESS_DENIED`, `insufficient_scope`, `QUERY_LIMIT_EXCEEDED`, and `expired_token` as normal operational cases.
|
||||
- For `imbot.*`, persist and reuse the same `CLIENT_ID`.
|
||||
- When a call fails, run `scripts/check_webhook.py --json` before asking the user.
|
||||
- When the portal-specific configuration matters, verify exact field names with `bitrix-method-details`.
|
||||
|
||||
## API Module Restrictions
|
||||
|
||||
Not all Bitrix24 REST modules work as expected through a webhook. Some methods exist only for external system integration. Before using methods from these modules, understand their limitations:
|
||||
|
||||
- **Telephony (`voximplant.*`, `telephony.*`):** Does NOT make real calls. `telephony.externalcall.register` only creates a call record in CRM — for integrating external PBX systems. Tell the user the REST API cannot initiate voice connections.
|
||||
- **Mail services (`mailservice.*`):** Configures SMTP/IMAP server settings, cannot send or read emails. No REST API exists for actual email operations.
|
||||
- **SMS providers (`messageservice.*`):** Registers SMS providers, does not send messages directly. Requires a pre-configured external provider.
|
||||
- **Connectors (`imconnector.*`):** Infrastructure for connecting external messengers to Open Lines. Requires an external server handler. Useless without a configured integration.
|
||||
- **Widget embedding (`placement.*`, `userfieldtype.*`):** Registers UI widgets and custom field types. Only works in Marketplace application context, not via webhook.
|
||||
- **Event handlers (`event.*`):** Registers webhook handlers for events. Requires an external HTTP server to receive notifications.
|
||||
- **Business processes (`bizproc.*`):** `bizproc.workflow.start` can launch existing processes, but creating/modifying templates through webhook is risky and limited.
|
||||
|
||||
If the user requests something from these modules — do not refuse. Explain what the method actually does and what it does NOT do. Let the user decide.
|
||||
|
||||
## Domain References
|
||||
|
||||
- `references/access.md` — webhook setup, OAuth, install callbacks.
|
||||
- `references/troubleshooting.md` — diagnostics and self-repair.
|
||||
- `references/mcp-workflow.md` — MCP tool selection and query patterns.
|
||||
- `references/crm.md` — deals, contacts, leads, companies, activities.
|
||||
- `references/smartprocess.md` — smart processes, funnels, stages, universal crm.item API.
|
||||
- `references/products.md` — product catalog, product rows on deals/quotes/invoices.
|
||||
- `references/quotes.md` — quotes (commercial proposals), smart invoices.
|
||||
- `references/tasks.md` — tasks, checklists, comments, planner.
|
||||
- `references/chat.md` — im, imbot, notifications, dialog history.
|
||||
- `references/channels.md` — channels (каналы), announcements, subscribers, broadcast messaging.
|
||||
- `references/openlines.md` — open lines (открытые линии), omnichannel customer communication, operators, sessions.
|
||||
- `references/calendar.md` — sections, events, attendees, availability.
|
||||
- `references/drive.md` — storage, folders, files, external links.
|
||||
- `references/files.md` — file uploads: base64 inline for CRM, disk+attach for tasks.
|
||||
- `references/users.md` — users, departments, org-structure, subordinates.
|
||||
- `references/projects.md` — workgroups, projects, scrum, membership.
|
||||
- `references/feed.md` — activity stream, feed posts, comments.
|
||||
- `references/timeman.md` — time tracking, work day, absence reports, task time.
|
||||
- `references/sites.md` — landing pages, sites, blocks, publishing.
|
||||
|
||||
Read only the reference file that matches the current task.
|
||||
|
||||
Note: Bitrix24 has no REST API for reading or sending emails. `mailservice.*` only configures SMTP/IMAP services.
|
||||
6
bundled-skills/bitrix24/_meta.json
Normal file
6
bundled-skills/bitrix24/_meta.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"ownerId": "kn7f9mnerv85jphv00w8303ms981kgvq",
|
||||
"slug": "bitrix24",
|
||||
"version": "0.15.5",
|
||||
"publishedAt": 1773265717446
|
||||
}
|
||||
116
bundled-skills/bitrix24/references/access.md
Normal file
116
bundled-skills/bitrix24/references/access.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# Access and Auth
|
||||
|
||||
## Webhook Setup
|
||||
|
||||
1. In Bitrix24 open `Developer resources -> Other -> Inbound webhook`.
|
||||
2. Create a webhook and copy its URL.
|
||||
3. Save it:
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py user.current --url "<webhook>" --json
|
||||
```
|
||||
|
||||
This saves the webhook to `~/.config/bitrix24-skill/config.json` and verifies it works in one step.
|
||||
|
||||
Expected format:
|
||||
|
||||
```text
|
||||
https://your-portal.bitrix24.ru/rest/<user_id>/<webhook>/
|
||||
```
|
||||
|
||||
After that, the skill reuses the saved webhook automatically for all calls.
|
||||
|
||||
To replace an existing webhook:
|
||||
|
||||
```bash
|
||||
python3 scripts/save_webhook.py --url "<new-webhook>" --force --check
|
||||
```
|
||||
|
||||
## Agent Setup Behavior
|
||||
|
||||
When a user asks for setup help or a REST call fails:
|
||||
|
||||
1. Check saved config with `scripts/check_webhook.py --json`
|
||||
2. If the user already shared a webhook in the conversation, save it and retry
|
||||
3. Only ask the user for a webhook if no saved config exists
|
||||
|
||||
Mask the webhook secret in user-facing output.
|
||||
|
||||
## Permissions
|
||||
|
||||
Grant the permission groups that match the methods you will call.
|
||||
|
||||
Recommended full-coverage set:
|
||||
|
||||
- CRM
|
||||
- Tasks
|
||||
- Calendar
|
||||
- Disk or Drive
|
||||
- IM or Chat
|
||||
- User and department access
|
||||
|
||||
## `CLIENT_ID` For Bot Integrations
|
||||
|
||||
For `imbot` integrations, Bitrix24 bot registration requires `CLIENT_ID`.
|
||||
|
||||
- Provide `CLIENT_ID` when registering the bot
|
||||
- Persist it as part of the bot credentials
|
||||
- Pass the same `CLIENT_ID` into all later `imbot.*` calls
|
||||
- Treat `CLIENT_ID` as a secret
|
||||
|
||||
## Official MCP Docs Endpoint
|
||||
|
||||
```text
|
||||
https://mcp-dev.bitrix24.tech/mcp
|
||||
```
|
||||
|
||||
Tools exposed by the server:
|
||||
|
||||
- `bitrix-search`
|
||||
- `bitrix-app-development-doc-details`
|
||||
- `bitrix-method-details`
|
||||
- `bitrix-article-details`
|
||||
- `bitrix-event-details`
|
||||
|
||||
## When To Use OAuth Instead Of A Webhook
|
||||
|
||||
Use a webhook when:
|
||||
|
||||
- you are connecting one portal quickly
|
||||
- the integration is admin-managed
|
||||
- you want the shortest setup path
|
||||
|
||||
Use OAuth when:
|
||||
|
||||
- your service lives outside Bitrix24
|
||||
- users connect their own portals to your service
|
||||
- you need renewable tokens instead of a fixed webhook secret
|
||||
|
||||
Key official docs:
|
||||
|
||||
- Full OAuth: `https://apidocs.bitrix24.ru/settings/oauth/index.html`
|
||||
- REST call overview: `https://apidocs.bitrix24.ru/sdk/bx24-js-sdk/how-to-call-rest-methods/index.html`
|
||||
- Install callback: `https://apidocs.bitrix24.ru/settings/app-installation/mass-market-apps/installation-callback.html`
|
||||
|
||||
## OAuth Facts From MCP Docs
|
||||
|
||||
- Authorization server: `https://oauth.bitrix24.tech/`
|
||||
- Authorization starts at `https://portal.bitrix24.com/oauth/authorize/`
|
||||
- Temporary authorization `code` is valid for 30 seconds
|
||||
- Token exchange at `https://oauth.bitrix24.tech/oauth/token/`
|
||||
- Returns `access_token`, `refresh_token`, `client_endpoint`, `server_endpoint`, `scope`
|
||||
|
||||
Useful MCP titles for auth topics:
|
||||
|
||||
- `Полный протокол авторизации OAuth 2.0`
|
||||
- `Упрощенный вариант получения токенов OAuth 2.0`
|
||||
- `Вызов методов REST`
|
||||
- `Callback установки`
|
||||
|
||||
## Install Callback For UI-Less Apps
|
||||
|
||||
If you build a local or UI-less app, Bitrix24 can POST OAuth credentials to an install callback URL. That flow is documented in `Callback установки`.
|
||||
|
||||
- Save the received `access_token` and `refresh_token`
|
||||
- Refresh access tokens on your backend
|
||||
- Do not rely on browser-side JS install helpers for the callback flow
|
||||
107
bundled-skills/bitrix24/references/calendar.md
Normal file
107
bundled-skills/bitrix24/references/calendar.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# Calendar
|
||||
|
||||
Use this file for personal calendars, group calendars, meetings, sections, recurrence, and availability checks.
|
||||
|
||||
## Core Methods
|
||||
|
||||
- `calendar.event.get` — list events in a date range (requires `type` and `ownerId`)
|
||||
- `calendar.event.get.nearest` — list upcoming events (simplest way to get schedule)
|
||||
- `calendar.event.getbyid` — get one event by ID
|
||||
- `calendar.event.add` — create event
|
||||
- `calendar.event.update` — update event
|
||||
- `calendar.event.delete` — delete event
|
||||
- `calendar.section.get` — list calendars (sections)
|
||||
- `calendar.section.add` — create calendar
|
||||
- `calendar.accessibility.get` — check user availability
|
||||
- `calendar.meeting.status.get` — get current user's meeting participation status
|
||||
- `calendar.resource.list` — list calendar resources
|
||||
|
||||
There is NO `calendar.get` or `calendar.list` method. Always use the full method names above.
|
||||
|
||||
## Critical: Required Parameters
|
||||
|
||||
`calendar.event.get` requires two mandatory parameters:
|
||||
|
||||
- `type` — one of: `user`, `group`, `company_calendar`
|
||||
- `ownerId` — user ID for `user` type, group ID for `group`, `0` for `company_calendar`
|
||||
|
||||
Without these, the call fails with `ERROR_METHOD_NOT_FOUND` or similar.
|
||||
|
||||
`from` and `to` use date format `YYYY-MM-DD` (not datetime). Defaults: `from` = 1 month ago, `to` = 3 months ahead.
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### Show schedule via batch (preferred — one HTTP call)
|
||||
|
||||
Combine calendar + tasks in one request:
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_batch.py \
|
||||
--cmd 'events=calendar.event.get?type=user&ownerId=1&from=2026-03-11&to=2026-03-11' \
|
||||
--cmd 'tasks=tasks.task.list?filter[RESPONSIBLE_ID]=1&filter[>=DEADLINE]=2026-03-11T00:00:00&filter[<=DEADLINE]=2026-03-11T23:59:59&select[]=ID&select[]=TITLE&select[]=DEADLINE&select[]=STATUS' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Show user's schedule for a specific day
|
||||
|
||||
Get user ID from cached config or `user.current`, then query events:
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py user.current --json
|
||||
python3 scripts/bitrix24_call.py calendar.event.get \
|
||||
--param 'type=user' \
|
||||
--param 'ownerId=1' \
|
||||
--param 'from=2026-03-10' \
|
||||
--param 'to=2026-03-10' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Show upcoming events (easiest for "what's on my schedule")
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py calendar.event.get.nearest \
|
||||
--param 'type=user' \
|
||||
--param 'ownerId=1' \
|
||||
--param 'days=7' \
|
||||
--param 'forCurrentUser=true' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Check availability before scheduling
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py calendar.accessibility.get \
|
||||
--param 'users[]=1' \
|
||||
--param 'users[]=2' \
|
||||
--param 'from=2026-03-10' \
|
||||
--param 'to=2026-03-11' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Create an event
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py calendar.event.add \
|
||||
--param 'type=user' \
|
||||
--param 'ownerId=1' \
|
||||
--param 'name=Team Meeting' \
|
||||
--param 'from=2026-03-10T10:00:00' \
|
||||
--param 'to=2026-03-10T11:00:00' \
|
||||
--json
|
||||
```
|
||||
|
||||
## Working Rules
|
||||
|
||||
- Always pass `type` and `ownerId` for `calendar.event.get`.
|
||||
- Use `calendar.event.get.nearest` for "show my schedule" — it needs fewer parameters.
|
||||
- Get user ID from `user.current` first if you don't know it.
|
||||
- Check `calendar.accessibility.get` before proposing meeting slots.
|
||||
- For read-only requests, execute immediately — do not ask permission.
|
||||
- One retry on first failure, then report blocker.
|
||||
|
||||
## Good MCP Queries
|
||||
|
||||
- `calendar event get nearest`
|
||||
- `calendar accessibility`
|
||||
- `calendar section`
|
||||
- `calendar resource booking`
|
||||
202
bundled-skills/bitrix24/references/channels.md
Normal file
202
bundled-skills/bitrix24/references/channels.md
Normal file
@@ -0,0 +1,202 @@
|
||||
# Channels (Каналы)
|
||||
|
||||
Use this file for Bitrix24 channels — broadcast-style chats where only owners/managers post and subscribers read.
|
||||
|
||||
Channels are a special chat type (`type: openChannel`, `ENTITY_TYPE: ANNOUNCEMENT`).
|
||||
They use standard `im.*` methods — there is no separate `im.channel.*` API.
|
||||
|
||||
## How Channels Differ From Chats
|
||||
|
||||
| Feature | Chat | Channel |
|
||||
|---------|------|---------|
|
||||
| Who can post | All members | Only owner + managers |
|
||||
| `type` in API | `chat` / `open` | `openChannel` |
|
||||
| Create with | `TYPE=CHAT` or `TYPE=OPEN` | `ENTITY_TYPE=ANNOUNCEMENT` |
|
||||
| List only these | `SKIP_CHAT=N` | `ONLY_CHANNEL=Y` |
|
||||
| Join | By invite | Self-subscribe (open) or invite |
|
||||
|
||||
## Core Methods
|
||||
|
||||
All use the same `im.*` family:
|
||||
|
||||
Create & manage:
|
||||
|
||||
- `im.chat.add` — create channel (with `ENTITY_TYPE=ANNOUNCEMENT`)
|
||||
- `im.chat.updateTitle` — rename channel
|
||||
- `im.chat.setOwner` — transfer ownership
|
||||
- `im.chat.mute` — mute/unmute notifications
|
||||
|
||||
List & find:
|
||||
|
||||
- `im.recent.list` — list subscribed channels (`ONLY_CHANNEL=Y`)
|
||||
- `im.dialog.get` — get channel info (returns `type: openChannel`)
|
||||
- `im.counters.get` — unread counters (includes channel counters)
|
||||
|
||||
Messages:
|
||||
|
||||
- `im.message.add` — post to channel (owner/managers only)
|
||||
- `im.dialog.messages.get` — read channel history
|
||||
- `im.dialog.messages.search` — search messages in channel
|
||||
|
||||
Subscribers:
|
||||
|
||||
- `im.chat.user.add` — add subscribers
|
||||
- `im.chat.user.delete` — remove subscribers
|
||||
- `im.chat.user.list` — list subscribers
|
||||
- `im.chat.leave` — unsubscribe (current user leaves)
|
||||
|
||||
Pin & hide:
|
||||
|
||||
- `im.recent.pin` — pin channel at top of list
|
||||
- `im.recent.hide` — hide channel from recent list
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### Create a channel
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py im.chat.add \
|
||||
--param 'TYPE=OPEN' \
|
||||
--param 'ENTITY_TYPE=ANNOUNCEMENT' \
|
||||
--param 'TITLE=Company News' \
|
||||
--param 'DESCRIPTION=Official company announcements' \
|
||||
--param 'USERS[]=1' \
|
||||
--param 'USERS[]=2' \
|
||||
--param 'MESSAGE=Welcome to the channel!' \
|
||||
--json
|
||||
```
|
||||
|
||||
Returns channel chat ID. Creator becomes the owner.
|
||||
|
||||
### List all channels
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py im.recent.list \
|
||||
--param 'ONLY_CHANNEL=Y' \
|
||||
--param 'LIMIT=50' \
|
||||
--json
|
||||
```
|
||||
|
||||
Filter channels with `ONLY_CHANNEL=Y`. Each result contains `type: openChannel`.
|
||||
|
||||
### Post to a channel
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py im.message.add \
|
||||
--param 'DIALOG_ID=chat42' \
|
||||
--param 'MESSAGE=Important update: new office hours starting Monday' \
|
||||
--json
|
||||
```
|
||||
|
||||
Only the channel owner and managers can post. Regular subscribers get `ACCESS_ERROR`.
|
||||
|
||||
### Read channel messages
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py im.dialog.messages.get \
|
||||
--param 'DIALOG_ID=chat42' \
|
||||
--param 'LIMIT=20' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Get channel info
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py im.dialog.get \
|
||||
--param 'DIALOG_ID=chat42' \
|
||||
--json
|
||||
```
|
||||
|
||||
Returns object with `type: openChannel`, owner, name, description, subscriber count.
|
||||
|
||||
### Add subscribers to channel
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py im.chat.user.add \
|
||||
--param 'CHAT_ID=42' \
|
||||
--param 'USERS[]=5' \
|
||||
--param 'USERS[]=6' \
|
||||
--param 'USERS[]=7' \
|
||||
--json
|
||||
```
|
||||
|
||||
### List channel subscribers
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py im.chat.user.list \
|
||||
--param 'CHAT_ID=42' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Unsubscribe from channel
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py im.chat.leave \
|
||||
--param 'CHAT_ID=42' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Rename a channel
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py im.chat.updateTitle \
|
||||
--param 'CHAT_ID=42' \
|
||||
--param 'TITLE=Company Updates 2026' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Mute channel notifications
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py im.chat.mute \
|
||||
--param 'CHAT_ID=42' \
|
||||
--param 'MUTE=Y' \
|
||||
--json
|
||||
```
|
||||
|
||||
`MUTE=Y` to mute, `MUTE=N` to unmute.
|
||||
|
||||
### Pin channel at top of chat list
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py im.recent.pin \
|
||||
--param 'DIALOG_ID=chat42' \
|
||||
--param 'PIN=Y' \
|
||||
--json
|
||||
```
|
||||
|
||||
## Working Rules
|
||||
|
||||
- Channels have NO separate API — use `im.*` methods with `ENTITY_TYPE=ANNOUNCEMENT`.
|
||||
- Identify channels by `type: openChannel` in responses from `im.dialog.get` or `im.recent.list`.
|
||||
- Only owner and managers can post (`im.message.add`). Subscribers get `ACCESS_ERROR`.
|
||||
- To make someone a manager, use `im.chat.setOwner` (transfers ownership) — there is no separate "set manager" method via REST.
|
||||
- Use `ONLY_CHANNEL=Y` in `im.recent.list` to filter channels from regular chats.
|
||||
- Channel `DIALOG_ID` format: `chatXXX` (same as regular group chats).
|
||||
- Subscribers can leave with `im.chat.leave`, but cannot be re-added without owner action.
|
||||
- Channel types in API: `openChannel` (public), `channel` (private), `generalChannel` (company-wide).
|
||||
|
||||
## Known Limitations
|
||||
|
||||
### Comments and threads
|
||||
|
||||
Comments on channel posts are a UI feature not exposed via REST API. When reading messages with `im.dialog.messages.get`, no thread/comment fields are returned (`REPLY_ID`, `THREAD_ID`, `parent_message_id` — none of these are present). `im.message.add` accepts `REPLY_ID` for sending a reply, but replies are not retrievable via API. If Bitrix24 adds REST methods for threads, we can use them immediately.
|
||||
|
||||
### Channel discovery
|
||||
|
||||
`im.recent.list` with `ONLY_CHANNEL=Y` returns only channels the current user is **subscribed to**. There is no REST API method to discover channels the user is NOT subscribed to (new or available channels on the portal).
|
||||
|
||||
`im.search.chat.list` does NOT search channels — it only returns regular chats (`chat`, `open`, `calendar`, `tasks`, `sonetGroup` types). Searching for a known channel name returns zero channel results.
|
||||
|
||||
To discover new channels, users must use the Bitrix24 UI. Once subscribed, the channel appears in `im.recent.list`.
|
||||
|
||||
These limitations are current as of March 2026. Check the MCP documentation server (`https://mcp-dev.bitrix24.tech/mcp`) for updates — use `bitrix-search` with queries like `im thread`, `im channel list` to see if new methods have appeared.
|
||||
|
||||
## Good MCP Queries
|
||||
|
||||
- `im chat add`
|
||||
- `im recent list`
|
||||
- `im dialog get`
|
||||
- `im chat user add`
|
||||
- `im chat mute`
|
||||
- `im recent pin`
|
||||
140
bundled-skills/bitrix24/references/chat.md
Normal file
140
bundled-skills/bitrix24/references/chat.md
Normal file
@@ -0,0 +1,140 @@
|
||||
# Chat and Notifications
|
||||
|
||||
Use this file for messenger dialogs, chats, history, notifications, and file delivery into chats.
|
||||
|
||||
> **Channels** (каналы / объявления) — see `references/channels.md`. Channels use the same `im.*` methods but with `ENTITY_TYPE=ANNOUNCEMENT` and `type: openChannel`.
|
||||
|
||||
## Separate `im.*` From `imbot.*`
|
||||
|
||||
Use `im.*` for normal IM REST methods (webhook-compatible):
|
||||
|
||||
- `im.message.add` — send message
|
||||
- `im.message.update` / `im.message.delete`
|
||||
- `im.message.share` — create entity (task/event/post) from a message
|
||||
- `im.chat.add` / `im.chat.get` / `im.chat.update`
|
||||
- `im.chat.user.add` / `im.chat.user.delete` / `im.chat.user.list`
|
||||
- `im.dialog.get` / `im.dialog.messages.get`
|
||||
- `im.dialog.messages.search` — search messages in a specific chat
|
||||
- `im.dialog.users.list` — list dialog participants
|
||||
- `im.dialog.read.all` — mark all chats as read
|
||||
- `im.recent.list` / `im.recent.get`
|
||||
- `im.dialog.writing` — typing indicator
|
||||
|
||||
Use `imbot.*` for bot scenarios (requires `CLIENT_ID`):
|
||||
|
||||
- `imbot.message.add` / `imbot.message.update` / `imbot.message.delete`
|
||||
- `imbot.chat.add` / `imbot.dialog.get`
|
||||
- `imbot.chat.sendTyping`
|
||||
|
||||
Do not mix `im.*` and `imbot.*` — pick the family that matches the integration.
|
||||
|
||||
## Notifications
|
||||
|
||||
- `im.notify.system.add` — system notification (app context only)
|
||||
- `im.notify.personal.add` — personal notification (app context only)
|
||||
- `im.notify.read` — mark notification as read
|
||||
|
||||
Important: `im.notify.system.add` and `im.notify.personal.add` work only through an application, not plain webhooks. If you get auth errors, this is likely the reason.
|
||||
|
||||
## Dialog Addressing
|
||||
|
||||
- `123` — direct dialog with user 123
|
||||
- `chat456` — group chat 456
|
||||
- `sg789` — group or project chat
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### Send a message to a chat
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py im.message.add \
|
||||
--param 'DIALOG_ID=chat42' \
|
||||
--param 'MESSAGE=Hello team' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Read dialog history
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py im.dialog.messages.get \
|
||||
--param 'DIALOG_ID=chat42' \
|
||||
--param 'LIMIT=20' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Send a Disk file to chat
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py im.disk.file.commit \
|
||||
--param 'CHAT_ID=42' \
|
||||
--param 'FILE_ID[]=5249' \
|
||||
--param 'MESSAGE=Project files' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Create a group chat
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py im.chat.add \
|
||||
--param 'TYPE=CHAT' \
|
||||
--param 'TITLE=Project discussion' \
|
||||
--param 'USERS[]=1' \
|
||||
--param 'USERS[]=2' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Search messages in a chat
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py im.dialog.messages.search \
|
||||
--param 'CHAT_ID=42' \
|
||||
--param 'SEARCH_MESSAGE=contract' \
|
||||
--param 'LIMIT=20' \
|
||||
--json
|
||||
```
|
||||
|
||||
Supports date filters: `DATE_FROM`, `DATE_TO` (ISO 8601), `DATE` (single day).
|
||||
Search string must be longer than 2 characters. Returns messages, users, and files.
|
||||
|
||||
### Create task from a chat message
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py im.message.share \
|
||||
--param 'MESSAGE_ID=34261' \
|
||||
--param 'DIALOG_ID=chat42' \
|
||||
--param 'TYPE=TASK' \
|
||||
--json
|
||||
```
|
||||
|
||||
`TYPE` values: `TASK` (task), `CALEND` (calendar event), `POST` (feed post), `CHAT` (forward to chat).
|
||||
Get `MESSAGE_ID` from `im.dialog.messages.get` or `im.dialog.messages.search`.
|
||||
|
||||
### Mark all chats as read
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py im.dialog.read.all --json
|
||||
```
|
||||
|
||||
No parameters needed. Marks all dialogs as read for the current user.
|
||||
|
||||
## `CLIENT_ID` for Bots
|
||||
|
||||
For `imbot.*` methods:
|
||||
|
||||
- Provide `CLIENT_ID` when registering the bot
|
||||
- Persist it and reuse in every `imbot.*` call
|
||||
- Treat `CLIENT_ID` as a secret
|
||||
|
||||
## Formatting
|
||||
|
||||
Bitrix24 chat uses BB-code. Do not double-convert if Markdown is already converted to BB-code.
|
||||
|
||||
## Good MCP Queries
|
||||
|
||||
- `im message add chat`
|
||||
- `im message share`
|
||||
- `im dialog messages search`
|
||||
- `im dialog read all`
|
||||
- `imbot message`
|
||||
- `im dialog messages get`
|
||||
- `im disk file commit`
|
||||
228
bundled-skills/bitrix24/references/crm.md
Normal file
228
bundled-skills/bitrix24/references/crm.md
Normal file
@@ -0,0 +1,228 @@
|
||||
# CRM
|
||||
|
||||
Use this file for deals, contacts, companies, leads, activities, and modern CRM item APIs.
|
||||
|
||||
## Core Methods
|
||||
|
||||
Deals:
|
||||
|
||||
- `crm.deal.list` / `crm.deal.get` / `crm.deal.add` / `crm.deal.update` / `crm.deal.delete`
|
||||
- `crm.deal.fields` — field schema
|
||||
- `crm.deal.contact.add` / `crm.deal.contact.items.get`
|
||||
|
||||
Contacts:
|
||||
|
||||
- `crm.contact.list` / `crm.contact.get` / `crm.contact.add` / `crm.contact.update` / `crm.contact.delete`
|
||||
- `crm.contact.fields`
|
||||
|
||||
Companies:
|
||||
|
||||
- `crm.company.list` / `crm.company.get` / `crm.company.add` / `crm.company.update` / `crm.company.delete`
|
||||
|
||||
Leads:
|
||||
|
||||
- `crm.lead.list` / `crm.lead.get` / `crm.lead.add` / `crm.lead.update` / `crm.lead.delete`
|
||||
- `crm.lead.fields`
|
||||
|
||||
Activities (classic):
|
||||
|
||||
- `crm.activity.list` / `crm.activity.add` / `crm.activity.update` / `crm.activity.delete`
|
||||
|
||||
Timeline — Todo (universal activities in deal/lead/contact timeline):
|
||||
|
||||
- `crm.activity.todo.add` — create a todo item in timeline
|
||||
- `crm.activity.todo.update` — update todo
|
||||
- `crm.activity.todo.updateDeadline` — change deadline only
|
||||
- `crm.activity.todo.updateDescription` — change description only
|
||||
|
||||
Timeline — Comments & Log:
|
||||
|
||||
- `crm.timeline.comment.add` — add comment to entity timeline
|
||||
- `crm.timeline.comment.update` — edit existing comment
|
||||
- `crm.timeline.logmessage.add` — add log entry to timeline (for recording events)
|
||||
|
||||
CRM Feed:
|
||||
|
||||
- `crm.livefeedmessage.add` — post message to CRM activity stream
|
||||
|
||||
Stage History:
|
||||
|
||||
- `crm.stagehistory.list` — history of stage transitions (deals, leads, invoices)
|
||||
|
||||
Modern generalized APIs (smart processes, dynamic types):
|
||||
|
||||
- `crm.item.list` / `crm.item.add` / `crm.item.update` / `crm.item.delete`
|
||||
- `crm.item.batchImport`
|
||||
|
||||
## Filter Syntax
|
||||
|
||||
CRM list methods use prefix operators:
|
||||
|
||||
- `>OPPORTUNITY` — greater than
|
||||
- `>=DATE_CREATE` — on or after
|
||||
- `=STAGE_ID` — equals (default without prefix)
|
||||
- `!STATUS_ID` — not equal
|
||||
|
||||
Example: `filter[>OPPORTUNITY]=10000` returns deals with opportunity above 10000.
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### List deals with filter
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.deal.list \
|
||||
--param 'filter[>OPPORTUNITY]=10000' \
|
||||
--param 'select[]=ID' \
|
||||
--param 'select[]=TITLE' \
|
||||
--param 'select[]=OPPORTUNITY' \
|
||||
--param 'select[]=STAGE_ID' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Create a deal
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.deal.add \
|
||||
--param 'fields[TITLE]=New Deal' \
|
||||
--param 'fields[OPPORTUNITY]=50000' \
|
||||
--param 'fields[CURRENCY_ID]=RUB' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Get field schema before writing
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.deal.fields --json
|
||||
```
|
||||
|
||||
### Add activity to a deal
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.activity.add \
|
||||
--param 'fields[OWNER_TYPE_ID]=2' \
|
||||
--param 'fields[OWNER_ID]=123' \
|
||||
--param 'fields[TYPE_ID]=2' \
|
||||
--param 'fields[SUBJECT]=Follow-up call' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Stuck deals (no activity for 14+ days)
|
||||
|
||||
Deals in active pipeline with no recent modification — useful for proactive "💤" warnings:
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.deal.list \
|
||||
--param 'filter[ASSIGNED_BY_ID]=1' \
|
||||
--param 'filter[STAGE_SEMANTIC_ID]=P' \
|
||||
--param 'filter[<DATE_MODIFY]=2026-02-22' \
|
||||
--param 'select[]=ID' \
|
||||
--param 'select[]=TITLE' \
|
||||
--param 'select[]=STAGE_ID' \
|
||||
--param 'select[]=DATE_MODIFY' \
|
||||
--param 'select[]=OPPORTUNITY' \
|
||||
--json
|
||||
```
|
||||
|
||||
`STAGE_SEMANTIC_ID=P` = in progress (active pipeline). `<DATE_MODIFY` = last modified before 14 days ago.
|
||||
|
||||
### Add todo to a deal timeline
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.activity.todo.add \
|
||||
--param 'ownerTypeId=2' \
|
||||
--param 'ownerId=123' \
|
||||
--param 'deadline=2026-03-15T15:00:00' \
|
||||
--param 'title=Follow up with client' \
|
||||
--param 'description=Call to discuss proposal' \
|
||||
--param 'responsibleId=5' \
|
||||
--json
|
||||
```
|
||||
|
||||
`ownerTypeId`: 1=lead, 2=deal, 3=contact, 4=company.
|
||||
Optional `pingOffsets` (array of minutes for reminders): `--param 'pingOffsets[]=0' --param 'pingOffsets[]=15'`
|
||||
|
||||
### Add comment to deal timeline
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.timeline.comment.add \
|
||||
--param 'fields[ENTITY_ID]=123' \
|
||||
--param 'fields[ENTITY_TYPE]=deal' \
|
||||
--param 'fields[COMMENT]=Client confirmed budget approval' \
|
||||
--json
|
||||
```
|
||||
|
||||
`ENTITY_TYPE` values: `deal`, `lead`, `contact`, `company`, `order`, `quote` (string, lowercase).
|
||||
|
||||
### Add log entry to timeline
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.timeline.logmessage.add \
|
||||
--param 'fields[entityTypeId]=2' \
|
||||
--param 'fields[entityId]=123' \
|
||||
--param 'fields[title]=Price changed' \
|
||||
--param 'fields[text]=Price updated from 100k to 120k after negotiation' \
|
||||
--param 'fields[iconCode]=info' \
|
||||
--json
|
||||
```
|
||||
|
||||
Note: `crm.timeline.logmessage.add` uses camelCase field names (`entityTypeId`), not UPPER_CASE.
|
||||
|
||||
### Post message to CRM feed
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.livefeedmessage.add \
|
||||
--param 'fields[POST_TITLE]=Deal update' \
|
||||
--param 'fields[MESSAGE]=Contract signed with Company X' \
|
||||
--param 'fields[ENTITYTYPEID]=2' \
|
||||
--param 'fields[ENTITYID]=123' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Get stage history for deals
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.stagehistory.list \
|
||||
--param 'entityTypeId=2' \
|
||||
--param 'filter[>=CREATED_TIME]=2026-03-01T00:00:00' \
|
||||
--param 'select[]=OWNER_ID' \
|
||||
--param 'select[]=STAGE_ID' \
|
||||
--param 'select[]=CREATED_TIME' \
|
||||
--json
|
||||
```
|
||||
|
||||
Returns items with `TYPE_ID`: 1=created, 2=intermediate stage, 3=final stage, 5=pipeline change.
|
||||
`STAGE_SEMANTIC_ID`: P=in progress, S=won, F=lost.
|
||||
|
||||
## Entity Type IDs
|
||||
|
||||
| ID | Entity |
|
||||
|----|--------|
|
||||
| 1 | Lead |
|
||||
| 2 | Deal |
|
||||
| 3 | Contact |
|
||||
| 4 | Company |
|
||||
| 5 | Invoice (old) |
|
||||
| 7 | Quote |
|
||||
| 31 | Smart Invoice (new) |
|
||||
| 128+ | Custom smart processes |
|
||||
|
||||
## Working Rules
|
||||
|
||||
- Read `*.fields` before writing custom or portal-specific fields.
|
||||
- Do not hardcode stage names across portals — pipelines and categories vary.
|
||||
- Use classic `crm.deal.*` for built-in entities, `crm.item.*` for smart processes.
|
||||
- Always use `select[]` to limit response size.
|
||||
- Pagination: page size is 50, use `start=0`, `start=50`, etc.
|
||||
|
||||
## Good MCP Queries
|
||||
|
||||
- `crm deal list add update`
|
||||
- `crm contact company`
|
||||
- `crm lead fields`
|
||||
- `crm activity`
|
||||
- `crm activity todo add`
|
||||
- `crm timeline comment`
|
||||
- `crm timeline logmessage`
|
||||
- `crm livefeedmessage`
|
||||
- `crm stagehistory`
|
||||
- `crm item smart process`
|
||||
118
bundled-skills/bitrix24/references/drive.md
Normal file
118
bundled-skills/bitrix24/references/drive.md
Normal file
@@ -0,0 +1,118 @@
|
||||
# Drive and Disk
|
||||
|
||||
Use this file for storage, folder, file, and external-link operations.
|
||||
|
||||
## Core Methods
|
||||
|
||||
Storages:
|
||||
|
||||
- `disk.storage.getlist` — list all storages
|
||||
- `disk.storage.get` — get storage by ID
|
||||
- `disk.storage.getchildren` — list root contents
|
||||
- `disk.storage.addfolder` — create folder in root
|
||||
- `disk.storage.uploadfile` — upload file to root
|
||||
|
||||
Folders:
|
||||
|
||||
- `disk.folder.get` — folder metadata
|
||||
- `disk.folder.getchildren` — list folder contents
|
||||
- `disk.folder.addsubfolder` — create subfolder
|
||||
- `disk.folder.uploadfile` — upload file to folder
|
||||
- `disk.folder.getexternallink` — public link
|
||||
- `disk.folder.moveto` — move folder
|
||||
- `disk.folder.deletetree` — delete folder permanently
|
||||
|
||||
Files:
|
||||
|
||||
- `disk.file.get` — file metadata (includes `DOWNLOAD_URL`)
|
||||
- `disk.file.copyto` — copy to folder
|
||||
- `disk.file.moveto` — move to folder
|
||||
- `disk.file.rename` — rename file
|
||||
- `disk.file.uploadversion` — upload new version
|
||||
- `disk.file.delete` — delete permanently
|
||||
|
||||
Attached objects (files linked to tasks, chats, CRM entities):
|
||||
|
||||
- `disk.attachedObject.get` — get info about an attached file (from task, chat, etc.)
|
||||
|
||||
Chat handoff:
|
||||
|
||||
- `im.disk.file.commit` — send existing Disk file to chat
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### List all storages
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py disk.storage.getlist --json
|
||||
```
|
||||
|
||||
### Browse storage contents
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py disk.storage.getchildren \
|
||||
--param 'id=1' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Browse folder contents
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py disk.folder.getchildren \
|
||||
--param 'id=42' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Get file metadata
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py disk.file.get \
|
||||
--param 'id=9043' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Upload file to a folder
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py disk.folder.uploadfile \
|
||||
--param 'id=42' \
|
||||
--param 'data[NAME]=document.txt' \
|
||||
--param 'fileContent[0]=document.txt' \
|
||||
--param 'fileContent[1]=<base64_content>' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Get public link for a folder
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py disk.folder.getexternallink \
|
||||
--param 'id=42' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Get attached file info (from task, chat, etc.)
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py disk.attachedObject.get \
|
||||
--param 'id=495' \
|
||||
--json
|
||||
```
|
||||
|
||||
Returns `OBJECT_ID` (disk file ID), `NAME`, `SIZE`, `DOWNLOAD_URL`, `MODULE_ID`, `ENTITY_TYPE`, `ENTITY_ID`.
|
||||
Get the attachment ID from methods like `tasks.task.get` (UF_TASK_WEBDAV_FILES) or chat message files.
|
||||
|
||||
## Working Rules
|
||||
|
||||
- Find file IDs from `disk.storage.getchildren` or `disk.folder.getchildren`.
|
||||
- Use `disk.file.get` to inspect metadata before download or move.
|
||||
- `DOWNLOAD_URL` requires authentication — it's not a public link.
|
||||
- Use `disk.folder.getexternallink` for sharing.
|
||||
- Use `disk.attachedObject.get` to get info about files attached to tasks or messages.
|
||||
|
||||
## Good MCP Queries
|
||||
|
||||
- `disk storage folder file`
|
||||
- `disk file upload version`
|
||||
- `disk external link`
|
||||
- `disk attachedObject get`
|
||||
- `im disk file commit`
|
||||
112
bundled-skills/bitrix24/references/feed.md
Normal file
112
bundled-skills/bitrix24/references/feed.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# Activity Stream (Feed)
|
||||
|
||||
Use this file for the Bitrix24 news feed — posting, reading, commenting, and sharing messages in the company feed.
|
||||
|
||||
Scope: `log`
|
||||
|
||||
## Core Methods
|
||||
|
||||
- `log.blogpost.get` — read feed posts (filter by `POST_ID` or `LOG_RIGHTS`)
|
||||
- `log.blogpost.add` — post a message to the feed
|
||||
- `log.blogpost.update` — update a feed post
|
||||
- `log.blogpost.share` — add recipients to an existing post
|
||||
- `log.blogcomment.add` — add comment to a feed post
|
||||
- `log.blogcomment.user.get` — get comments by user for a post
|
||||
|
||||
Also related (CRM-specific):
|
||||
|
||||
- `crm.livefeedmessage.add` — post to CRM entity feed
|
||||
|
||||
## Recipients (DEST / LOG_RIGHTS)
|
||||
|
||||
Feed messages use recipient codes:
|
||||
|
||||
- `UA` — all authorized users
|
||||
- `U<id>` — specific user (e.g. `U1`, `U42`)
|
||||
- `SG<id>` — workgroup/project (e.g. `SG15`)
|
||||
- `DR<id>` — department including subdepartments (e.g. `DR5`)
|
||||
|
||||
Default recipient: `['UA']` (everyone).
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### Read recent feed posts
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py log.blogpost.get --json
|
||||
```
|
||||
|
||||
### Read a specific post
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py log.blogpost.get \
|
||||
--param 'POST_ID=755' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Read posts visible to a specific group
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py log.blogpost.get \
|
||||
--param 'LOG_RIGHTS=SG15' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Post a message to the feed
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py log.blogpost.add \
|
||||
--param 'POST_MESSAGE=Hello team!' \
|
||||
--param 'DEST[]=UA' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Post to a specific department
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py log.blogpost.add \
|
||||
--param 'POST_TITLE=Department Update' \
|
||||
--param 'POST_MESSAGE=Monthly results are ready.' \
|
||||
--param 'DEST[]=DR5' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Post to a project group
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py log.blogpost.add \
|
||||
--param 'POST_MESSAGE=Sprint review tomorrow at 10:00' \
|
||||
--param 'DEST[]=SG15' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Add a comment
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py log.blogcomment.add \
|
||||
--param 'POST_ID=755' \
|
||||
--param 'TEXT=Great work!' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Share a post with additional recipients
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py log.blogpost.share \
|
||||
--param 'POST_ID=755' \
|
||||
--param 'DEST[]=U42' \
|
||||
--json
|
||||
```
|
||||
|
||||
## Working Rules
|
||||
|
||||
- `log.blogpost.get` without `POST_ID` returns recent posts for current user.
|
||||
- Posts are visible only to specified recipients. Use `DEST` to control visibility.
|
||||
- Comments inherit visibility from the parent post.
|
||||
- `IMPORTANT=Y` flag makes a post pinnable with an optional expiration date.
|
||||
|
||||
## Good MCP Queries
|
||||
|
||||
- `log blogpost add get`
|
||||
- `log blogcomment`
|
||||
- `crm livefeedmessage`
|
||||
116
bundled-skills/bitrix24/references/files.md
Normal file
116
bundled-skills/bitrix24/references/files.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# File Uploads
|
||||
|
||||
Bitrix24 REST API has two methods for uploading files. Which one to use depends on the entity.
|
||||
|
||||
## Method 1: Inline Base64 (CRM entities)
|
||||
|
||||
CRM entity fields (leads, deals, contacts, companies) accept files directly as `["filename", "base64_content"]`.
|
||||
|
||||
### Single file in a CRM field
|
||||
|
||||
```bash
|
||||
# Encode file to base64
|
||||
B64=$(python3 -c "import base64, sys; print(base64.b64encode(open(sys.argv[1],'rb').read()).decode())" /path/to/photo.jpg)
|
||||
|
||||
# Attach to contact
|
||||
python3 scripts/bitrix24_call.py crm.contact.update \
|
||||
--param 'ID=123' \
|
||||
--param "fields[PHOTO][0]=photo.jpg" \
|
||||
--param "fields[PHOTO][1]=$B64" \
|
||||
--confirm-write \
|
||||
--json
|
||||
```
|
||||
|
||||
### Multiple files in a user field
|
||||
|
||||
For UF (user field) that accepts multiple files, pass an array of `[name, base64]` pairs.
|
||||
|
||||
Use `--params-file` for this — it's cleaner than inline params:
|
||||
|
||||
```json
|
||||
{
|
||||
"ID": 123,
|
||||
"fields": {
|
||||
"UF_CRM_FILES": [
|
||||
["doc1.pdf", "<base64>"],
|
||||
["doc2.pdf", "<base64>"]
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.deal.update \
|
||||
--params-file /tmp/deal_files.json \
|
||||
--confirm-write \
|
||||
--json
|
||||
```
|
||||
|
||||
## Method 2: Disk Upload + Attach (Tasks and other entities)
|
||||
|
||||
Tasks do NOT accept base64 directly. Use the two-step process:
|
||||
|
||||
### Step 1: Upload file to Disk
|
||||
|
||||
First, find a folder to upload to:
|
||||
|
||||
```bash
|
||||
# List storage root (user's personal storage)
|
||||
python3 scripts/bitrix24_call.py disk.storage.getchildren \
|
||||
--param 'id=1' \
|
||||
--json
|
||||
```
|
||||
|
||||
Then upload:
|
||||
|
||||
```bash
|
||||
B64=$(python3 -c "import base64, sys; print(base64.b64encode(open(sys.argv[1],'rb').read()).decode())" /path/to/report.pdf)
|
||||
|
||||
python3 scripts/bitrix24_call.py disk.folder.uploadfile \
|
||||
--param 'id=FOLDER_ID' \
|
||||
--param 'fileContent[0]=report.pdf' \
|
||||
--param "fileContent[1]=$B64" \
|
||||
--param 'data[NAME]=report.pdf' \
|
||||
--confirm-write \
|
||||
--json
|
||||
```
|
||||
|
||||
The response contains the disk file object with `ID`.
|
||||
|
||||
### Step 2: Attach to task
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py tasks.task.files.attach \
|
||||
--param 'taskId=456' \
|
||||
--param 'fileId=DISK_FILE_ID' \
|
||||
--confirm-write \
|
||||
--json
|
||||
```
|
||||
|
||||
Note: the method is `tasks.task.files.attach` (with `s` in `files`), not `tasks.task.file.attach`.
|
||||
|
||||
## Chat file uploads
|
||||
|
||||
For sending files in chat, use the disk upload + `im.disk.file.commit` approach. See `references/chat.md` for details.
|
||||
|
||||
## Size limits
|
||||
|
||||
- Inline base64 in CRM fields: limited by POST request size (typically ~30 MB after encoding)
|
||||
- Disk uploads: subject to portal disk quota
|
||||
- Attachment objects in Open Lines: max 30 KB (metadata only, not file content)
|
||||
|
||||
## When to use which method
|
||||
|
||||
| Entity | Method | Notes |
|
||||
|--------|--------|-------|
|
||||
| CRM lead/deal/contact/company | Inline base64 | Pass `["name", "base64"]` in field |
|
||||
| Task | Disk + attach | `disk.folder.uploadfile` → `tasks.task.files.attach` |
|
||||
| Chat message | Disk + commit | `disk.folder.uploadfile` → `im.disk.file.commit` |
|
||||
| Feed post | Disk + attach | Upload first, reference disk file ID |
|
||||
|
||||
## Good MCP Queries
|
||||
|
||||
- `disk folder uploadfile`
|
||||
- `tasks task files attach`
|
||||
- `im disk file commit`
|
||||
- `crm contact photo`
|
||||
82
bundled-skills/bitrix24/references/mcp-workflow.md
Normal file
82
bundled-skills/bitrix24/references/mcp-workflow.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# MCP Workflow
|
||||
|
||||
Use MCP as the source of truth for Bitrix24 docs.
|
||||
|
||||
## Connection Facts
|
||||
|
||||
- Endpoint: `https://mcp-dev.bitrix24.tech/mcp`
|
||||
- This is a streamable MCP endpoint, not a regular documentation page
|
||||
- Without `Accept: text/event-stream`, the endpoint rejected requests with `406 Not Acceptable`
|
||||
- `initialize` reported:
|
||||
- protocol version `2025-03-26`
|
||||
- server `b24-dev-mcp`
|
||||
- version `0.2.0`
|
||||
|
||||
## Recommended Order
|
||||
|
||||
1. Search.
|
||||
2. Resolve the exact method, event, or article title.
|
||||
3. Fetch details.
|
||||
4. Only then make the REST call or write the example.
|
||||
|
||||
## Search Tool
|
||||
|
||||
Use `bitrix-search` first.
|
||||
|
||||
Helpful arguments:
|
||||
|
||||
- `query`: natural-language search text
|
||||
- `limit`: cap the number of matches
|
||||
- `doc_type`: narrow the result space
|
||||
|
||||
Supported `doc_type` values confirmed from MCP:
|
||||
|
||||
- `method`
|
||||
- `event`
|
||||
- `other`
|
||||
- `app_development_docs`
|
||||
|
||||
## Detail Tools
|
||||
|
||||
- `bitrix-method-details`: exact REST method name only
|
||||
- `bitrix-event-details`: exact event name only
|
||||
- `bitrix-article-details`: exact non-method article title
|
||||
- `bitrix-app-development-doc-details`: exact app-development title
|
||||
|
||||
## Practical Search Patterns
|
||||
|
||||
Use queries close to the user task:
|
||||
|
||||
- `crm deal add`
|
||||
- `task checklist`
|
||||
- `task comment`
|
||||
- `im message chat`
|
||||
- `calendar event section`
|
||||
- `disk storage folder file`
|
||||
- `user department structure`
|
||||
- `oauth install callback`
|
||||
|
||||
Then pass the exact name from search into the appropriate details tool.
|
||||
|
||||
## Patterns Confirmed During Analysis
|
||||
|
||||
The current MCP server returned strong matches for these families:
|
||||
|
||||
- CRM: `crm.deal.*`, `crm.contact.*`, `crm.company.*`, `crm.lead.*`, `crm.activity.*`, `crm.item.*`
|
||||
- Tasks: `tasks.task.*`, `task.checklistitem.*`, `task.commentitem.*`, `task.planner.getlist`
|
||||
- Chat: `im.message.*`, `im.chat.*`, `im.dialog.*`, `im.recent.*`, `im.notify.*`, `imbot.*`
|
||||
- Calendar: `calendar.event.*`, `calendar.section.*`, `calendar.accessibility.get`
|
||||
- Drive: `disk.storage.*`, `disk.folder.*`, `disk.file.*`, `im.disk.file.commit`
|
||||
- Users and org structure: `user.*`, `department.*`, `im.department.*`, `im.search.*`
|
||||
|
||||
## Avoid Stale Memory
|
||||
|
||||
Do not trust memory alone when:
|
||||
|
||||
- a method family is large
|
||||
- a scenario is scope-sensitive
|
||||
- a method may be deprecated
|
||||
- an example mixes `im.*` and `imbot.*`
|
||||
- a field name is portal-specific
|
||||
|
||||
Re-run `bitrix-method-details` in those cases.
|
||||
370
bundled-skills/bitrix24/references/openlines.md
Normal file
370
bundled-skills/bitrix24/references/openlines.md
Normal file
@@ -0,0 +1,370 @@
|
||||
# Open Lines (Открытые линии)
|
||||
|
||||
Use this file for Bitrix24 Open Lines — omnichannel customer communication (website chat, Telegram, WhatsApp, Facebook, VK, etc.). Open Lines connect external messaging channels to the portal and route incoming conversations to operators.
|
||||
|
||||
Scope: `imopenlines`. All methods require this scope in the webhook.
|
||||
|
||||
## Core Concepts
|
||||
|
||||
- **Line (config)** — an Open Line configuration: name, CRM binding, auto-actions, queue of operators
|
||||
- **Session** — one conversation between a customer and operator(s), from first message to close
|
||||
- **Operator** — a portal user who answers customer messages
|
||||
- **Dialog** — the chat between operator and customer within a session
|
||||
- **Connector** — external channel (Telegram, WhatsApp, Viber, Facebook, website widget, etc.)
|
||||
|
||||
## Line Management
|
||||
|
||||
### List all lines
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py imopenlines.config.list.get --json
|
||||
```
|
||||
|
||||
With filter and queue info:
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py imopenlines.config.list.get \
|
||||
--param 'PARAMS[filter][ACTIVE]=Y' \
|
||||
--param 'PARAMS[select][]=ID' \
|
||||
--param 'PARAMS[select][]=LINE_NAME' \
|
||||
--param 'PARAMS[select][]=ACTIVE' \
|
||||
--param 'PARAMS[select][]=CRM' \
|
||||
--param 'OPTIONS[QUEUE]=Y' \
|
||||
--json
|
||||
```
|
||||
|
||||
Supports pagination (`next`, `total`). Fields available for select/filter/order: see `imopenlines.config.add` docs.
|
||||
|
||||
### Get line by ID
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py imopenlines.config.get \
|
||||
--param 'CONFIG_ID=1' \
|
||||
--param 'WITH_QUEUE=Y' \
|
||||
--param 'SHOW_OFFLINE=Y' \
|
||||
--json
|
||||
```
|
||||
|
||||
- `CONFIG_ID` (required) — line ID
|
||||
- `WITH_QUEUE` — include operator queue (Y/N, default Y)
|
||||
- `SHOW_OFFLINE` — include offline operators (Y/N, default Y)
|
||||
|
||||
### Create a line
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py imopenlines.config.add \
|
||||
--param 'PARAMS[LINE_NAME]=Support' \
|
||||
--json
|
||||
```
|
||||
|
||||
Key fields in `PARAMS`: `LINE_NAME`, `CRM` (Y/N), `CRM_CREATE` (lead creation mode), `ACTIVE` (Y/N), and many others. Check MCP for full field list.
|
||||
|
||||
### Update a line
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py imopenlines.config.update \
|
||||
--param 'CONFIG_ID=1' \
|
||||
--param 'PARAMS[LINE_NAME]=New Name' \
|
||||
--param 'PARAMS[CRM]=Y' \
|
||||
--json
|
||||
```
|
||||
|
||||
- `CONFIG_ID` (required) — line ID
|
||||
- `PARAMS` — same fields as `imopenlines.config.add`
|
||||
|
||||
### Delete a line
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py imopenlines.config.delete \
|
||||
--param 'CONFIG_ID=1' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Get public page URL
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py imopenlines.config.path.get --json
|
||||
```
|
||||
|
||||
Returns the URL of the portal's public Open Lines page.
|
||||
|
||||
## Operator Actions
|
||||
|
||||
Operators manage conversations via `CHAT_ID` — the chat identifier for the dialog.
|
||||
|
||||
### Accept a dialog
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py imopenlines.operator.answer \
|
||||
--param 'CHAT_ID=2024' \
|
||||
--json
|
||||
```
|
||||
|
||||
Current operator takes the dialog.
|
||||
|
||||
### Skip a dialog
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py imopenlines.operator.skip \
|
||||
--param 'CHAT_ID=2024' \
|
||||
--json
|
||||
```
|
||||
|
||||
Current operator skips — dialog goes back to queue.
|
||||
|
||||
### Transfer a dialog
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py imopenlines.operator.transfer \
|
||||
--param 'CHAT_ID=2024' \
|
||||
--param 'TRANSFER_ID=5' \
|
||||
--json
|
||||
```
|
||||
|
||||
- `TRANSFER_ID` — user ID to transfer to an operator, OR `queue<LINE_ID>` to transfer to another line (e.g., `queue3`)
|
||||
|
||||
### Finish a dialog (own)
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py imopenlines.operator.finish \
|
||||
--param 'CHAT_ID=2024' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Finish another operator's dialog
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py imopenlines.operator.another.finish \
|
||||
--param 'CHAT_ID=2024' \
|
||||
--json
|
||||
```
|
||||
|
||||
Errors: `ACCESS_DENIED`, `CHAT_TYPE` (not an open line), `CHAT_ID` (invalid).
|
||||
|
||||
### Intercept a dialog
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py imopenlines.session.intercept \
|
||||
--param 'CHAT_ID=2024' \
|
||||
--json
|
||||
```
|
||||
|
||||
Current operator takes over a dialog that already has another operator.
|
||||
|
||||
## Session Management
|
||||
|
||||
### Start a session
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py imopenlines.session.start \
|
||||
--param 'CHAT_ID=2024' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Pin/unpin dialog to operator
|
||||
|
||||
```bash
|
||||
# Pin
|
||||
python3 scripts/bitrix24_call.py imopenlines.session.mode.pin \
|
||||
--param 'CHAT_ID=2024' \
|
||||
--param 'ACTIVATE=Y' \
|
||||
--json
|
||||
|
||||
# Unpin
|
||||
python3 scripts/bitrix24_call.py imopenlines.session.mode.pin \
|
||||
--param 'CHAT_ID=2024' \
|
||||
--param 'ACTIVATE=N' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Pin/unpin all dialogs
|
||||
|
||||
```bash
|
||||
# Pin all
|
||||
python3 scripts/bitrix24_call.py imopenlines.session.mode.pinAll --json
|
||||
|
||||
# Unpin all
|
||||
python3 scripts/bitrix24_call.py imopenlines.session.mode.unpinAll --json
|
||||
```
|
||||
|
||||
Returns array of affected session IDs.
|
||||
|
||||
### Get session history
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py imopenlines.session.history.get \
|
||||
--param 'CHAT_ID=1982' \
|
||||
--param 'SESSION_ID=469' \
|
||||
--json
|
||||
```
|
||||
|
||||
Returns full conversation: messages, users, chat metadata. Both `CHAT_ID` and `SESSION_ID` are required.
|
||||
|
||||
Response includes: `chatId`, `sessionId`, `message` (dict of messages), `users` (participants), `chat` (chat metadata with `entityType: LINES`).
|
||||
|
||||
## Dialog Info
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py imopenlines.dialog.get \
|
||||
--param 'CHAT_ID=2024' \
|
||||
--json
|
||||
```
|
||||
|
||||
Can query by any ONE of: `CHAT_ID`, `DIALOG_ID` (e.g., `chat29`), `SESSION_ID`, or `USER_CODE` (e.g., `livechat|1|1373|211`).
|
||||
|
||||
Returns: dialog name, color, avatar, owner, `entity_type`, `entity_id`, `manager_list`, `date_create`, `message_type` (`L` = lines).
|
||||
|
||||
## Messaging
|
||||
|
||||
### Send message from Open Line to user
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py imopenlines.network.message.add \
|
||||
--param 'CODE=ab515f5d85a8b844d484f6ea75a2e494' \
|
||||
--param 'USER_ID=2' \
|
||||
--param 'MESSAGE=Hello from support' \
|
||||
--json
|
||||
```
|
||||
|
||||
- `CODE` (required) — Open Line code (hash string from connector settings)
|
||||
- `USER_ID` (required) — recipient user ID
|
||||
- `MESSAGE` (required) — text with formatting support
|
||||
- `ATTACH` — attachment object (max 30 KB)
|
||||
- `KEYBOARD` — interactive keyboard (max 30 KB)
|
||||
- `URL_PREVIEW` — Y/N, default Y
|
||||
|
||||
### Send message in CRM entity chat
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py imopenlines.crm.message.add \
|
||||
--param 'CRM_ENTITY_TYPE=deal' \
|
||||
--param 'CRM_ENTITY=288' \
|
||||
--param 'USER_ID=12' \
|
||||
--param 'CHAT_ID=8773' \
|
||||
--param 'MESSAGE=Follow-up message' \
|
||||
--json
|
||||
```
|
||||
|
||||
- `CRM_ENTITY_TYPE` — `lead`, `deal`, `company`, `contact`
|
||||
- `CRM_ENTITY` — CRM entity ID
|
||||
- `USER_ID` — user or bot to add to chat
|
||||
- `CHAT_ID` — chat ID
|
||||
- `MESSAGE` — text
|
||||
|
||||
## CRM Integration
|
||||
|
||||
### Create lead from dialog
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py imopenlines.crm.lead.create \
|
||||
--param 'CHAT_ID=1988' \
|
||||
--json
|
||||
```
|
||||
|
||||
Creates a CRM lead based on the dialog. Returns `true` on success.
|
||||
|
||||
## Network (External Lines)
|
||||
|
||||
### Join external Open Line
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py imopenlines.network.join \
|
||||
--param 'CODE=ab515f5d85a8b844d484f6ea75a2e494' \
|
||||
--json
|
||||
```
|
||||
|
||||
Connects an external Open Line to the portal. Returns bot ID on success.
|
||||
|
||||
## Bot Integration
|
||||
|
||||
### Send auto-message from bot
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py imopenlines.bot.session.message.send \
|
||||
--param 'CHAT_ID=2' \
|
||||
--param 'MESSAGE=Welcome to our support' \
|
||||
--param 'NAME=WELCOME' \
|
||||
--json
|
||||
```
|
||||
|
||||
- `NAME` — message type: `WELCOME` (greeting) or `DEFAULT` (regular)
|
||||
|
||||
## API Revision
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py imopenlines.revision.get --json
|
||||
```
|
||||
|
||||
Returns current API revision numbers for REST, web, and mobile clients.
|
||||
|
||||
## Method Reference
|
||||
|
||||
### Line config
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `imopenlines.config.list.get` | List all lines (with filter/select/order) |
|
||||
| `imopenlines.config.get` | Get line by ID (with operator queue) |
|
||||
| `imopenlines.config.add` | Create a new line |
|
||||
| `imopenlines.config.update` | Update line settings |
|
||||
| `imopenlines.config.delete` | Delete a line |
|
||||
| `imopenlines.config.path.get` | Get public page URL |
|
||||
|
||||
### Operators
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `imopenlines.operator.answer` | Accept dialog |
|
||||
| `imopenlines.operator.skip` | Skip dialog (back to queue) |
|
||||
| `imopenlines.operator.transfer` | Transfer to operator or line |
|
||||
| `imopenlines.operator.finish` | Close own dialog |
|
||||
| `imopenlines.operator.another.finish` | Close another operator's dialog |
|
||||
|
||||
### Sessions
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `imopenlines.session.start` | Start a session |
|
||||
| `imopenlines.session.intercept` | Take over dialog from another operator |
|
||||
| `imopenlines.session.mode.pin` | Pin/unpin dialog to current operator |
|
||||
| `imopenlines.session.mode.pinAll` | Pin all dialogs to current operator |
|
||||
| `imopenlines.session.mode.unpinAll` | Unpin all dialogs from current operator |
|
||||
| `imopenlines.session.history.get` | Get session conversation history |
|
||||
| `imopenlines.session.join` | Join a session |
|
||||
| `imopenlines.session.open` | Get chat ID by USER_CODE |
|
||||
| `imopenlines.message.session.start` | Start session with message transfer |
|
||||
|
||||
### Dialog & messaging
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `imopenlines.dialog.get` | Get dialog info (by CHAT_ID, DIALOG_ID, SESSION_ID, or USER_CODE) |
|
||||
| `imopenlines.network.message.add` | Send message from Open Line to user |
|
||||
| `imopenlines.crm.message.add` | Send message in CRM entity chat |
|
||||
| `imopenlines.crm.lead.create` | Create CRM lead from dialog |
|
||||
|
||||
### Network & bot
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `imopenlines.network.join` | Connect external Open Line to portal |
|
||||
| `imopenlines.bot.session.message.send` | Send auto-message from bot |
|
||||
| `imopenlines.bot.session.operator` | Switch to free operator |
|
||||
| `imopenlines.bot.session.transfer` | Switch to specific operator |
|
||||
| `imopenlines.bot.session.finish` | End current session (bot) |
|
||||
| `imopenlines.revision.get` | Get API revision info |
|
||||
|
||||
## Known Limitations
|
||||
|
||||
- Open Lines require the `imopenlines` scope — standard `im` scope is not enough
|
||||
- `imopenlines.network.message.add` requires the line's CODE (hash), not the line ID
|
||||
- Attachment and keyboard objects are limited to 30 KB each
|
||||
- Session history (`imopenlines.session.history.get`) requires both CHAT_ID and SESSION_ID
|
||||
- There is no method to list all active sessions across all lines — you work with individual chats
|
||||
|
||||
For the latest API updates, check the MCP documentation server (`https://mcp-dev.bitrix24.tech/mcp`) using `bitrix-search` with queries like `imopenlines session`, `imopenlines operator`.
|
||||
|
||||
## Good MCP Queries
|
||||
|
||||
- `imopenlines config`
|
||||
- `imopenlines operator transfer`
|
||||
- `imopenlines session history`
|
||||
- `imopenlines dialog get`
|
||||
- `imopenlines crm message`
|
||||
- `imopenlines network message`
|
||||
- `imopenlines bot session`
|
||||
116
bundled-skills/bitrix24/references/products.md
Normal file
116
bundled-skills/bitrix24/references/products.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# Products and Product Rows
|
||||
|
||||
Use this file for the product catalog, product items, and attaching products to CRM entities (deals, quotes, invoices).
|
||||
|
||||
Scope: `crm`, `catalog`
|
||||
|
||||
## Product Catalog
|
||||
|
||||
- `crm.product.list` — list products (supports `order`, `filter`, `select`)
|
||||
- `crm.product.add` — add product to CRM catalog
|
||||
- `crm.product.get` — get product by ID
|
||||
- `crm.product.update` — update product
|
||||
- `crm.product.delete` — delete product
|
||||
- `crm.product.fields` — field schema
|
||||
|
||||
Key fields: `ID`, `NAME`, `PRICE`, `CURRENCY_ID`, `CATALOG_ID`, `SECTION_ID`, `ACTIVE`, `MEASURE`, `DESCRIPTION`.
|
||||
|
||||
## Product Sections
|
||||
|
||||
- `crm.productsection.list` — list product sections (categories)
|
||||
- `crm.productsection.add` — add section
|
||||
- `crm.productsection.get` — get section
|
||||
- `crm.productsection.update` — update section
|
||||
- `crm.productsection.delete` — delete section
|
||||
|
||||
## Product Rows on CRM Entities
|
||||
|
||||
Product rows attach catalog products to deals, quotes, invoices, and smart processes.
|
||||
|
||||
- `crm.item.productrow.list` — list product rows (filter by `ownerId`, `ownerType`)
|
||||
- `crm.item.productrow.get` — get one product row by ID
|
||||
- `crm.item.productrow.set` — set product rows on entity (replaces all existing rows!)
|
||||
- `crm.item.productrow.delete` — delete a product row
|
||||
- `crm.item.productrow.getAvailableForPayment` — get rows available for payment
|
||||
|
||||
Owner type short codes: `L` = lead, `D` = deal, `Q` = quote, `SI` = smart invoice, dynamic types use `T{entityTypeId}`.
|
||||
|
||||
### Product row fields
|
||||
|
||||
- `productId` — ID from catalog (optional, can use `productName` alone)
|
||||
- `productName` — product name (auto-filled from catalog if `productId` given)
|
||||
- `price` — price per unit including discounts and taxes
|
||||
- `quantity` — quantity (default 1)
|
||||
- `discountTypeId` — 1 = absolute, 2 = percentage
|
||||
- `discountRate` — discount in %
|
||||
- `discountSum` — discount in absolute value
|
||||
- `taxRate` — tax rate in %
|
||||
- `taxIncluded` — `Y`/`N`
|
||||
- `measureCode` — unit of measure code
|
||||
- `sort` — sort order
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### List all products
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.product.list \
|
||||
--param 'select[]=ID' \
|
||||
--param 'select[]=NAME' \
|
||||
--param 'select[]=PRICE' \
|
||||
--param 'select[]=CURRENCY_ID' \
|
||||
--param 'order[NAME]=ASC' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Add a product to catalog
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.product.add \
|
||||
--param 'fields[NAME]=Widget Pro' \
|
||||
--param 'fields[PRICE]=1500' \
|
||||
--param 'fields[CURRENCY_ID]=RUB' \
|
||||
--param 'fields[ACTIVE]=Y' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Get product rows of a deal
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.item.productrow.list \
|
||||
--param 'filter[ownerId]=123' \
|
||||
--param 'filter[ownerType]=D' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Set product rows on a deal
|
||||
|
||||
This replaces all existing product rows:
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.item.productrow.set \
|
||||
--param 'ownerType=D' \
|
||||
--param 'ownerId=123' \
|
||||
--param 'productRows[0][productId]=456' \
|
||||
--param 'productRows[0][price]=1500' \
|
||||
--param 'productRows[0][quantity]=2' \
|
||||
--param 'productRows[1][productName]=Custom Service' \
|
||||
--param 'productRows[1][price]=5000' \
|
||||
--param 'productRows[1][quantity]=1' \
|
||||
--json
|
||||
```
|
||||
|
||||
## Working Rules
|
||||
|
||||
- `crm.item.productrow.set` overwrites all existing rows — always include all desired rows.
|
||||
- `crm.product.*` uses UPPER_CASE field names.
|
||||
- `crm.item.productrow.*` uses camelCase field names.
|
||||
- Filter prefix operators work on `crm.product.list`: `>=PRICE`, `%NAME`.
|
||||
- `CATALOG_ID` filter is useful when multiple catalogs exist.
|
||||
|
||||
## Good MCP Queries
|
||||
|
||||
- `crm product list add fields`
|
||||
- `crm item productrow set list`
|
||||
- `crm productsection`
|
||||
- `catalog product`
|
||||
135
bundled-skills/bitrix24/references/projects.md
Normal file
135
bundled-skills/bitrix24/references/projects.md
Normal file
@@ -0,0 +1,135 @@
|
||||
# Projects and Workgroups
|
||||
|
||||
Use this file for workgroups, projects, scrum, group membership, and cross-entity queries (tasks in a project, files in a project, project chat).
|
||||
|
||||
## Core Methods
|
||||
|
||||
List and read:
|
||||
|
||||
- `socialnetwork.api.workgroup.list` — list groups with filter and select
|
||||
- `socialnetwork.api.workgroup.get` — get one group by ID
|
||||
- `sonet_group.get` — list groups accessible to current user
|
||||
- `sonet_group.user.groups` — groups of current user
|
||||
|
||||
Create and manage:
|
||||
|
||||
- `sonet_group.create` — create group (owner = current user)
|
||||
- `sonet_group.update` — update group
|
||||
- `sonet_group.delete` — delete group
|
||||
- `sonet_group.setowner` — change owner
|
||||
|
||||
Membership:
|
||||
|
||||
- `sonet_group.user.get` — list group members
|
||||
- `sonet_group.user.add` — add member directly
|
||||
- `sonet_group.user.invite` — invite member
|
||||
- `sonet_group.user.delete` — remove member
|
||||
- `sonet_group.user.update` — change member role
|
||||
- `sonet_group.user.request` — request to join
|
||||
- `sonet_group.feature.access` — check user permissions in group
|
||||
|
||||
Scrum (for scrum-type projects):
|
||||
|
||||
- `tasks.api.scrum.sprint.list` / `tasks.api.scrum.sprint.getFields`
|
||||
- `tasks.api.scrum.backlog.get`
|
||||
- `tasks.api.scrum.task.getFields`
|
||||
|
||||
## Querying Entities Within a Project
|
||||
|
||||
### Tasks in a project
|
||||
|
||||
Filter `tasks.task.list` by `GROUP_ID`:
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py tasks.task.list \
|
||||
--param 'filter[GROUP_ID]=15' \
|
||||
--param 'select[]=ID' \
|
||||
--param 'select[]=TITLE' \
|
||||
--param 'select[]=STATUS' \
|
||||
--param 'select[]=DEADLINE' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Files in a project
|
||||
|
||||
Projects have their own disk storage. Find it via `disk.storage.getlist` and filter by `ENTITY_TYPE=group`:
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py disk.storage.getlist \
|
||||
--param 'filter[ENTITY_TYPE]=group' \
|
||||
--param 'filter[ENTITY_ID]=15' \
|
||||
--json
|
||||
```
|
||||
|
||||
Then browse with `disk.storage.getchildren` using the storage ID.
|
||||
|
||||
### Project chat
|
||||
|
||||
Group chats in Bitrix24 use dialog format `sg<GROUP_ID>`:
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py im.dialog.messages.get \
|
||||
--param 'DIALOG_ID=sg15' \
|
||||
--param 'LIMIT=20' \
|
||||
--json
|
||||
```
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### List all my projects
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py sonet_group.user.groups --json
|
||||
```
|
||||
|
||||
### Get project details
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py socialnetwork.api.workgroup.get \
|
||||
--param 'id=15' \
|
||||
--json
|
||||
```
|
||||
|
||||
### List project members
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py sonet_group.user.get \
|
||||
--param 'ID=15' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Create a project
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py sonet_group.create \
|
||||
--param 'NAME=New Project' \
|
||||
--param 'DESCRIPTION=Project description' \
|
||||
--param 'VISIBLE=Y' \
|
||||
--param 'OPENED=Y' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Search groups by filter
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py socialnetwork.api.workgroup.list \
|
||||
--param 'filter[%NAME]=marketing' \
|
||||
--param 'select[]=ID' \
|
||||
--param 'select[]=NAME' \
|
||||
--param 'select[]=NUMBER_OF_MEMBERS' \
|
||||
--json
|
||||
```
|
||||
|
||||
## Working Rules
|
||||
|
||||
- Use `socialnetwork.api.workgroup.list` for filtered queries with `select[]`.
|
||||
- Use `sonet_group.get` for a quick list of accessible groups.
|
||||
- Task filtering by project: `tasks.task.list` with `filter[GROUP_ID]`.
|
||||
- Project files live in a disk storage with `ENTITY_TYPE=group`.
|
||||
- Project chat is addressable as `sg<GROUP_ID>` in `im.*` methods.
|
||||
|
||||
## Good MCP Queries
|
||||
|
||||
- `socialnetwork workgroup list get`
|
||||
- `sonet_group user create`
|
||||
- `tasks scrum sprint backlog`
|
||||
107
bundled-skills/bitrix24/references/quotes.md
Normal file
107
bundled-skills/bitrix24/references/quotes.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# Quotes and Invoices
|
||||
|
||||
Use this file for commercial proposals (quotes), new smart invoices, and legacy invoices.
|
||||
|
||||
Scope: `crm`
|
||||
|
||||
## Quotes
|
||||
|
||||
- `crm.quote.add` — create a quote
|
||||
- `crm.quote.list` — list quotes (supports `order`, `filter`, `select`)
|
||||
- `crm.quote.get` — get a quote by ID
|
||||
- `crm.quote.update` — update a quote
|
||||
- `crm.quote.delete` — delete a quote
|
||||
- `crm.quote.fields` — field schema
|
||||
- `crm.quote.productrows.get` — get product rows of a quote
|
||||
- `crm.quote.productrows.set` — set product rows on a quote
|
||||
|
||||
Key fields: `TITLE`, `STATUS_ID`, `OPPORTUNITY`, `CURRENCY_ID`, `COMPANY_ID`, `CONTACT_ID`, `DEAL_ID`, `ASSIGNED_BY_ID`, `BEGINDATE`, `CLOSEDATE`, `COMMENTS`, `OPENED`.
|
||||
|
||||
Status values: `DRAFT`, `SENT`, `APPROVED`, `DECLINED`, etc.
|
||||
|
||||
## Smart Invoices (New)
|
||||
|
||||
New invoices use the universal `crm.item.*` API with `entityTypeId=31`.
|
||||
|
||||
- `crm.item.list` with `entityTypeId=31` — list invoices
|
||||
- `crm.item.add` with `entityTypeId=31` — create invoice
|
||||
- `crm.item.update` with `entityTypeId=31` — update invoice
|
||||
- `crm.item.delete` with `entityTypeId=31` — delete invoice
|
||||
- `crm.item.fields` with `entityTypeId=31` — field schema
|
||||
|
||||
## Legacy Invoices (Deprecated)
|
||||
|
||||
`crm.invoice.*` methods are deprecated. Use `crm.item.*` with `entityTypeId=31` for new invoices.
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### Create a quote
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.quote.add \
|
||||
--param 'fields[TITLE]=Quote for services' \
|
||||
--param 'fields[STATUS_ID]=DRAFT' \
|
||||
--param 'fields[CURRENCY_ID]=RUB' \
|
||||
--param 'fields[OPPORTUNITY]=50000' \
|
||||
--param 'fields[COMPANY_ID]=1' \
|
||||
--param 'fields[ASSIGNED_BY_ID]=1' \
|
||||
--json
|
||||
```
|
||||
|
||||
### List quotes
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.quote.list \
|
||||
--param 'select[]=ID' \
|
||||
--param 'select[]=TITLE' \
|
||||
--param 'select[]=STATUS_ID' \
|
||||
--param 'select[]=OPPORTUNITY' \
|
||||
--param 'order[ID]=DESC' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Get quote product rows
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.quote.productrows.get \
|
||||
--param 'id=42' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Create a new smart invoice
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.item.add \
|
||||
--param 'entityTypeId=31' \
|
||||
--param 'fields[title]=Invoice for order' \
|
||||
--param 'fields[currencyId]=RUB' \
|
||||
--param 'fields[opportunity]=15000' \
|
||||
--param 'fields[companyId]=1' \
|
||||
--json
|
||||
```
|
||||
|
||||
### List smart invoices
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.item.list \
|
||||
--param 'entityTypeId=31' \
|
||||
--param 'select[]=id' \
|
||||
--param 'select[]=title' \
|
||||
--param 'select[]=stageId' \
|
||||
--param 'select[]=opportunity' \
|
||||
--json
|
||||
```
|
||||
|
||||
## Working Rules
|
||||
|
||||
- `crm.quote.*` uses UPPER_CASE field names.
|
||||
- `crm.item.*` with `entityTypeId=31` uses camelCase field names.
|
||||
- For new invoices, prefer `crm.item.*` over deprecated `crm.invoice.*`.
|
||||
- Quote product rows: use `crm.quote.productrows.set` (not `crm.item.productrow.set`).
|
||||
- Invoice product rows: use `crm.item.productrow.set` with `ownerType=SI`.
|
||||
|
||||
## Good MCP Queries
|
||||
|
||||
- `crm quote add list fields productrows`
|
||||
- `crm item invoice entityTypeId 31`
|
||||
- `crm invoice add list`
|
||||
165
bundled-skills/bitrix24/references/sites.md
Normal file
165
bundled-skills/bitrix24/references/sites.md
Normal file
@@ -0,0 +1,165 @@
|
||||
# Landing Pages and Sites
|
||||
|
||||
Use this file for creating and managing Bitrix24 sites and landing pages, adding blocks, editing content, and publishing.
|
||||
|
||||
Scope: `landing`
|
||||
|
||||
## Sites
|
||||
|
||||
- `landing.site.getList` — list sites (params: `select`, `filter`, `order`)
|
||||
- `landing.site.add` — create a site (fields: `TITLE`, `CODE`, `DOMAIN_ID`)
|
||||
- `landing.site.update` — update a site
|
||||
- `landing.site.delete` — delete a site
|
||||
- `landing.site.publication` — publish site and all its pages
|
||||
- `landing.site.unpublic` — unpublish site and all its pages
|
||||
- `landing.site.getPublicUrl` — get public URL of a site
|
||||
- `landing.site.fullExport` — export site structure
|
||||
- `landing.site.getFolders` — get site folders
|
||||
- `landing.site.getRights` — get current user's rights to a site
|
||||
|
||||
## Pages
|
||||
|
||||
- `landing.landing.getList` — list pages (params: `select`, `filter`, `order`; filter by `SITE_ID`)
|
||||
- `landing.landing.add` — add page (fields: `TITLE`, `CODE`, `SITE_ID`)
|
||||
- `landing.landing.update` — update page
|
||||
- `landing.landing.delete` — delete page
|
||||
- `landing.landing.copy` — copy page
|
||||
- `landing.landing.move` — move page to another site
|
||||
- `landing.landing.publication` — publish a page
|
||||
- `landing.landing.unpublic` — unpublish a page
|
||||
- `landing.landing.getpublicurl` — get public URL of a page
|
||||
|
||||
Extra flags in `landing.landing.getList`: `get_preview=1`, `get_urls=1`, `check_area=1`.
|
||||
|
||||
## Blocks
|
||||
|
||||
Blocks are visual sections added to pages.
|
||||
|
||||
- `landing.block.getlist` — list blocks on a page
|
||||
- `landing.block.getrepository` — list available block templates by section
|
||||
- `landing.block.getContentFromRepository` — get block HTML before placing
|
||||
- `landing.landing.addblock` — add block to page (fields: `CODE`, `AFTER_ID`, `ACTIVE`, `CONTENT`)
|
||||
- `landing.block.updatenodes` — edit block content by node selectors
|
||||
- `landing.block.updatecontent` — replace entire block HTML
|
||||
- `landing.block.getcontent` — get block content (HTML, CSS, JS)
|
||||
- `landing.block.getmanifest` — get block manifest (describes editable nodes)
|
||||
|
||||
Repository sections: `cover`, `about`, `text_image`, `columns`, `tiles`, `forms`, `team`, `gallery`, `menu`, `footer`, `social`, etc.
|
||||
|
||||
### Node types in `updatenodes`
|
||||
|
||||
- Text: `'.selector': 'New text with <b>html</b>'`
|
||||
- Image: `'.selector': {src: '/path/image.png', alt: 'Alt text'}`
|
||||
- Link: `'.selector': {text: 'Link text', href: 'https://...', target: '_blank'}`
|
||||
- Embed: `'.selector': {src: '//youtube.com/embed/...', source: 'https://youtube.com/watch?v=...'}`
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### List all sites
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py landing.site.getList \
|
||||
--param 'params[select][]=ID' \
|
||||
--param 'params[select][]=TITLE' \
|
||||
--param 'params[select][]=DOMAIN.DOMAIN' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Create a site
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py landing.site.add \
|
||||
--param 'fields[TITLE]=My Site' \
|
||||
--param 'fields[CODE]=mysite' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Add a page to a site
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py landing.landing.add \
|
||||
--param 'fields[TITLE]=About Us' \
|
||||
--param 'fields[CODE]=about' \
|
||||
--param 'fields[SITE_ID]=292' \
|
||||
--json
|
||||
```
|
||||
|
||||
### List available block templates
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py landing.block.getrepository \
|
||||
--param 'section=cover' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Add a block to a page
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py landing.landing.addblock \
|
||||
--param 'lid=351' \
|
||||
--param 'fields[CODE]=04.1.one_col_fix_with_title' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Edit block text content
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py landing.block.updatenodes \
|
||||
--param 'lid=351' \
|
||||
--param 'block=6058' \
|
||||
--param 'data[.landing-block-node-title]=Welcome to our site' \
|
||||
--param 'data[.landing-block-node-text]=We provide great services.' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Replace entire block HTML
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py landing.block.updatecontent \
|
||||
--param 'lid=351' \
|
||||
--param 'block=6058' \
|
||||
--param 'content=<section class="landing-block"><h1>Hello World</h1></section>' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Publish a site
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py landing.site.publication \
|
||||
--param 'id=292' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Get site public URL
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py landing.site.getPublicUrl \
|
||||
--param 'id[]=292' \
|
||||
--json
|
||||
```
|
||||
|
||||
## Building a Site from Scratch
|
||||
|
||||
1. Create site: `landing.site.add`
|
||||
2. Add pages: `landing.landing.add` with `SITE_ID`
|
||||
3. Browse block templates: `landing.block.getrepository` by section
|
||||
4. Add blocks to pages: `landing.landing.addblock` with block `CODE`
|
||||
5. Edit block content: `landing.block.updatenodes` or `landing.block.updatecontent`
|
||||
6. Publish: `landing.site.publication`
|
||||
7. Get URL: `landing.site.getPublicUrl`
|
||||
|
||||
## Working Rules
|
||||
|
||||
- Landing methods use nested `params` for `select`/`filter`/`order` (not top-level).
|
||||
- Block `CODE` comes from `landing.block.getrepository`.
|
||||
- `landing.block.updatenodes` uses CSS selectors to target editable nodes.
|
||||
- Get the block manifest first (`landing.block.getmanifest`) to see which nodes are editable.
|
||||
- `landing.block.updatecontent` replaces the entire block HTML — use with care.
|
||||
- Filter uses `%` for LIKE: `filter[TITLE]=%keyword%`.
|
||||
|
||||
## Good MCP Queries
|
||||
|
||||
- `landing site add getList publication`
|
||||
- `landing landing add getList page`
|
||||
- `landing block addblock getrepository updatenodes`
|
||||
- `landing block updatecontent getcontent`
|
||||
135
bundled-skills/bitrix24/references/smartprocess.md
Normal file
135
bundled-skills/bitrix24/references/smartprocess.md
Normal file
@@ -0,0 +1,135 @@
|
||||
# Smart Processes and Funnels
|
||||
|
||||
Use this file for smart processes (custom CRM types), funnels/categories, stages, and stage history.
|
||||
|
||||
Scope: `crm`
|
||||
|
||||
## Entity Type IDs
|
||||
|
||||
Standard types:
|
||||
|
||||
- `1` — lead
|
||||
- `2` — deal
|
||||
- `3` — contact
|
||||
- `4` — company
|
||||
- `5` — old invoice (deprecated)
|
||||
- `7` — quote
|
||||
- `31` — new invoice (smart invoice)
|
||||
- `128+` — custom smart processes
|
||||
|
||||
Discover custom types with `crm.type.list`.
|
||||
|
||||
## Smart Process Types
|
||||
|
||||
- `crm.type.list` — list all smart process types (filter by `title`, `entityTypeId`, `isCategoriesEnabled`, etc.)
|
||||
- `crm.type.get` — get one type by `id`
|
||||
- `crm.type.add` — create a new smart process type
|
||||
- `crm.type.update` — update a smart process type
|
||||
- `crm.type.delete` — delete a smart process type
|
||||
|
||||
## Items (Universal API)
|
||||
|
||||
`crm.item.*` works for deals, leads, contacts, companies, quotes, invoices, and all custom smart processes. The `entityTypeId` parameter is always required.
|
||||
|
||||
- `crm.item.list` — list items (requires `entityTypeId`, supports `select`, `filter`, `order`)
|
||||
- `crm.item.get` — get one item by `id` and `entityTypeId`
|
||||
- `crm.item.add` — create item (requires `entityTypeId` and `fields`)
|
||||
- `crm.item.update` — update item
|
||||
- `crm.item.delete` — delete item
|
||||
- `crm.item.fields` — get field schema for an entity type
|
||||
|
||||
Filter operators are prefixes on the key: `>=dateCreate`, `!stageId`, `%title`.
|
||||
|
||||
## Funnels (Categories)
|
||||
|
||||
- `crm.category.list` — list funnels for a type (requires `entityTypeId`)
|
||||
- `crm.category.get` — get one funnel
|
||||
- `crm.category.add` — create funnel
|
||||
- `crm.category.update` — update funnel
|
||||
- `crm.category.delete` — delete funnel
|
||||
- `crm.category.fields` — field schema
|
||||
|
||||
## Stages and Statuses
|
||||
|
||||
- `crm.status.list` — list statuses/stages (filter by `ENTITY_ID`)
|
||||
- `crm.stagehistory.list` — stage change history (requires `entityTypeId`)
|
||||
|
||||
Stage history `TYPE_ID` values:
|
||||
|
||||
- `1` — create
|
||||
- `2` — intermediate stage change
|
||||
- `3` — final stage
|
||||
- `5` — funnel change
|
||||
|
||||
`STAGE_SEMANTIC_ID`: `P` = in progress, `S` = success, `F` = fail.
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### List all smart process types
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.type.list --json
|
||||
```
|
||||
|
||||
### List items of a smart process
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.item.list \
|
||||
--param 'entityTypeId=128' \
|
||||
--param 'select[]=id' \
|
||||
--param 'select[]=title' \
|
||||
--param 'select[]=stageId' \
|
||||
--param 'select[]=assignedById' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Create an item in a smart process
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.item.add \
|
||||
--param 'entityTypeId=128' \
|
||||
--param 'fields[title]=New item' \
|
||||
--param 'fields[assignedById]=1' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Get field schema for a type
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.item.fields \
|
||||
--param 'entityTypeId=128' \
|
||||
--json
|
||||
```
|
||||
|
||||
### List funnels (categories) for deals
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.category.list \
|
||||
--param 'entityTypeId=2' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Get stage history
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py crm.stagehistory.list \
|
||||
--param 'entityTypeId=2' \
|
||||
--param 'filter[>=CREATED_TIME]=2026-01-01T00:00:00' \
|
||||
--json
|
||||
```
|
||||
|
||||
## Working Rules
|
||||
|
||||
- `entityTypeId` is mandatory for all `crm.item.*` and `crm.category.*` methods.
|
||||
- Use `crm.type.list` to discover `entityTypeId` values for custom processes.
|
||||
- Fields use camelCase in `crm.item.*` (e.g. `stageId`, `assignedById`, `categoryId`).
|
||||
- For deal-specific methods (`crm.deal.*`), fields use UPPER_CASE (`STAGE_ID`, `CATEGORY_ID`).
|
||||
- Always call `crm.item.fields` before writing custom field values.
|
||||
|
||||
## Good MCP Queries
|
||||
|
||||
- `crm type list add smart process`
|
||||
- `crm item list add fields`
|
||||
- `crm category list funnels`
|
||||
- `crm stagehistory`
|
||||
- `crm status list`
|
||||
158
bundled-skills/bitrix24/references/tasks.md
Normal file
158
bundled-skills/bitrix24/references/tasks.md
Normal file
@@ -0,0 +1,158 @@
|
||||
# Tasks
|
||||
|
||||
Use this file for task CRUD, delegation, checklists, comments, planner data, and filtering.
|
||||
|
||||
## Core Methods
|
||||
|
||||
- `tasks.task.list` — list tasks with filters, sorting, and pagination
|
||||
- `tasks.task.get` — get one task by ID
|
||||
- `tasks.task.add` — create task
|
||||
- `tasks.task.update` — update task
|
||||
- `tasks.task.complete` — mark task as completed
|
||||
- `tasks.task.renew` — reopen task
|
||||
- `tasks.task.delegate` — delegate task
|
||||
- `tasks.task.delete` — delete task
|
||||
- `tasks.task.favorite.add` / `tasks.task.favorite.remove`
|
||||
|
||||
Checklist:
|
||||
|
||||
- `task.checklistitem.add` / `task.checklistitem.getlist`
|
||||
- `task.checklistitem.complete` / `task.checklistitem.renew`
|
||||
- `task.checklistitem.delete`
|
||||
|
||||
Comments:
|
||||
|
||||
- `task.commentitem.add` / `task.commentitem.getlist`
|
||||
|
||||
Planner:
|
||||
|
||||
- `task.planner.getlist` — returns task IDs from current user's "Plan for the day"
|
||||
|
||||
Deprecated: `task.item.*` methods — do not use.
|
||||
|
||||
## Critical: Filter Syntax
|
||||
|
||||
`tasks.task.list` uses prefix operators on filter keys:
|
||||
|
||||
- `>=DEADLINE` — deadline on or after date
|
||||
- `<=DEADLINE` — deadline on or before date
|
||||
- `!STATUS` — status not equal to value
|
||||
- `RESPONSIBLE_ID` — assigned user
|
||||
- `CREATED_BY` — creator
|
||||
- `GROUP_ID` — project group
|
||||
|
||||
Dates in filters use `YYYY-MM-DD` format.
|
||||
|
||||
**Wrong:** `filter[DEADLINE]=2026-03-10` — this does not filter by exact date.
|
||||
**Right:** `filter[>=DEADLINE]=2026-03-10` + `filter[<=DEADLINE]=2026-03-10` for tasks with deadline on that day.
|
||||
|
||||
## Statuses
|
||||
|
||||
- `1` — new
|
||||
- `2` — waiting (pending)
|
||||
- `3` — in progress
|
||||
- `4` — supposedly completed (awaiting approval)
|
||||
- `5` — completed
|
||||
- `6` — deferred
|
||||
|
||||
To exclude deferred tasks: `filter[!STATUS]=6`
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### Overdue tasks (for proactive warnings)
|
||||
|
||||
Tasks where deadline has passed but task is not completed or deferred:
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py tasks.task.list \
|
||||
--param 'filter[RESPONSIBLE_ID]=1' \
|
||||
--param 'filter[<DEADLINE]=2026-03-08' \
|
||||
--param 'filter[<REAL_STATUS]=5' \
|
||||
--param 'select[]=ID' \
|
||||
--param 'select[]=TITLE' \
|
||||
--param 'select[]=DEADLINE' \
|
||||
--param 'select[]=STATUS' \
|
||||
--param 'order[DEADLINE]=asc' \
|
||||
--json
|
||||
```
|
||||
|
||||
`<DEADLINE` = deadline before today. `<REAL_STATUS` = status less than 5 (excludes completed=5 and deferred=6).
|
||||
|
||||
Use this in morning briefing and task lists to flag overdue items with "⚠️".
|
||||
|
||||
### Show active tasks for current user
|
||||
|
||||
First get user ID, then list active tasks:
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py user.current --json
|
||||
python3 scripts/bitrix24_call.py tasks.task.list \
|
||||
--param 'filter[RESPONSIBLE_ID]=1' \
|
||||
--param 'filter[!STATUS]=5' \
|
||||
--param 'filter[!STATUS]=6' \
|
||||
--param 'select[]=ID' \
|
||||
--param 'select[]=TITLE' \
|
||||
--param 'select[]=STATUS' \
|
||||
--param 'select[]=DEADLINE' \
|
||||
--param 'order[DEADLINE]=asc' \
|
||||
--json
|
||||
```
|
||||
|
||||
Note: to exclude both statuses 5 and 6, use `REAL_STATUS` with range filter or pass `filter[<REAL_STATUS]=5`.
|
||||
|
||||
### Tasks with deadline on a specific date
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py tasks.task.list \
|
||||
--param 'filter[>=DEADLINE]=2026-03-10' \
|
||||
--param 'filter[<=DEADLINE]=2026-03-10' \
|
||||
--param 'select[]=ID' \
|
||||
--param 'select[]=TITLE' \
|
||||
--param 'select[]=DEADLINE' \
|
||||
--param 'select[]=STATUS' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Create a task
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py tasks.task.add \
|
||||
--param 'fields[TITLE]=Task title' \
|
||||
--param 'fields[RESPONSIBLE_ID]=1' \
|
||||
--param 'fields[DEADLINE]=2026-03-15' \
|
||||
--param 'fields[PRIORITY]=2' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Add checklist item
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py task.checklistitem.add \
|
||||
--param 'TASKID=456' \
|
||||
--param 'FIELDS[TITLE]=Subtask text' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Add comment
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py task.commentitem.add \
|
||||
--param 'TASKID=456' \
|
||||
--param 'FIELDS[POST_MESSAGE]=Comment text' \
|
||||
--json
|
||||
```
|
||||
|
||||
## Working Rules
|
||||
|
||||
- Always use `select[]` to pick only the fields you need.
|
||||
- Use `order[DEADLINE]=asc` to sort by deadline.
|
||||
- Pagination: page size is 50, use `start=0`, `start=50`, etc.
|
||||
- Get user ID from `user.current` before filtering by `RESPONSIBLE_ID`.
|
||||
- For read-only requests, execute immediately.
|
||||
|
||||
## Good MCP Queries
|
||||
|
||||
- `tasks task list filter`
|
||||
- `task checklistitem`
|
||||
- `task commentitem`
|
||||
- `task planner`
|
||||
143
bundled-skills/bitrix24/references/timeman.md
Normal file
143
bundled-skills/bitrix24/references/timeman.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# Time Tracking and Work Reports
|
||||
|
||||
Use this file for work day management, time tracking on tasks, absence reports, and work schedules.
|
||||
|
||||
Scope: `timeman`
|
||||
|
||||
## Work Day Management
|
||||
|
||||
- `timeman.status` — get current work day status (OPENED/CLOSED/PAUSED/EXPIRED)
|
||||
- `timeman.open` — start or resume work day
|
||||
- `timeman.pause` — pause work day
|
||||
- `timeman.close` — end work day
|
||||
- `timeman.settings` — get work time settings for a user
|
||||
- `timeman.schedule.get` — get work schedule by ID
|
||||
|
||||
Status values from `timeman.status`:
|
||||
|
||||
- `OPENED` — work day is active
|
||||
- `CLOSED` — work day is finished
|
||||
- `PAUSED` — work day is paused
|
||||
- `EXPIRED` — opened before today and never closed
|
||||
|
||||
## Time Control and Absence Reports
|
||||
|
||||
- `timeman.timecontrol.reports.get` — get absence report for a user/month
|
||||
- `timeman.timecontrol.reports.users.get` — list users in department with access level
|
||||
- `timeman.timecontrol.report.add` — submit absence explanation
|
||||
- `timeman.timecontrol.settings.get` / `.set` — time control module settings
|
||||
- `timeman.timecontrol.reports.settings.get` — report UI settings
|
||||
|
||||
## Task Time Tracking
|
||||
|
||||
- `task.elapseditem.add` — add time spent on a task
|
||||
- `task.elapseditem.getlist` — list time entries for a task
|
||||
- `task.elapseditem.get` — get a single time entry
|
||||
- `task.elapseditem.update` — update time entry
|
||||
- `task.elapseditem.delete` — delete time entry
|
||||
|
||||
## Office Network
|
||||
|
||||
- `timeman.networkrange.get` — get office network ranges
|
||||
- `timeman.networkrange.set` — set office network ranges
|
||||
- `timeman.networkrange.check` — check if IP is in office network
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### Check work day status
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py timeman.status --json
|
||||
```
|
||||
|
||||
### Check work day status for another user
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py timeman.status \
|
||||
--param 'USER_ID=42' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Start work day
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py timeman.open --json
|
||||
```
|
||||
|
||||
### End work day
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py timeman.close --json
|
||||
```
|
||||
|
||||
### Get absence report for a user
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py timeman.timecontrol.reports.get \
|
||||
--param 'USER_ID=42' \
|
||||
--param 'MONTH=3' \
|
||||
--param 'YEAR=2026' \
|
||||
--json
|
||||
```
|
||||
|
||||
### List department users for time reports
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py timeman.timecontrol.reports.users.get \
|
||||
--param 'DEPARTMENT_ID=5' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Get time spent on a task
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py task.elapseditem.getlist \
|
||||
--param 'TASKID=456' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Log time on a task
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py task.elapseditem.add \
|
||||
--param 'TASKID=456' \
|
||||
--param 'FIELDS[SECONDS]=3600' \
|
||||
--param 'FIELDS[COMMENT_TEXT]=Development work' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Get user work schedule
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py timeman.settings \
|
||||
--param 'USER_ID=42' \
|
||||
--json
|
||||
```
|
||||
|
||||
## Building Department Reports
|
||||
|
||||
To build a time report for a department:
|
||||
|
||||
1. Get department employees: `im.department.employees.get`
|
||||
2. For each employee, get work day status: `timeman.status` with `USER_ID`
|
||||
3. For detailed reports: `timeman.timecontrol.reports.get` with `USER_ID`, `MONTH`, `YEAR`
|
||||
4. For task time: `task.elapseditem.getlist` per task
|
||||
|
||||
## Working Rules
|
||||
|
||||
- `timeman.status` returns current user by default — pass `USER_ID` for other users.
|
||||
- `timeman.timecontrol.reports.get` requires `USER_ID`, `MONTH`, and `YEAR` (all mandatory).
|
||||
- Access to other users' reports depends on role (manager/admin).
|
||||
- `task.elapseditem.*` uses `TASKID` (not `TASK_ID`).
|
||||
- Time entries use `SECONDS` field, not hours.
|
||||
|
||||
## Note: No Email API
|
||||
|
||||
Bitrix24 has `mailservice.*` methods for configuring SMTP/IMAP mail services, but there is **no REST API for reading or sending individual emails** from Bitrix24 mailboxes.
|
||||
|
||||
## Good MCP Queries
|
||||
|
||||
- `timeman status open close`
|
||||
- `timeman timecontrol reports`
|
||||
- `task elapseditem time spent`
|
||||
- `timeman schedule settings`
|
||||
121
bundled-skills/bitrix24/references/troubleshooting.md
Normal file
121
bundled-skills/bitrix24/references/troubleshooting.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# Troubleshooting
|
||||
|
||||
Use this file when webhook calls fail or the agent cannot reach the portal.
|
||||
|
||||
## Default Behavior
|
||||
|
||||
Do the first diagnosis yourself before asking the user anything.
|
||||
|
||||
Preferred order:
|
||||
|
||||
1. Run `scripts/check_webhook.py --json`
|
||||
2. Inspect the result: format, DNS, HTTP status
|
||||
3. Report concrete findings and one next fix
|
||||
4. Only ask the user for a webhook if no saved config exists
|
||||
|
||||
## Typical Failure: No Webhook Configured
|
||||
|
||||
If `check_webhook.py` reports `"source": "missing"`:
|
||||
|
||||
- Ask the user for a webhook URL
|
||||
- Save and verify in one step:
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py user.current --url "<webhook>" --json
|
||||
```
|
||||
|
||||
If the user already pasted the webhook earlier in the conversation, save it immediately and retry.
|
||||
|
||||
## Typical Failure: DNS Resolution Failed
|
||||
|
||||
Usually means:
|
||||
|
||||
- typo in the portal domain
|
||||
- local network issue
|
||||
|
||||
Tell the user the portal address could not be reached. Name the host that failed.
|
||||
|
||||
## Typical Failure: Bad Format
|
||||
|
||||
Expected format:
|
||||
|
||||
```text
|
||||
https://your-portal.bitrix24.ru/rest/<user_id>/<webhook>/
|
||||
```
|
||||
|
||||
Common mistakes:
|
||||
|
||||
- copied portal URL instead of webhook URL
|
||||
- extra quotes or spaces
|
||||
- wrong user ID segment
|
||||
|
||||
## Typical Failure: HTTP 401 or Auth Errors
|
||||
|
||||
Usually indicates:
|
||||
|
||||
- revoked webhook
|
||||
- wrong secret
|
||||
- expired OAuth token
|
||||
|
||||
Ask the user to verify or regenerate the webhook in Bitrix24.
|
||||
|
||||
## Typical Failure: `ACCESS_DENIED` or `insufficient_scope`
|
||||
|
||||
Missing permissions. Tell the user exactly which scope family is likely missing:
|
||||
|
||||
- CRM
|
||||
- Tasks
|
||||
- Calendar
|
||||
- Disk
|
||||
- IM
|
||||
- `imbot`
|
||||
|
||||
Do not just say "permissions issue" without naming the likely scope.
|
||||
|
||||
## User-Facing Style
|
||||
|
||||
Prefer:
|
||||
|
||||
- what you checked
|
||||
- what failed
|
||||
- what is already confirmed working
|
||||
- one next action
|
||||
- plain business language ("connection to Bitrix24", "access to calendar")
|
||||
- doing the next safe step yourself before asking the user
|
||||
|
||||
Avoid:
|
||||
|
||||
- long lists of shell commands for the user
|
||||
- asking for confirmation before a simple retry
|
||||
- exposing webhook URLs or secrets
|
||||
- talking about curl, MCP, JSON, DNS, or config mechanics unless explicitly asked
|
||||
- multiple-choice menus
|
||||
|
||||
## Response Templates
|
||||
|
||||
Bad:
|
||||
|
||||
- "What you need to do now: 1. create env 2. source env 3. run curl 4. or try direct URL..."
|
||||
|
||||
Better for missing webhook:
|
||||
|
||||
- "Сейчас доступ к Битрикс24 не подключен. Пришлите вебхук, и я сразу настрою и проверю подключение."
|
||||
|
||||
Better when DNS failed:
|
||||
|
||||
- "Не удаётся связаться с Битрикс24. Похоже, адрес портала указан неверно."
|
||||
|
||||
Better when auth failed:
|
||||
|
||||
- "Связь с Битрикс24 есть, но доступ не подтверждён. Скорее всего, вебхук нужно обновить."
|
||||
|
||||
## Autonomous Retry Rule
|
||||
|
||||
For safe read-only requests:
|
||||
|
||||
- Execute immediately
|
||||
- If it fails, run `check_webhook.py --json`
|
||||
- If fixable, retry once automatically
|
||||
- Only then report the blocker
|
||||
|
||||
Do not ask "Should I try again?" — just do it.
|
||||
125
bundled-skills/bitrix24/references/users.md
Normal file
125
bundled-skills/bitrix24/references/users.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# Users, Departments, and Org Structure
|
||||
|
||||
Use this file for user lookup, department hierarchy, subordinates, managers, and org-structure reports.
|
||||
|
||||
## Users
|
||||
|
||||
- `user.current` — current webhook user (always start here to get own ID)
|
||||
- `user.get` — get users by filter (supports `UF_DEPARTMENT`, `ACTIVE`, etc.)
|
||||
- `user.search` — fast fuzzy search by name, position, department
|
||||
- `profile` — basic info about current user (no scope required)
|
||||
|
||||
## Departments
|
||||
|
||||
- `department.get` — list departments (supports `PARENT`, `UF_HEAD`, `NAME` filters)
|
||||
- `department.add` / `department.update` / `department.delete`
|
||||
- `department.fields` — field schema
|
||||
|
||||
Key fields in `department.get`:
|
||||
|
||||
- `ID` — department ID
|
||||
- `NAME` — department name
|
||||
- `PARENT` — parent department ID (use to build tree)
|
||||
- `UF_HEAD` — user ID of department head
|
||||
- `SORT` — sort order
|
||||
|
||||
## Org Structure Helpers (Messenger API)
|
||||
|
||||
- `im.department.employees.get` — employees of given department(s)
|
||||
- `im.department.managers.get` — managers/heads of given department(s)
|
||||
- `im.department.colleagues.list` — colleagues of current user (for managers: returns subordinates)
|
||||
- `im.department.get` — department data by ID
|
||||
- `im.search.user.list` — search users by name/position
|
||||
- `im.search.department.list` — search departments by name
|
||||
- `im.user.get` — get user data by ID
|
||||
|
||||
`BX24.selectUsers` is frontend-only, not usable from REST.
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### Get current user identity
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py user.current --json
|
||||
```
|
||||
|
||||
### Build department tree
|
||||
|
||||
Get all departments, use `PARENT` field to reconstruct hierarchy:
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py department.get --json
|
||||
```
|
||||
|
||||
### Get subdepartments of a specific department
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py department.get \
|
||||
--param 'PARENT=1' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Get department head
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py im.department.managers.get \
|
||||
--param 'ID[]=5' \
|
||||
--param 'USER_DATA=Y' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Get all employees of a department
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py im.department.employees.get \
|
||||
--param 'ID[]=5' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Get subordinates (for a manager)
|
||||
|
||||
`im.department.colleagues.list` returns subordinates when called by a manager:
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py im.department.colleagues.list --json
|
||||
```
|
||||
|
||||
### Get users by department
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py user.get \
|
||||
--param 'filter[UF_DEPARTMENT]=5' \
|
||||
--param 'filter[ACTIVE]=true' \
|
||||
--json
|
||||
```
|
||||
|
||||
### Search users by name
|
||||
|
||||
```bash
|
||||
python3 scripts/bitrix24_call.py user.search \
|
||||
--param 'FILTER[NAME]=Ivan' \
|
||||
--json
|
||||
```
|
||||
|
||||
## Building Reports by Department
|
||||
|
||||
To build a report by department with subordinates:
|
||||
|
||||
1. Get all departments: `department.get`
|
||||
2. For each department, get employees: `im.department.employees.get` or `user.get` with `filter[UF_DEPARTMENT]`
|
||||
3. For each department, get head: `im.department.managers.get`
|
||||
4. Cross-reference with task/timeman data as needed
|
||||
|
||||
## Working Rules
|
||||
|
||||
- Always start with `user.current` to know the webhook user's ID.
|
||||
- Use `department.get` with `PARENT` filter to navigate the tree.
|
||||
- `UF_HEAD` in department data gives the head's user ID directly.
|
||||
- Pagination: page size 50, use `START=0`, `START=50`, etc.
|
||||
|
||||
## Good MCP Queries
|
||||
|
||||
- `user current get search`
|
||||
- `department get fields`
|
||||
- `im department employees managers colleagues`
|
||||
- `im search user department`
|
||||
69
bundled-skills/bitrix24/scripts/auto_update.sh
Normal file
69
bundled-skills/bitrix24/scripts/auto_update.sh
Normal file
@@ -0,0 +1,69 @@
|
||||
#!/bin/bash
|
||||
# Auto-update bitrix24 skill on the target machine.
|
||||
#
|
||||
# Checks ClawHub for new versions, installs if available, and restarts gateway.
|
||||
# Designed for scheduled execution (e.g., every hour via cron or OpenClaw scheduled task).
|
||||
#
|
||||
# Usage:
|
||||
# ssh slon-mac "export PATH=\$HOME/.nvm/versions/node/*/bin:/opt/homebrew/bin:/usr/local/bin:\$PATH && bash -s" < scripts/auto_update.sh
|
||||
#
|
||||
# Or run directly on slon-mac:
|
||||
# bash /path/to/auto_update.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
export PATH="$HOME/.nvm/versions/node/*/bin:/opt/homebrew/bin:/usr/local/bin:$PATH"
|
||||
|
||||
SKILL="bitrix24"
|
||||
LOG_PREFIX="[bitrix24-auto-update]"
|
||||
|
||||
log() { echo "$LOG_PREFIX $(date '+%H:%M:%S') $1"; }
|
||||
|
||||
# Get currently installed version
|
||||
INSTALLED=$(npx clawhub list 2>/dev/null | grep "$SKILL" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "")
|
||||
if [ -z "$INSTALLED" ]; then
|
||||
log "ERROR: $SKILL not installed"
|
||||
exit 1
|
||||
fi
|
||||
log "Installed: v$INSTALLED"
|
||||
|
||||
# Get latest available version from registry
|
||||
LATEST=$(npx clawhub inspect "$SKILL" 2>/dev/null | grep -oE 'latest:\s*[0-9]+\.[0-9]+\.[0-9]+' | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' || echo "")
|
||||
if [ -z "$LATEST" ]; then
|
||||
log "Could not check latest version (network or rate limit)"
|
||||
exit 0
|
||||
fi
|
||||
log "Latest: v$LATEST"
|
||||
|
||||
# Compare
|
||||
if [ "$INSTALLED" = "$LATEST" ]; then
|
||||
log "Up to date"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log "Update available: v$INSTALLED → v$LATEST"
|
||||
|
||||
# Install
|
||||
log "Installing v$LATEST..."
|
||||
if npx clawhub install "$SKILL" --version "$LATEST" --force 2>&1; then
|
||||
log "Installed v$LATEST"
|
||||
else
|
||||
log "ERROR: Install failed (rate limit?). Will retry next run."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Restart gateway
|
||||
log "Restarting gateway..."
|
||||
if openclaw gateway restart 2>&1; then
|
||||
log "Gateway restarted"
|
||||
else
|
||||
log "WARNING: Gateway restart failed"
|
||||
fi
|
||||
|
||||
log "UPDATE COMPLETE: $SKILL v$INSTALLED → v$LATEST"
|
||||
|
||||
# Output summary for the user notification
|
||||
echo ""
|
||||
echo "---"
|
||||
echo "Скилл Bitrix24 обновлён: v$INSTALLED → v$LATEST"
|
||||
echo "Подробности: https://rsvbitrix.github.io/bitrix24-skill/#changelog"
|
||||
96
bundled-skills/bitrix24/scripts/bitrix24_batch.py
Normal file
96
bundled-skills/bitrix24/scripts/bitrix24_batch.py
Normal file
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Execute multiple Bitrix24 REST methods in one HTTP request using batch API."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from urllib import error, parse, request
|
||||
|
||||
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||
if str(SCRIPT_DIR) not in sys.path:
|
||||
sys.path.insert(0, str(SCRIPT_DIR))
|
||||
|
||||
from bitrix24_config import load_url, normalize_url, validate_url # noqa: E402
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description="Batch-call Bitrix24 REST methods.")
|
||||
parser.add_argument(
|
||||
"--cmd",
|
||||
action="append",
|
||||
required=True,
|
||||
help="Command in name=method?params form, e.g. 'tasks=tasks.task.list?filter[STATUS]=2'. Repeat for each method.",
|
||||
)
|
||||
parser.add_argument("--halt", type=int, default=0, help="Stop on first error (1) or run all (0, default)")
|
||||
parser.add_argument("--url", help="Webhook URL override")
|
||||
parser.add_argument("--config-file", help="Config file path override")
|
||||
parser.add_argument("--timeout", type=float, default=30.0, help="HTTP timeout in seconds")
|
||||
parser.add_argument("--json", action="store_true", help="Pretty-print JSON response")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def parse_commands(raw_cmds: list[str]) -> dict[str, str]:
|
||||
"""Parse 'name=method?params' into {name: 'method?params'}."""
|
||||
commands: dict[str, str] = {}
|
||||
for i, item in enumerate(raw_cmds):
|
||||
if "=" not in item:
|
||||
raise ValueError(f"Invalid --cmd '{item}'. Use name=method or name=method?params")
|
||||
name, method_with_params = item.split("=", 1)
|
||||
if not name:
|
||||
name = f"cmd{i}"
|
||||
commands[name] = method_with_params
|
||||
return commands
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
raw_url, source = load_url(cli_url=args.url, config_file=args.config_file)
|
||||
if not raw_url:
|
||||
print(json.dumps({"ok": False, "error": "No Bitrix24 webhook configured", "source": source}, indent=2))
|
||||
return 1
|
||||
|
||||
normalized_url = validate_url(raw_url)
|
||||
|
||||
try:
|
||||
commands = parse_commands(args.cmd)
|
||||
except ValueError as exc:
|
||||
print(str(exc), file=sys.stderr)
|
||||
return 1
|
||||
|
||||
url = normalize_url(normalized_url) + "batch.json"
|
||||
params: list[tuple[str, str]] = [("halt", str(args.halt))]
|
||||
for name, method_call in commands.items():
|
||||
params.append((f"cmd[{name}]", method_call))
|
||||
|
||||
data = parse.urlencode(params).encode("utf-8")
|
||||
req = request.Request(url, data=data, headers={"Accept": "application/json"})
|
||||
|
||||
try:
|
||||
with request.urlopen(req, timeout=args.timeout) as response:
|
||||
payload = response.read().decode("utf-8", errors="replace")
|
||||
status = response.getcode()
|
||||
except error.HTTPError as exc:
|
||||
payload = exc.read().decode("utf-8", errors="replace")
|
||||
status = exc.code
|
||||
except Exception as exc:
|
||||
print(json.dumps({"ok": False, "error": str(exc), "source": source}, indent=2))
|
||||
return 1
|
||||
|
||||
try:
|
||||
body = json.loads(payload)
|
||||
except Exception:
|
||||
body = {"raw": payload}
|
||||
|
||||
result = {"ok": status < 400, "status": status, "source": source, "commands": list(commands.keys()), "body": body}
|
||||
if args.json:
|
||||
print(json.dumps(result, ensure_ascii=True, indent=2))
|
||||
else:
|
||||
print(json.dumps(result, ensure_ascii=True))
|
||||
return 0 if status < 400 else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
273
bundled-skills/bitrix24/scripts/bitrix24_call.py
Normal file
273
bundled-skills/bitrix24/scripts/bitrix24_call.py
Normal file
@@ -0,0 +1,273 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Call Bitrix24 REST methods using saved config or explicit URL.
|
||||
|
||||
Features:
|
||||
--param key=value Standard parameter passing (repeat for multiple)
|
||||
--params-file FILE Pass parameters from a JSON file (safer for complex data)
|
||||
--dry-run Validate request without executing
|
||||
--iterate Auto-paginate list methods (collect all pages)
|
||||
--max-items N Limit total items when iterating
|
||||
--confirm-write Required flag for write operations (add, update, set)
|
||||
--confirm-destructive Required flag for destructive operations (delete, remove)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from urllib import error, parse, request
|
||||
|
||||
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||
if str(SCRIPT_DIR) not in sys.path:
|
||||
sys.path.insert(0, str(SCRIPT_DIR))
|
||||
|
||||
from bitrix24_config import load_url, normalize_url, persist_url_to_config, validate_url, cache_user_data # noqa: E402
|
||||
|
||||
# Operation classification patterns
|
||||
WRITE_SUFFIXES = re.compile(
|
||||
r"(?:^|\.)(add|update|set|register|bind|import|complete|start|stop|move|clear|confirm|attach|send|mute|pin)$",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
DESTRUCTIVE_SUFFIXES = re.compile(
|
||||
r"(?:^|\.)(delete|remove|recyclebin|unregister|unbind)$",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
MAX_PAGES = 200 # Safety limit for auto-pagination
|
||||
|
||||
|
||||
def classify_operation(method: str) -> str:
|
||||
"""Classify method as 'read', 'write', or 'destructive'."""
|
||||
if DESTRUCTIVE_SUFFIXES.search(method):
|
||||
return "destructive"
|
||||
if WRITE_SUFFIXES.search(method):
|
||||
return "write"
|
||||
return "read"
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description="Call a Bitrix24 REST method.")
|
||||
parser.add_argument("method", help="REST method, e.g. user.current or calendar.event.get")
|
||||
parser.add_argument("--url", help="Webhook URL (saved to config automatically)")
|
||||
parser.add_argument("--config-file", help="Config file path override")
|
||||
parser.add_argument(
|
||||
"--param",
|
||||
action="append",
|
||||
default=[],
|
||||
help="Request parameter in key=value form; repeat as needed",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--params-file",
|
||||
help="Path to a JSON file with method parameters (alternative to --param)",
|
||||
)
|
||||
parser.add_argument("--timeout", type=float, default=20.0, help="HTTP timeout in seconds")
|
||||
parser.add_argument("--json", action="store_true", help="Pretty-print JSON response")
|
||||
parser.add_argument("--dry-run", action="store_true", help="Show what would be called without executing")
|
||||
parser.add_argument("--iterate", action="store_true", help="Auto-paginate list methods (collect all pages)")
|
||||
parser.add_argument("--max-items", type=int, default=None, help="Max items to collect when iterating")
|
||||
parser.add_argument("--confirm-write", action="store_true", help="Confirm write operation (add/update/set)")
|
||||
parser.add_argument("--confirm-destructive", action="store_true", help="Confirm destructive operation (delete/remove)")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def parse_params(raw_params: list[str]) -> list[tuple[str, str]]:
|
||||
out: list[tuple[str, str]] = []
|
||||
for item in raw_params:
|
||||
if "=" not in item:
|
||||
raise ValueError(f"Invalid --param '{item}'. Use key=value.")
|
||||
key, value = item.split("=", 1)
|
||||
out.append((key, value))
|
||||
return out
|
||||
|
||||
|
||||
def do_request(url: str, data: bytes, timeout: float, source: str) -> dict:
|
||||
"""Execute HTTP request and return structured result."""
|
||||
req = request.Request(url, data=data, headers={"Accept": "application/json"})
|
||||
try:
|
||||
with request.urlopen(req, timeout=timeout) as response:
|
||||
payload = response.read().decode("utf-8", errors="replace")
|
||||
status = response.getcode()
|
||||
except error.HTTPError as exc:
|
||||
payload = exc.read().decode("utf-8", errors="replace")
|
||||
status = exc.code
|
||||
except Exception as exc:
|
||||
return {"ok": False, "error": str(exc), "source": source}
|
||||
|
||||
try:
|
||||
body = json.loads(payload)
|
||||
except Exception:
|
||||
body = {"raw": payload}
|
||||
|
||||
return {"ok": status < 400, "status": status, "source": source, "body": body}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
raw_url, source = load_url(cli_url=args.url, config_file=args.config_file)
|
||||
if not raw_url:
|
||||
print(json.dumps({"ok": False, "error": "No Bitrix24 webhook configured", "source": source}, ensure_ascii=True, indent=2))
|
||||
return 1
|
||||
|
||||
normalized_url = validate_url(raw_url)
|
||||
|
||||
if not source.startswith("config:"):
|
||||
persist_url_to_config(normalized_url, args.config_file)
|
||||
|
||||
method = args.method[:-5] if args.method.endswith(".json") else args.method
|
||||
|
||||
# --- Resolve parameters ---
|
||||
# --params-file takes priority (safer for complex JSON)
|
||||
if args.params_file:
|
||||
try:
|
||||
params_json = json.loads(Path(args.params_file).read_text(encoding="utf-8"))
|
||||
except FileNotFoundError:
|
||||
print(json.dumps({"ok": False, "error": f"Params file not found: {args.params_file}"}, indent=2))
|
||||
return 1
|
||||
except json.JSONDecodeError as e:
|
||||
print(json.dumps({"ok": False, "error": f"Invalid JSON in {args.params_file}: {e}"}, indent=2))
|
||||
return 1
|
||||
if not isinstance(params_json, dict):
|
||||
print(json.dumps({"ok": False, "error": "params-file must contain a JSON object"}), indent=2)
|
||||
return 1
|
||||
# Convert JSON dict to URL-encoded pairs (flat)
|
||||
params = _flatten_json(params_json)
|
||||
else:
|
||||
try:
|
||||
params = parse_params(args.param)
|
||||
except ValueError as exc:
|
||||
print(str(exc), file=sys.stderr)
|
||||
return 1
|
||||
|
||||
# --- Operation classification & safety checks ---
|
||||
op_type = classify_operation(method)
|
||||
|
||||
if args.dry_run:
|
||||
dry = {
|
||||
"dry_run": True,
|
||||
"method": method,
|
||||
"operation": op_type,
|
||||
"params_count": len(params),
|
||||
"source": source,
|
||||
}
|
||||
print(json.dumps(dry, ensure_ascii=True, indent=2))
|
||||
return 0
|
||||
|
||||
if op_type == "write" and not args.confirm_write:
|
||||
print(json.dumps({
|
||||
"ok": False,
|
||||
"error": f"Write operation '{method}' requires --confirm-write flag",
|
||||
"operation": op_type,
|
||||
}, ensure_ascii=True, indent=2))
|
||||
return 2
|
||||
|
||||
if op_type == "destructive" and not args.confirm_destructive:
|
||||
print(json.dumps({
|
||||
"ok": False,
|
||||
"error": f"Destructive operation '{method}' requires --confirm-destructive flag",
|
||||
"operation": op_type,
|
||||
}, ensure_ascii=True, indent=2))
|
||||
return 2
|
||||
|
||||
base_url = normalize_url(normalized_url)
|
||||
|
||||
# --- Auto-pagination ---
|
||||
if args.iterate and method.endswith(".list"):
|
||||
all_items: list = []
|
||||
start = 0
|
||||
total = None
|
||||
for page in range(1, MAX_PAGES + 1):
|
||||
page_params = params + [("start", str(start))]
|
||||
url = base_url + f"{method}.json"
|
||||
data = parse.urlencode(page_params).encode("utf-8")
|
||||
result = do_request(url, data, args.timeout, source)
|
||||
|
||||
if not result.get("ok"):
|
||||
print(json.dumps(result, ensure_ascii=True, indent=2 if args.json else None))
|
||||
return 1
|
||||
|
||||
body = result.get("body", {})
|
||||
page_result = body.get("result", [])
|
||||
if isinstance(page_result, dict):
|
||||
# Some methods return {tasks: [...]} instead of [...]
|
||||
for v in page_result.values():
|
||||
if isinstance(v, list):
|
||||
page_result = v
|
||||
break
|
||||
else:
|
||||
page_result = []
|
||||
|
||||
all_items.extend(page_result)
|
||||
|
||||
if total is None:
|
||||
total = body.get("total", len(all_items))
|
||||
|
||||
if args.max_items and len(all_items) >= args.max_items:
|
||||
all_items = all_items[:args.max_items]
|
||||
break
|
||||
|
||||
next_start = body.get("next")
|
||||
if next_start is None:
|
||||
break
|
||||
start = next_start
|
||||
|
||||
result = {
|
||||
"ok": True,
|
||||
"status": 200,
|
||||
"source": source,
|
||||
"body": {"result": all_items, "total": total, "fetched": len(all_items)},
|
||||
}
|
||||
print(json.dumps(result, ensure_ascii=True, indent=2 if args.json else None))
|
||||
return 0
|
||||
|
||||
# --- Single request ---
|
||||
url = base_url + f"{method}.json"
|
||||
data = parse.urlencode(params).encode("utf-8")
|
||||
result = do_request(url, data, args.timeout, source)
|
||||
|
||||
# Auto-cache user_id and timezone after successful user.current call
|
||||
status = result.get("status", 0)
|
||||
body = result.get("body", {})
|
||||
if method == "user.current" and status and status < 400 and isinstance(body, dict):
|
||||
user_result = body.get("result", {})
|
||||
uid = user_result.get("ID")
|
||||
tz = user_result.get("TIME_ZONE", "")
|
||||
if uid:
|
||||
try:
|
||||
cache_user_data(int(uid), tz, args.config_file)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if args.json:
|
||||
print(json.dumps(result, ensure_ascii=True, indent=2))
|
||||
else:
|
||||
print(json.dumps(result, ensure_ascii=True))
|
||||
return 0 if result.get("ok") else 1
|
||||
|
||||
|
||||
def _flatten_json(obj: dict, prefix: str = "") -> list[tuple[str, str]]:
|
||||
"""Flatten nested JSON dict into URL-encoded key=value pairs.
|
||||
|
||||
{"fields": {"TITLE": "X"}} → [("fields[TITLE]", "X")]
|
||||
{"select": ["ID", "TITLE"]} → [("select[]", "ID"), ("select[]", "TITLE")]
|
||||
"""
|
||||
pairs: list[tuple[str, str]] = []
|
||||
for key, value in obj.items():
|
||||
full_key = f"{prefix}[{key}]" if prefix else key
|
||||
if isinstance(value, dict):
|
||||
pairs.extend(_flatten_json(value, full_key))
|
||||
elif isinstance(value, list):
|
||||
for item in value:
|
||||
if isinstance(item, dict):
|
||||
pairs.extend(_flatten_json(item, f"{full_key}[]"))
|
||||
else:
|
||||
pairs.append((f"{full_key}[]", str(item)))
|
||||
else:
|
||||
pairs.append((full_key, str(value)))
|
||||
return pairs
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
94
bundled-skills/bitrix24/scripts/bitrix24_config.py
Normal file
94
bundled-skills/bitrix24/scripts/bitrix24_config.py
Normal file
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Shared helpers for Bitrix24 skill scripts."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
WEBHOOK_RE = re.compile(r"^https://(?P<host>[^/]+)/rest/(?P<user_id>\d+)/(?P<secret>[^/]+)/?$")
|
||||
DEFAULT_CONFIG_PATH = Path.home() / ".config" / "bitrix24-skill" / "config.json"
|
||||
|
||||
|
||||
def normalize_url(value: str) -> str:
|
||||
return value.strip().strip('"').strip("'").rstrip("/") + "/"
|
||||
|
||||
|
||||
def validate_url(value: str) -> str:
|
||||
normalized = normalize_url(value)
|
||||
if not WEBHOOK_RE.match(normalized):
|
||||
raise ValueError("Webhook format is invalid. Expected https://<host>/rest/<user_id>/<secret>/")
|
||||
return normalized
|
||||
|
||||
|
||||
def mask_url(value: str) -> str:
|
||||
match = WEBHOOK_RE.match(value)
|
||||
if not match:
|
||||
return value
|
||||
secret = match.group("secret")
|
||||
if len(secret) <= 4:
|
||||
masked = "*" * len(secret)
|
||||
else:
|
||||
masked = f"{secret[:2]}***{secret[-2:]}"
|
||||
return f"https://{match.group('host')}/rest/{match.group('user_id')}/{masked}/"
|
||||
|
||||
|
||||
def load_config(path: Path) -> dict:
|
||||
if not path.is_file():
|
||||
return {}
|
||||
try:
|
||||
data = json.loads(path.read_text(encoding="utf-8"))
|
||||
except Exception:
|
||||
return {}
|
||||
return data if isinstance(data, dict) else {}
|
||||
|
||||
|
||||
def save_config(path: Path, data: dict) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(json.dumps(data, ensure_ascii=True, indent=2) + "\n", encoding="utf-8")
|
||||
|
||||
|
||||
def persist_url_to_config(url: str, config_file: str | None = None) -> Path:
|
||||
config_path = Path(config_file).expanduser() if config_file else DEFAULT_CONFIG_PATH
|
||||
config = load_config(config_path)
|
||||
config["webhook_url"] = validate_url(url)
|
||||
save_config(config_path, config)
|
||||
return config_path
|
||||
|
||||
|
||||
def load_url(
|
||||
*,
|
||||
cli_url: str | None,
|
||||
config_file: str | None = None,
|
||||
) -> tuple[str | None, str]:
|
||||
if cli_url:
|
||||
return cli_url, "arg:url"
|
||||
|
||||
config_path = Path(config_file).expanduser() if config_file else DEFAULT_CONFIG_PATH
|
||||
config = load_config(config_path)
|
||||
config_url = config.get("webhook_url")
|
||||
if isinstance(config_url, str) and config_url.strip():
|
||||
return config_url, f"config:{config_path}"
|
||||
|
||||
return None, "missing"
|
||||
|
||||
|
||||
def get_cached_user(config_file: str | None = None) -> dict | None:
|
||||
"""Return cached user data (user_id, timezone) or None."""
|
||||
config_path = Path(config_file).expanduser() if config_file else DEFAULT_CONFIG_PATH
|
||||
config = load_config(config_path)
|
||||
user_id = config.get("user_id")
|
||||
if user_id is not None:
|
||||
return {"user_id": user_id, "timezone": config.get("timezone", "")}
|
||||
return None
|
||||
|
||||
|
||||
def cache_user_data(user_id: int, timezone: str = "", config_file: str | None = None) -> None:
|
||||
"""Save user_id and timezone to config for reuse."""
|
||||
config_path = Path(config_file).expanduser() if config_file else DEFAULT_CONFIG_PATH
|
||||
config = load_config(config_path)
|
||||
config["user_id"] = user_id
|
||||
if timezone:
|
||||
config["timezone"] = timezone
|
||||
save_config(config_path, config)
|
||||
100
bundled-skills/bitrix24/scripts/changelog_to_json.py
Normal file
100
bundled-skills/bitrix24/scripts/changelog_to_json.py
Normal file
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Convert CHANGELOG.md to docs/changelog.json for the landing page.
|
||||
|
||||
Usage: python3 scripts/changelog_to_json.py <current_version>
|
||||
|
||||
Parses CHANGELOG.md format:
|
||||
## 0.15.4 — 2026-03-11
|
||||
### Added
|
||||
- **Feature name**: description
|
||||
|
||||
Outputs docs/changelog.json:
|
||||
{"version": "0.15.4", "entries": [{"version": "0.15.4", "date": "2026-03-11", "items": [...]}]}
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
CHANGELOG = ROOT / "CHANGELOG.md"
|
||||
OUTPUT = ROOT / "docs" / "changelog.json"
|
||||
|
||||
VERSION_RE = re.compile(r"^## (\d+\.\d+\.\d+)\s*[—–-]\s*(\d{4}-\d{2}-\d{2})")
|
||||
SECTION_RE = re.compile(r"^### (.+)")
|
||||
ITEM_RE = re.compile(r"^- \*\*(.+?)\*\*:\s*(.+)")
|
||||
ITEM_PLAIN_RE = re.compile(r"^- (.+)")
|
||||
|
||||
|
||||
def parse_changelog() -> list[dict]:
|
||||
if not CHANGELOG.exists():
|
||||
return []
|
||||
|
||||
entries = []
|
||||
current: dict | None = None
|
||||
section = ""
|
||||
|
||||
for line in CHANGELOG.read_text(encoding="utf-8").splitlines():
|
||||
line = line.rstrip()
|
||||
|
||||
vm = VERSION_RE.match(line)
|
||||
if vm:
|
||||
if current:
|
||||
entries.append(current)
|
||||
current = {"version": vm.group(1), "date": vm.group(2), "items": []}
|
||||
section = ""
|
||||
continue
|
||||
|
||||
sm = SECTION_RE.match(line)
|
||||
if sm:
|
||||
section = sm.group(1)
|
||||
continue
|
||||
|
||||
if current is None:
|
||||
continue
|
||||
|
||||
im = ITEM_RE.match(line)
|
||||
if im:
|
||||
current["items"].append({
|
||||
"section": section,
|
||||
"title": im.group(1),
|
||||
"description": im.group(2),
|
||||
})
|
||||
continue
|
||||
|
||||
ip = ITEM_PLAIN_RE.match(line)
|
||||
if ip:
|
||||
current["items"].append({
|
||||
"section": section,
|
||||
"title": "",
|
||||
"description": ip.group(1),
|
||||
})
|
||||
|
||||
if current:
|
||||
entries.append(current)
|
||||
|
||||
return entries
|
||||
|
||||
|
||||
def main() -> None:
|
||||
version = sys.argv[1] if len(sys.argv) > 1 else ""
|
||||
entries = parse_changelog()
|
||||
|
||||
data = {
|
||||
"version": version or (entries[0]["version"] if entries else ""),
|
||||
"entries": entries[:10], # Last 10 releases
|
||||
}
|
||||
|
||||
OUTPUT.parent.mkdir(parents=True, exist_ok=True)
|
||||
OUTPUT.write_text(
|
||||
json.dumps(data, ensure_ascii=False, indent=2) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
print(f"Generated {OUTPUT} ({len(entries)} releases, showing {len(data['entries'])})")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
166
bundled-skills/bitrix24/scripts/check_webhook.py
Normal file
166
bundled-skills/bitrix24/scripts/check_webhook.py
Normal file
@@ -0,0 +1,166 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Check Bitrix24 webhook availability from saved config or explicit URL."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import socket
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from urllib import error, request
|
||||
|
||||
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||
if str(SCRIPT_DIR) not in sys.path:
|
||||
sys.path.insert(0, str(SCRIPT_DIR))
|
||||
|
||||
from bitrix24_config import load_url, mask_url, normalize_url, validate_url # noqa: E402
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description="Check a Bitrix24 webhook URL.")
|
||||
parser.add_argument("--url", help="Webhook URL to check")
|
||||
parser.add_argument("--config-file", help="Config file path override")
|
||||
parser.add_argument("--skip-http", action="store_true", help="Skip user.current probe")
|
||||
parser.add_argument("--json", action="store_true", help="Print JSON output")
|
||||
parser.add_argument("--timeout", type=float, default=10.0, help="HTTP timeout in seconds")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def resolve_dns(host: str) -> tuple[bool, list[str], str | None]:
|
||||
try:
|
||||
infos = socket.getaddrinfo(host, 443, type=socket.SOCK_STREAM)
|
||||
except OSError as exc:
|
||||
return False, [], str(exc)
|
||||
|
||||
ips = sorted({info[4][0] for info in infos if info[4]})
|
||||
return True, ips, None
|
||||
|
||||
|
||||
def probe_user_current(url: str, timeout: float) -> tuple[bool, int | None, dict[str, Any] | None, str | None]:
|
||||
target = url + "user.current.json"
|
||||
req = request.Request(target, headers={"Accept": "application/json"})
|
||||
try:
|
||||
with request.urlopen(req, timeout=timeout) as response:
|
||||
status = response.getcode()
|
||||
payload = response.read().decode("utf-8", errors="replace")
|
||||
except error.HTTPError as exc:
|
||||
try:
|
||||
payload = exc.read().decode("utf-8", errors="replace")
|
||||
except Exception:
|
||||
payload = ""
|
||||
return False, exc.code, safe_json(payload), f"HTTP {exc.code}"
|
||||
except Exception as exc:
|
||||
return False, None, None, str(exc)
|
||||
|
||||
data = safe_json(payload)
|
||||
if isinstance(data, dict) and data.get("error"):
|
||||
return False, status, data, str(data.get("error_description") or data["error"])
|
||||
return True, status, data, None
|
||||
|
||||
|
||||
def safe_json(payload: str) -> dict[str, Any] | None:
|
||||
if not payload:
|
||||
return None
|
||||
try:
|
||||
value = json.loads(payload)
|
||||
except Exception:
|
||||
return None
|
||||
return value if isinstance(value, dict) else {"raw": value}
|
||||
|
||||
|
||||
def build_result(args: argparse.Namespace) -> dict[str, Any]:
|
||||
raw_url, source = load_url(cli_url=args.url, config_file=args.config_file)
|
||||
result: dict[str, Any] = {
|
||||
"source": source,
|
||||
"url_found": raw_url is not None,
|
||||
"format_ok": False,
|
||||
"dns_ok": False,
|
||||
"http_ok": None if args.skip_http else False,
|
||||
}
|
||||
|
||||
if not raw_url:
|
||||
result["error"] = "No Bitrix24 webhook found in config"
|
||||
return result
|
||||
|
||||
normalized = normalize_url(raw_url)
|
||||
result["masked_url"] = mask_url(normalized)
|
||||
|
||||
try:
|
||||
validate_url(normalized)
|
||||
except ValueError:
|
||||
result["error"] = "Webhook format is invalid"
|
||||
return result
|
||||
|
||||
parts = normalized.split("/")
|
||||
host = parts[2]
|
||||
user_id = parts[4]
|
||||
result["format_ok"] = True
|
||||
result["host"] = host
|
||||
result["user_id"] = user_id
|
||||
|
||||
dns_ok, ips, dns_error = resolve_dns(host)
|
||||
result["dns_ok"] = dns_ok
|
||||
result["dns_ips"] = ips
|
||||
if dns_error:
|
||||
result["dns_error"] = dns_error
|
||||
return result
|
||||
|
||||
if args.skip_http:
|
||||
return result
|
||||
|
||||
http_ok, status, payload, http_error = probe_user_current(normalized, args.timeout)
|
||||
result["http_ok"] = http_ok
|
||||
result["http_status"] = status
|
||||
if payload is not None:
|
||||
result["http_payload"] = payload
|
||||
if http_error:
|
||||
result["http_error"] = http_error
|
||||
return result
|
||||
|
||||
|
||||
def print_plain(result: dict[str, Any]) -> None:
|
||||
for key in [
|
||||
"source",
|
||||
"url_found",
|
||||
"format_ok",
|
||||
"host",
|
||||
"user_id",
|
||||
"dns_ok",
|
||||
"dns_ips",
|
||||
"dns_error",
|
||||
"http_ok",
|
||||
"http_status",
|
||||
"http_error",
|
||||
"error",
|
||||
]:
|
||||
if key in result:
|
||||
print(f"{key}: {result[key]}")
|
||||
if "http_payload" in result:
|
||||
print("http_payload:")
|
||||
print(json.dumps(result["http_payload"], ensure_ascii=True, indent=2))
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
result = build_result(args)
|
||||
|
||||
if args.json:
|
||||
print(json.dumps(result, ensure_ascii=True, indent=2))
|
||||
else:
|
||||
print_plain(result)
|
||||
|
||||
if not result.get("url_found"):
|
||||
return 1
|
||||
if not result.get("format_ok"):
|
||||
return 1
|
||||
if not result.get("dns_ok"):
|
||||
return 1
|
||||
if result.get("http_ok") is False:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
47
bundled-skills/bitrix24/scripts/publish.sh
Normal file
47
bundled-skills/bitrix24/scripts/publish.sh
Normal file
@@ -0,0 +1,47 @@
|
||||
#!/bin/bash
|
||||
# Publish skill to ClawHub with automatic version/changelog sync.
|
||||
#
|
||||
# Usage: ./scripts/publish.sh 0.15.5
|
||||
#
|
||||
# What it does:
|
||||
# 1. Updates version in docs/index.html footer
|
||||
# 2. Generates docs/changelog.json from CHANGELOG.md
|
||||
# 3. Commits version bump
|
||||
# 4. Pushes to origin/main
|
||||
# 5. Publishes to ClawHub
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
VERSION="${1:?Usage: $0 <version>}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
cd "$ROOT_DIR"
|
||||
|
||||
echo "=== Publishing bitrix24@${VERSION} ==="
|
||||
|
||||
# 1. Update version in footer
|
||||
echo "→ Updating docs/index.html footer..."
|
||||
sed -i '' "s/Bitrix24 Skill v[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*/Bitrix24 Skill v${VERSION}/" docs/index.html
|
||||
|
||||
# 2. Generate changelog.json from CHANGELOG.md
|
||||
echo "→ Generating docs/changelog.json..."
|
||||
python3 "$SCRIPT_DIR/changelog_to_json.py" "$VERSION"
|
||||
|
||||
# 3. Commit
|
||||
echo "→ Committing version bump..."
|
||||
git add docs/index.html docs/changelog.json CHANGELOG.md
|
||||
git commit -m "Release v${VERSION}" --allow-empty || true
|
||||
|
||||
# 4. Push
|
||||
echo "→ Pushing to origin/main..."
|
||||
git push origin main
|
||||
|
||||
# 5. Publish to ClawHub
|
||||
echo "→ Publishing to ClawHub..."
|
||||
npx clawhub publish . --version "$VERSION"
|
||||
|
||||
echo ""
|
||||
echo "=== Done: bitrix24@${VERSION} published ==="
|
||||
echo "Wait ~90s for security scan, then install:"
|
||||
echo " ssh slon-mac \"export PATH=\\\$HOME/.nvm/versions/node/*/bin:/opt/homebrew/bin:/usr/local/bin:\\\$PATH && npx clawhub install bitrix24 --version ${VERSION} --force\""
|
||||
66
bundled-skills/bitrix24/scripts/save_webhook.py
Normal file
66
bundled-skills/bitrix24/scripts/save_webhook.py
Normal file
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Save a Bitrix24 webhook into stable config."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||
if str(SCRIPT_DIR) not in sys.path:
|
||||
sys.path.insert(0, str(SCRIPT_DIR))
|
||||
|
||||
from bitrix24_config import DEFAULT_CONFIG_PATH, load_config, save_config, validate_url # noqa: E402
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description="Save a Bitrix24 webhook into stable config.")
|
||||
parser.add_argument("--url", required=True, help="Bitrix24 webhook URL")
|
||||
parser.add_argument("--config-file", default=str(DEFAULT_CONFIG_PATH), help="Target config file path")
|
||||
parser.add_argument("--force", action="store_true", help="Overwrite existing saved webhook")
|
||||
parser.add_argument("--check", action="store_true", help="Run check_webhook.py after saving")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def run_check(config_path: Path) -> int:
|
||||
script = Path(__file__).with_name("check_webhook.py")
|
||||
cmd = [sys.executable, str(script), "--config-file", str(config_path), "--json"]
|
||||
result = subprocess.run(cmd, check=False)
|
||||
return result.returncode
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
try:
|
||||
normalized = validate_url(args.url)
|
||||
except ValueError as exc:
|
||||
print(str(exc), file=sys.stderr)
|
||||
return 1
|
||||
|
||||
config_path = Path(args.config_file).expanduser()
|
||||
config = load_config(config_path)
|
||||
found = isinstance(config.get("webhook_url"), str) and bool(config.get("webhook_url"))
|
||||
if found and not args.force:
|
||||
changed = False
|
||||
else:
|
||||
config["webhook_url"] = normalized
|
||||
save_config(config_path, config)
|
||||
changed = True
|
||||
|
||||
print(f"saved_to: {config_path}")
|
||||
if found and not args.force:
|
||||
print("note: saved webhook already existed and was kept unchanged; use --force to replace it")
|
||||
elif found and args.force:
|
||||
print("note: existing saved webhook was replaced")
|
||||
elif changed:
|
||||
print("note: webhook was saved")
|
||||
|
||||
if args.check:
|
||||
return run_check(config_path)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
1107
bundled-skills/sendforsign/SKILL.md
Normal file
1107
bundled-skills/sendforsign/SKILL.md
Normal file
File diff suppressed because it is too large
Load Diff
730
bundled-skills/sendforsign/references/api-reference.md
Normal file
730
bundled-skills/sendforsign/references/api-reference.md
Normal file
@@ -0,0 +1,730 @@
|
||||
# SendForSign API — Full Reference
|
||||
|
||||
Base URL: `https://api.sendforsign.com/api`
|
||||
Auth header: `X-Sendforsign-Key: {API_KEY}`
|
||||
Content-Type: `application/json` (unless uploading files)
|
||||
|
||||
All POST endpoints use a `data` envelope with an `action` field.
|
||||
|
||||
---
|
||||
|
||||
## CLIENTS — POST /api/client
|
||||
|
||||
### Create client
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "create",
|
||||
"client": {
|
||||
"fullname": "John Doe",
|
||||
"email": "john@example.com",
|
||||
"organization": "Acme Corp",
|
||||
"customKey": "optional-your-own-id",
|
||||
"users": [
|
||||
{ "fullname": "Jane Doe", "email": "jane@example.com", "customKey": "optional" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
Response 201: `{ clientKey, createTime, users: [{ userKey }] }`
|
||||
|
||||
### List clients
|
||||
```json
|
||||
{ "data": { "action": "list" } }
|
||||
```
|
||||
Response 200: array of `{ clientKey, fullname, email, organization, customKey, createTime, changeTime }`
|
||||
|
||||
### Read client
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "read",
|
||||
"client": { "clientKey": "..." }
|
||||
}
|
||||
}
|
||||
```
|
||||
Can also identify by: `fullname`, `email`, `organization`, or `customKey`.
|
||||
|
||||
### Update client
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "update",
|
||||
"client": {
|
||||
"clientKey": "...",
|
||||
"fullname": "New Name",
|
||||
"email": "new@email.com",
|
||||
"organization": "New Org",
|
||||
"customKey": "new-custom-key"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
Response 201: updated client with `changeTime`.
|
||||
|
||||
---
|
||||
|
||||
## USERS — POST /api/user
|
||||
|
||||
### Create user
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "create",
|
||||
"clientKey": "...",
|
||||
"user": { "fullname": "Jane", "email": "jane@example.com", "customKey": "optional" }
|
||||
}
|
||||
}
|
||||
```
|
||||
Response 201: `{ userKey, createTime }`
|
||||
|
||||
### List users
|
||||
```json
|
||||
{ "data": { "action": "list", "clientKey": "..." } }
|
||||
```
|
||||
Response 200: array of users with `userKey, fullname, email, customKey, createTime, changeTime`
|
||||
|
||||
### Read user
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "read",
|
||||
"clientKey": "...",
|
||||
"user": { "userKey": "..." }
|
||||
}
|
||||
}
|
||||
```
|
||||
Can also identify by: `fullname`, `email`, `customKey`.
|
||||
|
||||
### Update user
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "update",
|
||||
"clientKey": "...",
|
||||
"user": { "userKey": "...", "fullname": "...", "email": "...", "customKey": "..." }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CONTRACTS — POST /api/contract
|
||||
|
||||
### Create from HTML
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "create",
|
||||
"clientKey": "...",
|
||||
"userKey": "... (optional)",
|
||||
"contract": {
|
||||
"name": "My Contract",
|
||||
"value": "<p>Contract content in HTML</p>"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
Response 201: `{ contractKey, createTime }`
|
||||
|
||||
### Create from template
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "create",
|
||||
"clientKey": "...",
|
||||
"contract": { "templateKey": "...", "name": "My Contract" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Create from template + fill placeholders
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "create",
|
||||
"clientKey": "...",
|
||||
"contract": { "templateKey": "...", "name": "My Contract" },
|
||||
"placeholders": [
|
||||
{ "placeholderKey": "...", "value": "filled value" },
|
||||
{ "name": "placeholder_name", "value": "filled value" }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Create and send in one step
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "create",
|
||||
"clientKey": "...",
|
||||
"contract": { "name": "My Contract", "value": "<p>HTML content</p>" },
|
||||
"recipients": [
|
||||
{
|
||||
"email": "signer@example.com",
|
||||
"action": "sign",
|
||||
"fullname": "John Signer",
|
||||
"position": 1,
|
||||
"customMessage": "Please sign this document"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
Recipient `action` values: `"view"` | `"sign"` | `"approve"` | `"lock"`
|
||||
|
||||
### List contracts
|
||||
```json
|
||||
{ "data": { "action": "list", "clientKey": "..." } }
|
||||
```
|
||||
Response 200: array of `{ contractKey, name, status, createTime, changeTime }`
|
||||
|
||||
### Read contract
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "read",
|
||||
"clientKey": "...",
|
||||
"contract": { "contractKey": "..." }
|
||||
}
|
||||
}
|
||||
```
|
||||
Can also identify by `name`. Response includes `value` (HTML content) and `status`.
|
||||
|
||||
### Update contract
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "update",
|
||||
"clientKey": "...",
|
||||
"contract": { "contractKey": "...", "name": "New Name", "value": "<p>New HTML</p>" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Archive contract
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "archive",
|
||||
"clientKey": "...",
|
||||
"contract": { "contractKey": "..." }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Get contract timeline / audit trail
|
||||
```
|
||||
GET /api/contract_event?clientKey=CLIENT_KEY&contractKey=CONTRACT_KEY
|
||||
```
|
||||
Response 200: array of events with status changes, timestamps, recipient info.
|
||||
|
||||
---
|
||||
|
||||
## TEMPLATES — POST /api/template
|
||||
|
||||
### Create template
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "create",
|
||||
"clientKey": "...",
|
||||
"template": {
|
||||
"name": "My Template",
|
||||
"value": "<p>Hello {{recipient_name}}, this agreement...</p>"
|
||||
},
|
||||
"placeholders": [
|
||||
{ "name": "recipient_name", "value": "Default Value" }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
Response 201: `{ templateKey, createTime }`
|
||||
|
||||
### List templates
|
||||
```json
|
||||
{ "data": { "action": "list", "clientKey": "..." } }
|
||||
```
|
||||
|
||||
### Read template
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "read",
|
||||
"clientKey": "...",
|
||||
"template": { "templateKey": "..." }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Update template
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "update",
|
||||
"clientKey": "...",
|
||||
"template": { "templateKey": "...", "name": "New Name", "value": "<p>New HTML</p>" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Delete template
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "delete",
|
||||
"clientKey": "...",
|
||||
"template": { "templateKey": "..." }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Convert contract to template
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "convert",
|
||||
"clientKey": "...",
|
||||
"template": { "name": "Template Name", "contractKey": "..." }
|
||||
}
|
||||
}
|
||||
```
|
||||
Note: Content and placeholders are copied; placeholder values are cleared.
|
||||
|
||||
---
|
||||
|
||||
## PLACEHOLDERS — POST /api/placeholder
|
||||
|
||||
### Create placeholder
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "create",
|
||||
"clientKey": "...",
|
||||
"contractKey": "...",
|
||||
"placeholder": { "name": "client_name", "value": "John Doe" }
|
||||
}
|
||||
}
|
||||
```
|
||||
Response 201: `{ placeholderKey, id, createTime, changeTime, type, position }`
|
||||
|
||||
### List placeholders
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "list",
|
||||
"clientKey": "...",
|
||||
"contractKey": "..."
|
||||
}
|
||||
}
|
||||
```
|
||||
Use `templateKey` instead of `contractKey` for template placeholders.
|
||||
Response: array of `{ id, name, value, placeholderKey, position, createTime, changeTime }`
|
||||
|
||||
### Update placeholder (basic)
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "update",
|
||||
"clientKey": "...",
|
||||
"contractKey": "...",
|
||||
"placeholder": { "placeholderKey": "...", "name": "new_name", "value": "new value" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Place basic placeholder on PDF ("pdfbasic")
|
||||
|
||||
CRITICAL: All values inside `insertion` must be **strings**. `id` starts from `"1"`, not `"0"`. `action` and `clientKey` are required inside each insertion item. Violations cause silent failure (201 response but field not placed).
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "update",
|
||||
"clientKey": "...",
|
||||
"contractKey": "...",
|
||||
"placeholders": [
|
||||
{
|
||||
"placeholderKey": "...",
|
||||
"insertion": [
|
||||
{
|
||||
"action": "update",
|
||||
"clientKey": "...",
|
||||
"id": "1",
|
||||
"pageId": "0",
|
||||
"width": "100",
|
||||
"height": "100",
|
||||
"positionX": "1",
|
||||
"positionY": "1"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Place special placeholder on PDF ("pdfspecial")
|
||||
Special types: `1` = date signed, `2` = fullname, `3` = email, `4` = signature
|
||||
|
||||
The `placeholderKey` for special placeholders comes from the placeholder list response (fetched with `X-Sendforsign-Component: true`). Special placeholders are auto-created when a recipient is added. Their keys follow the pattern `{recipientKey}_{specialType}`.
|
||||
|
||||
Same rules as basic: all `insertion` values must be **strings**, `id` starts from `"1"`, and `action`+`clientKey` are required inside insertion.
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "update",
|
||||
"clientKey": "...",
|
||||
"contractKey": "...",
|
||||
"placeholders": [
|
||||
{
|
||||
"placeholderKey": "e8c68d6e-27b8-4388-b90d-47f7b3853c4c_4",
|
||||
"isSpecial": true,
|
||||
"specialType": 4,
|
||||
"insertion": [
|
||||
{
|
||||
"action": "update",
|
||||
"clientKey": "...",
|
||||
"id": "1",
|
||||
"pageId": "0",
|
||||
"positionX": "56",
|
||||
"positionY": "79",
|
||||
"width": "100",
|
||||
"height": "50"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Update placeholder — Table
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "update",
|
||||
"clientKey": "...",
|
||||
"contractKey": "...",
|
||||
"placeholder": {
|
||||
"placeholderKey": "...",
|
||||
"table": {
|
||||
"columns": ["Item", "Qty", "Price"],
|
||||
"rows": [
|
||||
["Widget A", 10, 9.99],
|
||||
["Widget B", 5, 19.99]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
Note: `placeholderKey` and `table` are nested inside `placeholder` (singular), not at the top `data` level.
|
||||
|
||||
### Delete placeholder
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "delete",
|
||||
"clientKey": "...",
|
||||
"contractKey": "...",
|
||||
"placeholder": { "placeholderKey": "..." }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## RECIPIENTS — POST /api/recipient
|
||||
|
||||
### Create recipient
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "create",
|
||||
"clientKey": "...",
|
||||
"contractKey": "...",
|
||||
"userKey": "... (optional)",
|
||||
"recipients": [
|
||||
{
|
||||
"action": "sign",
|
||||
"fullname": "John Signer",
|
||||
"email": "signer@example.com",
|
||||
"position": 1,
|
||||
"customMessage": "Please review and sign"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
Note: `recipients` is an ARRAY — even for a single recipient, wrap it in `[]`.
|
||||
Response 200: `{ result: true, recipientKey }`
|
||||
|
||||
### List recipients
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "list",
|
||||
"clientKey": "...",
|
||||
"contractKey": "... (optional)"
|
||||
}
|
||||
}
|
||||
```
|
||||
Response: array of `{ id, recipientKey, email, fullname, customMessage, position, action, createTime, changeTime }`
|
||||
|
||||
### Update recipient
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "update",
|
||||
"clientKey": "...",
|
||||
"contractKey": "...",
|
||||
"recipient": {
|
||||
"recipientKey": "...",
|
||||
"action": "approve",
|
||||
"fullname": "New Name",
|
||||
"email": "new@email.com",
|
||||
"position": 2,
|
||||
"customMessage": "Updated message"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Delete recipient
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "delete",
|
||||
"clientKey": "...",
|
||||
"contractKey": "...",
|
||||
"recipient": { "recipientKey": "..." }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Send contract to recipients
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "send",
|
||||
"clientKey": "...",
|
||||
"contractKey": "...",
|
||||
"recipients": [
|
||||
{
|
||||
"email": "signer@example.com",
|
||||
"action": "sign",
|
||||
"fullname": "John Signer",
|
||||
"position": 1,
|
||||
"customMessage": "Please sign"
|
||||
},
|
||||
{
|
||||
"email": "viewer@example.com",
|
||||
"action": "view",
|
||||
"position": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
To update existing recipient while sending, include `recipientKey` in the recipient object.
|
||||
|
||||
---
|
||||
|
||||
## DOCUMENTS
|
||||
|
||||
### Download PDF
|
||||
```
|
||||
GET /api/download_pdf?clientKey=CLIENT_KEY&contractKey=CONTRACT_KEY
|
||||
Header: X-Sendforsign-Key: API_KEY
|
||||
```
|
||||
Response: binary PDF (ArrayBuffer / file stream). Save with `--output file.pdf` in curl.
|
||||
|
||||
### Download Word (DOCX)
|
||||
```
|
||||
GET /api/download_docx?clientKey=CLIENT_KEY&contractKey=CONTRACT_KEY
|
||||
```
|
||||
Save with `--output file.docx`.
|
||||
|
||||
### Upload PDF
|
||||
First create contract with `contractType: "pdf"`, then:
|
||||
```
|
||||
POST /api/upload_pdf?clientKey=CLIENT_KEY&contractKey=CONTRACT_KEY
|
||||
Content-Type: multipart/form-data
|
||||
Field name: "pdf", MIME type: application/pdf
|
||||
```
|
||||
```bash
|
||||
curl -X POST "https://api.sendforsign.com/api/upload_pdf?clientKey=...&contractKey=..." \
|
||||
-H "X-Sendforsign-Key: $API_KEY" \
|
||||
-F "pdf=@document.pdf;type=application/pdf"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## WEBHOOKS — POST /api/webhook
|
||||
|
||||
Event types: `Contract.created`, `Contract.sent`, `Contract.seen`, `Contract.approved`, `Contract.signed`, `Contract.fully_signed`
|
||||
|
||||
Webhook payloads include: `clientKey`, `contractKey`, `createTime`, `status`, recipient info (email, fullname, recipientKey), contract name.
|
||||
|
||||
### Create webhook
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "create",
|
||||
"clientKey": "...",
|
||||
"webhooks": [
|
||||
{
|
||||
"url": "https://yourapp.com/webhook",
|
||||
"created": true,
|
||||
"seen": true,
|
||||
"sent": true,
|
||||
"approved": true,
|
||||
"signed": true,
|
||||
"fullySigned": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
Response 201: `{ webhookKey, url, secret (whsec_...), createTime }`
|
||||
|
||||
### List webhooks
|
||||
```json
|
||||
{ "data": { "action": "list", "clientKey": "..." } }
|
||||
```
|
||||
|
||||
### Read webhook
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "read",
|
||||
"clientKey": "...",
|
||||
"webhooks": [{ "webhookKey": "..." }]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Update webhook
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "update",
|
||||
"clientKey": "...",
|
||||
"webhooks": [
|
||||
{
|
||||
"webhookKey": "...",
|
||||
"url": "https://new-url.com/webhook",
|
||||
"signed": false,
|
||||
"fullySigned": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Delete webhook
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"action": "delete",
|
||||
"clientKey": "...",
|
||||
"webhooks": [{ "webhookKey": "..." }]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## SETTINGS — POST /api/settings
|
||||
|
||||
### Custom branding
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"type": "brandings",
|
||||
"clientKey": "...",
|
||||
"branding": {
|
||||
"emailSenderName": "My Company",
|
||||
"emailExplainer": "Custom email footer text",
|
||||
"emailLogo": "https://example.com/logo.png",
|
||||
"buttonColor": "#FF5733",
|
||||
"emailDomain": "mail.mycompany.com",
|
||||
"recipientLinkDomain": "sign.mycompany.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
Note: `emailDomain` and `recipientLinkDomain` require Business plan.
|
||||
|
||||
### Email notifications
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"type": "email_notifications",
|
||||
"clientKey": "...",
|
||||
"notification": {
|
||||
"approved": true,
|
||||
"signed": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## TOKENS — POST /api/token
|
||||
|
||||
Tokens are short-lived (1800 seconds / 30 min). Use for OAuth 2.0 compliant flows.
|
||||
|
||||
### Generate platform token
|
||||
```json
|
||||
{
|
||||
"apiKey": "...",
|
||||
"apiSecret": "..."
|
||||
}
|
||||
```
|
||||
No auth header needed for this endpoint.
|
||||
|
||||
### Generate client token
|
||||
```json
|
||||
{
|
||||
"apiKey": "...",
|
||||
"apiSecret": "...",
|
||||
"clientKey": "..."
|
||||
}
|
||||
```
|
||||
|
||||
### Revoke token
|
||||
```json
|
||||
{
|
||||
"action": "revoke",
|
||||
"token": "..."
|
||||
}
|
||||
```
|
||||
Returns 401 on expired tokens — handle with token refresh logic.
|
||||
|
||||
---
|
||||
|
||||
## AI API (Beta) — POST https://aiapi.sendforsign.com/webhook/aiapi
|
||||
|
||||
```bash
|
||||
curl -X POST https://aiapi.sendforsign.com/webhook/aiapi \
|
||||
-H "X-Sendforsign-Key: $API_KEY" \
|
||||
-H "clientKey: $CLIENT_KEY" \
|
||||
-H "secretKey: $SECRET_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"message": "Create a non-disclosure agreement between TechCorp and John Doe"}'
|
||||
```
|
||||
|
||||
Capabilities:
|
||||
1. Generate contracts in free format with legal best practices
|
||||
2. Create contracts from existing templates
|
||||
3. Populate template placeholders with provided data
|
||||
4. List available templates
|
||||
5. View placeholder fields for templates
|
||||
|
||||
Response: AI-generated text + contract details including unique identifier and 30-minute preview URL.
|
||||
177
bundled-skills/sendforsign/scripts/render_pdf.py
Normal file
177
bundled-skills/sendforsign/scripts/render_pdf.py
Normal file
@@ -0,0 +1,177 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Render PDF pages to PNG images AND extract exact text positions for SendForSign.
|
||||
|
||||
Usage:
|
||||
python render_pdf.py <pdf_path> [output_dir]
|
||||
|
||||
Output: JSON with per-page image paths + text elements with pre-calculated API coordinates.
|
||||
|
||||
Coordinate system:
|
||||
SendForSign API uses a 1000px-wide coordinate system for ALL PDFs.
|
||||
|
||||
Text positions come from pdftohtml -xml (PDF points, page width ~595pt for A4).
|
||||
They are scaled to 1000px space using: api_coord = round(pdf_pt * 1000 / page_width_pts)
|
||||
|
||||
Images are rendered at exactly 1000px wide for visual reference.
|
||||
Pixel coordinate in the image = API coordinate directly.
|
||||
|
||||
Workflow:
|
||||
1. Read image_path to visually identify which fields need placeholders.
|
||||
2. Search text_elements for the exact text string to get precise api_x/api_y/api_w/api_h.
|
||||
3. Use those api_* values directly in the placeholder placement API call.
|
||||
4. For non-text areas (images, empty boxes), estimate from the 1000px image visually.
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import json
|
||||
import sys
|
||||
import re
|
||||
import struct
|
||||
import tempfile
|
||||
import xml.etree.ElementTree as ET
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def get_png_dimensions(png_path: str) -> tuple[int, int]:
|
||||
"""Read width and height from PNG header (bytes 16-24)."""
|
||||
try:
|
||||
with open(png_path, "rb") as f:
|
||||
f.seek(16)
|
||||
w = struct.unpack(">I", f.read(4))[0]
|
||||
h = struct.unpack(">I", f.read(4))[0]
|
||||
return w, h
|
||||
except Exception:
|
||||
return 0, 0
|
||||
|
||||
|
||||
def extract_text_positions(pdf_path: str, output_dir: Path) -> dict:
|
||||
"""
|
||||
Run pdftohtml -xml to get per-page text elements with exact PDF-point coordinates.
|
||||
Returns dict: {page_number (1-indexed): {"width_pts": W, "height_pts": H, "texts": [...]}}
|
||||
Each text: {"text": str, "left": int, "top": int, "width": int, "height": int}
|
||||
"""
|
||||
xml_base = str(output_dir / "pdftext")
|
||||
result = subprocess.run(
|
||||
["pdftohtml", "-xml", "-noframes", "-zoom", "1", str(pdf_path), xml_base],
|
||||
capture_output=True, text=True
|
||||
)
|
||||
xml_path = Path(xml_base + ".xml")
|
||||
if not xml_path.exists():
|
||||
return {}
|
||||
|
||||
try:
|
||||
tree = ET.parse(str(xml_path))
|
||||
root = tree.getroot()
|
||||
except ET.ParseError:
|
||||
return {}
|
||||
|
||||
pages = {}
|
||||
for page_el in root.findall("page"):
|
||||
page_num = int(page_el.get("number", 1))
|
||||
page_w = float(page_el.get("width", 595))
|
||||
page_h = float(page_el.get("height", 841))
|
||||
scale = 1000.0 / page_w
|
||||
|
||||
texts = []
|
||||
for text_el in page_el.findall("text"):
|
||||
raw = "".join(text_el.itertext()).strip()
|
||||
if not raw:
|
||||
continue
|
||||
left = int(text_el.get("left", 0))
|
||||
top = int(text_el.get("top", 0))
|
||||
w = int(text_el.get("width", 0))
|
||||
h = int(text_el.get("height", 0))
|
||||
texts.append({
|
||||
"text": raw,
|
||||
"pdf_left": left, "pdf_top": top, "pdf_width": w, "pdf_height": h,
|
||||
"api_x": round(left * scale),
|
||||
"api_y": round(top * scale),
|
||||
"api_w": round(w * scale),
|
||||
"api_h": round(h * scale),
|
||||
})
|
||||
|
||||
pages[page_num] = {
|
||||
"width_pts": page_w,
|
||||
"height_pts": page_h,
|
||||
"scale_to_api": round(scale, 6),
|
||||
"texts": texts,
|
||||
}
|
||||
return pages
|
||||
|
||||
|
||||
def render_pdf(pdf_path: str, output_dir: str = None) -> dict:
|
||||
pdf_path = Path(pdf_path).resolve()
|
||||
|
||||
if not pdf_path.exists():
|
||||
return {"error": f"File not found: {pdf_path}"}
|
||||
|
||||
if output_dir is None:
|
||||
output_dir = Path(tempfile.mkdtemp(prefix="sfs_pdf_"))
|
||||
else:
|
||||
output_dir = Path(output_dir)
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Render images at exactly 1000px wide
|
||||
prefix = str(output_dir / "page")
|
||||
render_result = subprocess.run(
|
||||
["pdftoppm", "-scale-to-x", "1000", "-scale-to-y", "-1", "-png",
|
||||
str(pdf_path), prefix],
|
||||
capture_output=True, text=True
|
||||
)
|
||||
if render_result.returncode != 0:
|
||||
return {"error": f"pdftoppm failed: {render_result.stderr}"}
|
||||
|
||||
image_files = sorted(output_dir.glob("page-*.png"))
|
||||
if not image_files:
|
||||
image_files = sorted(output_dir.glob("page.png"))
|
||||
|
||||
# Extract exact text positions from PDF
|
||||
text_data = extract_text_positions(str(pdf_path), output_dir)
|
||||
|
||||
pages = []
|
||||
for i, img_file in enumerate(image_files):
|
||||
w_px, h_px = get_png_dimensions(str(img_file))
|
||||
page_num = i + 1 # pdftohtml uses 1-indexed pages
|
||||
page_texts = text_data.get(page_num, {})
|
||||
|
||||
scale_to_api = round(1000.0 / w_px, 6) if w_px > 0 else 1.0
|
||||
|
||||
pages.append({
|
||||
"page_id": i,
|
||||
"image_path": str(img_file),
|
||||
"width_px": w_px,
|
||||
"height_px": h_px,
|
||||
"scale_to_api": scale_to_api,
|
||||
"text_elements": page_texts.get("texts", []),
|
||||
"coordinate_hint": (
|
||||
"Image is 1000px wide — pixel = API coord for visual estimates. "
|
||||
"For text fields: use api_x/api_y/api_w/api_h from text_elements directly."
|
||||
),
|
||||
})
|
||||
|
||||
return {
|
||||
"pdf_path": str(pdf_path),
|
||||
"page_count": len(pages),
|
||||
"pages": pages,
|
||||
"instructions": (
|
||||
"To place a placeholder over existing text: "
|
||||
"1) Search text_elements for the text string. "
|
||||
"2) Use its api_x/api_y/api_w/api_h directly as positionX/positionY/width/height. "
|
||||
"3) Add 2-3px padding if needed (increase width/height slightly). "
|
||||
"For non-text areas: read image_path visually and estimate pixel coords (= API coords at 1000px width). "
|
||||
"Use page_id as pageId in the API (0-indexed)."
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python render_pdf.py <pdf_path> [output_dir]", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
pdf = sys.argv[1]
|
||||
out = sys.argv[2] if len(sys.argv) > 2 else None
|
||||
|
||||
result = render_pdf(pdf, out)
|
||||
print(json.dumps(result, indent=2, ensure_ascii=False))
|
||||
Reference in New Issue
Block a user