Публикация workflow как действий
Превратите workflow в вызываемое действие, которое AI-агент может запустить через REST или MCP.
Публикация workflow как действий
Triggo предоставляет внешним вызывающим две поверхности: REST API по адресу /api/v1/runtime/ и MCP-сервер по POST /mcp. Обе показывают один и тот же ресурс — опубликованное действие — и обе отказываются видеть всё, что не опубликовано. Эта страница описывает полный путь от «у меня есть workflow, который работает на холсте» до «мой агент может вызвать его по slug с Bearer-токеном».
Callable и event-driven workflow
У каждого workflow в Triggo есть type, который определяет, как он запускается. Тип хранится в таблице workflows и принимает одно из двух значений:
event_driven— запускается платформой. Срабатывает вебхук, отсчитывается расписание, триггер коннектора эмитирует событие — и workflow исполняется с тем полезным грузом, который доставила платформа. Event-driven workflow нельзя вызвать напрямую от агента. У них ровно один узелtrigger; попытка опубликовать такой через runtime API отклоняется.callable— запускается явным вызовом агента. Это та форма, которую вы публикуете как действие. Callable workflow должен иметь ровно один узелaction_input(который определяет схему ввода, заполняемую вызывающим) и хотя бы один узелreturn_output(который определяет, что возвращает вызов). Валидатор следит за обоими правилами: «Callable workflow must have exactly 1 Action Input node» / «Callable workflow must have at least 1 Return Output node».
Тип выводится в момент создания из формы определения: если на холсте есть плейсхолдеры Action Input и Return Output — workflow callable; если есть trigger-узел — event-driven. Сегодня это плейсхолдерные типы узлов (action_input, return_output) — спецификация system-nodes-overhaul редизайнит их в полноценные узлы Manual Trigger и Return, но поведение, описанное здесь, соответствует текущему коду.
Форма callable workflow
Callable workflow — это три вещи, сшитые вместе:
- Узел Action Input в начале. Его массив
data.properties(name, type, required) становится публичной схемой ввода действия. В момент деплоя схема выводитсяderiveInputSchemaFromDefinition()в объект, похожий на JSON Schema:{ type: "object", properties: { <name>: { type: <type> } }, required: [...] }. - Один или несколько узлов Return Output в конце. Их
data.propertiesстановятся схемой вывода черезderiveOutputSchemaFromDefinition()—{ type: "object", properties: {... } }, илиnull, если свойства вывода не заданы. - Любые узлы между ними делают основную работу — действия коннекторов, условия, трансформации, code-узлы, циклы, вызовы LLM. Сопоставление полей
{{action_input.field}}пробрасывает ввод вызывающего в узлы ниже по графу.
Схема ввода — это публичный контракт действия. Её изменение — breaking change для каждого вызывающего. Добавить новое необязательное поле безопасно; переименовать существующее поле, сделать поле обязательным или изменить его тип — нет. Вызывающие валидируются против этой схемы ещё до того, как запуск попадает в очередь (см. «Ошибки» ниже).
Публикация
На холсте основная кнопка в панели инструментов для callable workflow — Activate при первом деплое и Publish Action при каждом последующем (вторичная кнопка Run manually находится рядом). Полное описание состояния первичного действия с учётом триггеров см. в разделе Активация и версии (event-driven workflow проходят ту же транзакцию, но надпись на кнопке — Activate/Stop). Клик по первичной кнопке выполняет три действия одной транзакцией:
- Валидирует текущий черновик против правил callable (1
action_input, >=1return_output) или правил event-driven (ровно 1 триггер), плюс лимиты сложности по узлам/связям. - Архивирует предыдущую задеплоенную версию (статус меняется с
deployedнаarchived) и повышает черновик до новой строкиworkflowVersionsсо статусомdeployed. - Для callable workflow выводит и фиксирует схемы ввода и вывода на новой версии.
После того как у workflow появилась версия deployed, можно создать публичное действие. Действие несёт slug — стабильный уникальный в рамках рабочего пространства идентификатор, который вызывающий использует вместо внутреннего UUID, — и ссылается на текущую задеплоенную версию через actionReleases. Slug выбирает публикующий, он не выводится из имени workflow: он явно передаётся в actionService.create(), проверяется на уникальность в рабочем пространстве и не может быть переиспользован другим workflow (в v1 действует инвариант «одно действие на workflow»).
После публикации действие видно на двух поверхностях:
- REST:
GET /api/v1/runtime/actionsиGET /api/v1/runtime/actions/:slug. - MCP:
list_actions(возвращает тот же список) иget_action(возвращает ту же детализацию).
Черновики и приостановленные действия не видны ни на одной из поверхностей — list по умолчанию фильтрует по status = "active", а попытка запуска против неактивного действия возвращает ACTION_UNAVAILABLE (409).
Вызов через REST
POST /api/v1/runtime/actions/:slug/run с Bearer API key, несущим область actions:run.
Тело запроса:
{
"input": { "<field>": "<value>" },
"dry_run": false
}input— объект, соответствующийinputSchemaдействия. Значения, не являющиеся объектом, приводятся к{}.dry_run— опциональное булево. Когдаtrue, запуск записывается сsource: "dry_run"и сразу помечаетсяsucceeded; задание BullMQ не ставится в очередь, побочные эффекты коннекторов не происходят, проверки пользовательского rate-limit и биллинговой квоты пропускаются.
Ответ (HTTP 202 Accepted, из executionService.createActionRun):
{
"run_id": "6f3e...-uuid",
"action_slug": "normalize-lead",
"action_release_version": 3,
"source": "action",
"status": "accepted",
"input": { "...": "..." },
"output": null,
"error": null,
"steps": [],
"approval": null,
"dry_run": false,
"started_at": null,
"completed_at": null,
"duration_ms": null,
"created_at": "2026-04-12T10:15:30.000Z"
}Полный пример curl — действие normalize-lead, которое принимает сырую запись из CRM и возвращает очищенную версию:
curl -sS -X POST https://api.triggo.dev/api/v1/runtime/actions/normalize-lead/run \
-H "Authorization: Bearer trg_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"email": " Ivan@Example.COM ",
"phone": "+7 (912) 345-67-89",
"source": "webform"
},
"dry_run": false
}'В ответ вы получаете 202 с run_id. Финальный результат заберите через GET /api/v1/runtime/runs/:runId, когда запуск завершится (см. «Опрос статуса запуска» ниже).
Вызов через MCP
MCP-инструмент run_action принимает { slug, input, dry_run? }. Минимальный JSON-RPC-конверт:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "run_action",
"arguments": {
"slug": "normalize-lead",
"input": {
"email": " Ivan@Example.COM ",
"phone": "+7 (912) 345-67-89",
"source": "webform"
},
"dry_run": false
}
}
}Ответ — это MCP tool result, в котором content[0].text содержит тот же JSON-объект, что и тело REST-ответа 202 — run_id, status: "accepted", action_release_version и так далее. В случае ошибки конверт переключает isError: true, а в text приходит { error, code, details? }. Сам MCP-транспорт остаётся 200 OK; ошибки инструмента выражаются внутри конверта, а не через HTTP-статус.
Синхронный или асинхронный режим
POST /run — асинхронный. Эндпоинт валидирует ввод против схемы действия, проверяет пользовательский лимит скорости исполнения (100/час) и биллинговую квоту, вставляет строку в runs со status: "accepted", ставит задание BullMQ execution в очередь Redis и сразу возвращает 202 — он не ждёт завершения задания.
Возвращённый run_id — это идентификатор, который вы опрашиваете. Синхронного верхнего предела приводить не нужно, потому что эндпоинт никогда не блокируется на исполнении; таймауты 30 секунд на шаг и 300 секунд на пайплайн управляют самим фоновым запуском, а не первоначальным HTTP-вызовом.
Одно исключение: когда у действия approval_policy = "always", начальный статус — waiting_for_approval вместо accepted, создаётся строка approval_requests с истечением через 1 час, и задание исполнения не ставится в очередь, пока подтверждение не будет решено через POST /runs/:runId/approve или MCP-инструмент approve_run.
Опрос статуса запуска
Получите запуск по id с любой из поверхностей:
- REST:
GET /api/v1/runtime/runs/:runId(областьruns:read). - MCP:
get_run_statusс{ run_id }(областьruns:read).
В ответе приходит актуальный status (accepted, running, waiting_for_approval, succeeded, failed, cancelled, timed_out), структурированный output (заполняется из узла Return Output при успехе) и таймлайн steps, выведенный из событий журнала. Полная форма таймлайна шагов и интерпретация каждого статуса — в разделе Отладка запусков.
Ошибки
Ошибки на REST-поверхности используют следующую карту кодов:
| HTTP | Код | Когда |
|---|---|---|
| 400 | INPUT_VALIDATION_FAILED | input не соответствует схеме действия. Ответ включает details: [...] с ошибками по полям. |
| 401 | UNAUTHORIZED | Bearer-токен отсутствует, повреждён, отозван или просрочен. |
| 403 | FORBIDDEN | Ключ валиден, но не несёт actions:run. |
| 404 | ACTION_NOT_FOUND | Slug не существует в этом рабочем пространстве. Снаружи черновики выглядят идентично удалённым действиям. |
| 409 | ACTION_UNAVAILABLE | Действие существует, но его статус не active — обычно paused или archived, либо у него ещё нет текущего релиза. |
| 429 | RATE_LIMIT_EXCEEDED | Либо скользящее окно на ключ (зависит от тарифа), либо пользовательский лимит 100 запусков в час. В ответе есть заголовок retry-after в секундах; см. Ограничения скорости. |
| 500 | что-либо ещё | Внутренний сбой workflow при создании запуска — редко; сам запуск падает позже и проявляется на GET /runs/:runId. |
Поверх MCP те же коды появляются внутри content[0].text с isError: true, а транспорт остаётся 200 OK.
Смотрите также
- Обзор интеграции с агентами — гайд по выбору REST или MCP.
- MCP Quickstart — конфигурации клиентов, полный список инструментов, детали транспорта.
- Активация и версии — машина состояний деплоя, история версий, откат.
- Ограничения скорости — окна по тарифам, обработка 429,
retry-after.