Источник: smarty-code/smarty-backend-stable/smarty-automation/docs/API.md
Дата актуальности: 2026-06-13. Read-only документация.
Все контракты — RabbitMQ очередь automation. Формат сообщения:
{
"type": "<имя_события>",
"data": { /* payload, специфичный для события */ }
}
Доставка: at-most-once (acker.ack() всегда, nack не используется). Retry — только через бизнес-логику (reSend / delayed events).
Продюсер: бот, триггер автоматизации, любой модуль через mq.publish('automation', 'requestUrl', data).
Консьюмер: smarty-automation воркер.
| Поле |
Тип |
Обяз. |
По умолчанию |
Описание |
url |
string |
да |
— |
URL внешнего сервиса. Валидация: validator.isURL, протоколы http/https, в production — require_tld: true. |
method |
string |
да |
— |
HTTP-метод (GET, POST, PATCH, ...). Для multipart нормализуется в POST/PATCH. |
params |
Array<{fieldName, fieldValue}> |
нет |
[] |
Параметры в формате «ключ-значение». fieldValue может быть {isFile: true, id} для загрузки файла. |
paramsRaw |
Object |
нет |
null |
Альтернативный формат параметров (объект). Используется если Content-Type не JSON, или как дополнение к params. |
headers |
Array<{fieldName, fieldValue}> |
нет |
— |
HTTP-заголовки. Content-Type: multipart/form-data включает multipart-режим. |
isFileResponse |
boolean |
нет |
false |
Если true — ответ скачивается как файл и сохраняется в Attaches. |
performer |
{_id, type, _wsId, constructor.modelName} |
нет |
{} |
Профиль-инициатор (для привязки скачанного файла). |
requestId |
string |
нет |
— |
Идентификатор запроса (для трейсинга). |
timeoutRequest |
number |
нет |
10000 |
Таймаут HTTP-запроса, мс. |
reSendTries |
Array<{count, timeout}> |
нет |
[] |
Этапы retry при ошибке (не из retryErrors). |
resultHandler |
{contextData, event, queue} |
нет |
— |
Куда отправить результат. Если нет — результат теряется. |
- Успех:
emitBgEvent.sendEvent(resultHandler.event, {isError: false, contextData, response}, resultHandler.queue)
- Ошибка (retry исчерпан):
emitBgEvent.sendEvent(resultHandler.event, {isError: true, contextData, response: error}, resultHandler.queue)
- Файл-ответ: создаётся
Attaches, в response — _id вложения.
| Условие |
Этапы retry |
Таймаут |
Ошибка из retryErrors (Premature close, ECONNRESET, timeout и т.д.) |
5 попыток |
3000 мс |
| Прочие ошибки |
data.reSendTries (из сообщения) |
По этапам из reSendTries |
validator.isURL(encodeURI(url), {protocols: ['http','https'], require_tld: NODE_ENV==='production'}) — если URL невалиден, событие тихо пропускается (лог warn).
params и paramsRaw могут использоваться одновременно — paramsRaw добавляется в params если Content-Type не «plain» (не JSON).
- При
Content-Type без application/json (isPlain=true) — paramsRaw передаётся как options.data напрямую (без преобразования).
- Файлы из
params ищутся сначала в Attaches, потом в File (из smarty-media). Кэш filesMap — в рамках одного сообщения.
Продюсер: модуль интеграции с Dialogflow.
Консьюмер: smarty-automation воркер.
| Поле |
Тип |
Обяз. |
Описание |
url |
string |
да |
URL целевого API. |
method |
string |
да |
HTTP-метод. |
paramsRaw |
Object |
нет |
Тело запроса. |
accessData |
Object |
да |
OAuth-данные (см. ниже). |
contextData |
any |
да |
Контекст для awaitRequestHook. |
| Поле |
Тип |
Описание |
client_email |
string |
Service account email (JWT iss). |
private_key |
string |
RSA private key (PEM). |
private_key_id |
string |
ID ключа — используется как ключ кэша. |
token_uri |
string |
URL OAuth token endpoint. |
- Успех:
emitBgEvent.sendEvent('awaitRequestHook', {isError: false, contextData, response: JSON.stringify(response)}, 'messenger_integration')
- Ошибка:
emitBgEvent.sendEvent('awaitRequestHook', {isError: true, contextData, response: JSON.stringify({error})}, 'messenger_integration')
- Ключ:
accessData.private_key_id.
- TTL:
expires_in - 10 секунд (обновление на 10 сек раньше истечения).
- Сбрасывается при рестарте воркера (in-memory объект).
- Scope захардкожен:
https://www.googleapis.com/auth/dialogflow. Для других Google API нужен рефакторинг.
- Нет обработки 401 — если токен отозван, кэш не инвалидируется, все запросы будут падать до истечения
expiresIn.
Продюсер: бот, триггер автоматизации.
Консьюмер: smarty-automation воркер.
| Поле |
Тип |
Обяз. |
По умолчанию |
Описание |
modelName |
string |
да |
— |
Имя модели CRM (requisition, task, company, ...). |
performer |
{_id, type} |
да |
— |
Профиль-создатель. |
content |
Array<{fieldName, fieldValue}> |
да |
— |
Поля создаваемого объекта. |
links |
Array<{_id, modelName}> |
нет |
[] |
Связи с другими объектами. |
isBot |
boolean |
нет |
false |
Флаг «создал бот» — влияет на проверку квот. |
fieldName |
Обработка |
attaches |
ID вложений накапливаются, затем копируются к объекту. Изображения заменяют [[id]] на markdown . |
geoLocation |
Преобразуется в {title, coordinates: []}. |
startDate |
Для assignments — fallback на Date.now() если пусто. |
phone |
Оборачивается в {type: 0, number} (Mobile). |
| Прочие |
Если модель имеет FieldParserMarkdown и поле markdown-типа — markdownEscape(). |
- Создание объекта через
Model.createObjectLowLevel().
- Создание связей через
LinksModel.linkObjects().
- Проверка квот (
ObjectUnderQuota) — если бот и не requisition, overRate выставляется.
checkAttachesAvailable() — вложения должны быть скачаны (retry до 31 × 10 сек).
Продюсер: триггер автоматизации.
Консьюмер: smarty-automation воркер.
| Поле |
Тип |
Обяз. |
Описание |
modelName |
string |
да |
Модель объекта (чат). |
performer |
{_id, type} |
да |
Профиль-инициатор. |
employeesIds |
string[] |
да |
ID сотрудников для добавления. |
ExtChatParticipants.addParticipantsToChat(profile, chat, employeesIds).
- Нет результата/колбэка — fire-and-forget.
Продюсер: startCheckingRequisitionsToArchive() (инициализация) + сам себя (каждый час).
Консьюмер: smarty-automation воркер.
| Поле |
Тип |
Обяз. |
Описание |
wsId |
string |
да |
ID воркспейса. |
- Найти все заявки (
requisition) воркспейса, не в архиве.
- Для каждой: найти стадии группы, взять последнюю.
- Если заявка на последней стадии ≥ 30 дней (
moveWaitTime):
isImportant=true → перенос в группу 672a02cff191c469741723e7.
- Иначе →
inArchive=true, _archivedOn=Date.now().
- Перезапланировать себя через 1 час (
sendDelayedEvent).
- Обновление заявок в MongoDB.
- Delayed event через 1 час — самосcheduling.
Продюсер: внешний сервис через API → smarty-system-events.
Консьюмер: smarty-automation воркер (переиспользует обработчик из smarty-system-events).
| Поле |
Тип |
Обяз. |
Описание |
wsApiId |
string |
нет |
ID WorkspaceApi. |
token |
string |
нет |
Значение WsObjectToken. |
body |
any |
да |
Тело hook-запроса. |
- Найти
WsObjectToken по token.
- Если не найден — тихий выход (без ошибки).
- Проверить
Model.is('Hookable') — если нет — выход.
- Найти объект по
token.targetId.
object.hookExecute({profile, body}).
- Зависит от реализации
hookExecute конкретной модели.
Модуль: lib/uploadFileFromRequest.js
buildRequestOptions(url, method, params, headers, isFormData, timeout) → options
| Параметр |
Тип |
По умолчанию |
Описание |
url |
string |
— |
URL (будет encodeURI()). |
method |
string |
— |
HTTP-метод. |
params |
Array\|Object\|null |
— |
Параметры (массив {fieldName, fieldValue} или raw-объект). |
headers |
Array<{fieldName, fieldValue}> |
— |
Заголовки. |
isFormData |
boolean |
false |
Принудительный multipart. |
timeout |
number |
10000 |
Таймаут, мс. |
{
url: string, // encodeURI(url)
method: string, // нормализованный метод
timeout: number,
headers: Object, // { [lowercase_name]: value }
data?: any // body (FormData | Object | raw)
}
¶ Логика сборки body
| Условие |
options.data |
sendMultipart=true (isFormData или Content-Type: multipart/form-data) |
FormData из buildUploadForm(params) |
isPlain=true (Content-Type не JSON) |
params как есть (raw) |
params — непустой массив |
Объект {fieldName: fieldValue} |
params пуст/отсутствует |
undefined (не передаётся) |
Нормализация метода: если sendMultipart || method in [post, patch] → метод остаётся; иначе для multipart принудительно POST.
makeExternalRequest(performer, options, isFileResponse, requestId) → response
| Режим |
Поведение |
Возврат |
isFileResponse=true |
downloadFile() → Attaches.create() |
_id вложения (string) |
isFileResponse=false |
request(options, false, requestId) |
Ответ HTTP-клиента (объект) |
| Сообщение |
Источник |
Premature close |
Поток оборван |
[REQUEST] socket hang up |
Соединение закрыто сервером |
[REQUEST] read ECONNRESET |
Сброс соединения |
external server error |
Ответ 5xx |
Cancel: Timeout of 2000ms. |
Таймаут 2 сек |
Cancel: Timeout of 60000ms. |
Таймаут 60 сек |
| Ошибка |
Где |
Условие |
ServerError('attaches not downloaded') |
checkAttachesAvailable.js |
Вложения не скачаны после 31 попытки (310 сек) ИЛИ ошибка загрузки. |
AccessDenied |
AutomationObject.js |
Профиль без права can_manage_workflow и без админ-права manage_automation. |
| Ситуация |
Действие |
| Ошибка в обработчике события |
HandlersMap ловит → лог → acker.ack() (не nack). |
reSend не исчерпан |
Событие переотправляется через delayed event. |
reSend исчерпан |
handleResponse(data, error, true) — результат с isError=true. |
Невалидный URL (requestUrl) |
Тихий пропуск, лог warn. |
Токен не найден (dialogHook) |
Тихий выход, без ошибки. |
Коллекция: automation_workflow
Схема: algorithm (пустой объект — заглушка)
Плагины:
| Плагин |
Роль |
AutomationObject |
PublicInterface + CheckAccessRights + WsObject + WsMiscParams |
ObjectWithAvatar |
Поддержка аватара/иконки |
Права доступа: can_manage_workflow (access_right) или manage_automation (admin_right).
| Область |
Причина |
Внутренности object.hookExecute() |
Зависит от конкретной модели, определяется плагином Hookable в smarty-db. |
Model.createObjectLowLevel() |
Глубокая логика создания объектов CRM — в smarty-db. |
downloadFile() |
Реализация в smarty-media/lib/downloadFile.js. |
lib/utils/request |
HTTP-клиент-обёртка — в lib/utils/request.js. |
mq.publish() / queueTimer |
Инфраструктура RabbitMQ — в smarty-db/db/mq.js. |
| Реальные продюсеры событий |
Кто отправляет requestUrl/createObject — определяется конфигурацией автоматизаций (боты, триггеры). |