Сценарии подтверждения
Закрывайте ответственные действия человеческим подтверждением — просматривайте, подтверждайте и отклоняйте запуски, ожидающие решения, через runtime API или MCP.
Сценарии подтверждения
Подтверждения позволяют workflow приостановиться для решения человека перед дорогостоящим или необратимым шагом — отправкой внешнего сообщения, удалением записи, переводом денег, применением автономного исправления. В workflow можно настроить обязательное подтверждение для конкретных действий или шагов; такие запуски во время работы находятся в состоянии waiting_for_approval, пока человек (или агент с нужными правами) не подтвердит или не отклонит их. Эта страница описывает поверхность принятия решений, доступную агентам: просмотр ожидающих запусков, подтверждение или отклонение через REST или MCP и обработку истечения.
Жизненный цикл
При попадании запуска в точку подтверждения вместе движутся две записи: строка runs, отслеживающая выполнение, и строка approval_requests, хранящая состояние решения, комментарий и аудит-трейл.
У самого запроса на подтверждение четыре конечных состояния:
approve_run / POST approve (decision=approved)
┌──────────────────────────────────────────────────────▶ approved
│
pending ──────┤
│ POST approve (decision=rejected)
├──────────────────────────────────────────────────────▶ rejected
│
│ TTL 1 ч, воркер approval-expiry
└──────────────────────────────────────────────────────▶ expiredРодительский запуск движется синхронно: waiting_for_approval → running при подтверждении, cancelled при отклонении, timed_out при истечении. Переход фиксируется только после постановки job'а на возобновление, поэтому сбой Redis не оставит запуск в состоянии running без соответствующего job'а у исполнителя.
Получение запусков, ожидающих решения
GET /api/v1/runtime/runs с needs_approval=true сужает список до запусков, которые сейчас приостановлены в точке подтверждения. Эндпоинт требует право runs:read и принимает такие фильтры — все опциональные:
| Query-параметр | Тип | Значение |
|---|---|---|
needs_approval | true | false | При true возвращает только запуски в waiting_for_approval. |
action_slug | string | Фильтр по одному опубликованному действию. |
status | string | Явный фильтр по статусу (например, waiting_for_approval, failed). |
source | string | action, replay, scheduler и т. д. |
has_incident | true | false | Запуски, связанные с инцидентом или нет. |
date_from, date_to | ISO-8601 | Ограничение по created_at. |
limit | integer, 1–100, по умолчанию 20 | Размер страницы. |
offset | integer, по умолчанию 0 | Смещение страницы. |
curl -sS 'https://api.triggo.ai/api/v1/runtime/runs?needs_approval=true&limit=20' \
-H "Authorization: Bearer $TRIGGO_API_KEY"Ответ содержит по одной строке на запуск:
{
"runs": [
{
"run_id": "3f2e…",
"action_slug": "send_refund",
"source": "action",
"status": "waiting_for_approval",
"duration_ms": null,
"created_at": "2026-04-13T09:41:12.004Z"
}
],
"total": 1,
"limit": 20,
"offset": 0
}Чтобы получить полный контекст подтверждения (строку ожидающего approval_requests, разрешённые входы, описание, срок истечения), вызовите GET /api/v1/runtime/runs/:runId для нужного запуска.
Подтверждение или отклонение через REST
Тот же эндпоинт обрабатывает оба решения. Поле decision в теле выбирает исход.
POST /api/v1/runtime/runs/:runId/approve
Authorization: Bearer <key> (право: approvals:decide)
Content-Type: application/json
{
"decision": "approved" | "rejected",
"comment": "опционально, до 1000 символов"
}curl -sS -X POST https://api.triggo.ai/api/v1/runtime/runs/3f2e…/approve \
-H "Authorization: Bearer $TRIGGO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"decision":"approved","comment":"verified refund with support thread"}'При успехе вы получаете:
{
"run_id": "3f2e…",
"status": "running",
"decision": "approved",
"decided_at": "2026-04-13T09:42:03.118Z"
}Отклонение возвращает "status": "cancelled". comment усекается на сервере до 1000 символов и записывается в строку approval_requests вместе с userId и decided_via: "api", что попадает в аудит-лог (approval.decided).
Подтверждение через MCP
MCP предоставляет ту же операцию как инструмент approve_run. То же право (approvals:decide), та же форма payload'а, тот же результат.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "approve_run",
"arguments": {
"run_id": "3f2e…",
"decision": "approved",
"comment": "verified refund with support thread"
}
}
}decision должно быть "approved" или "rejected"; comment опционален и ограничен 1000 символами Zod-схемой инструмента. Результат зеркалит REST-ответ.
Истечение
Каждый запрос на подтверждение создаётся с TTL 1 час. При создании планируется BullMQ-job approval-expiry, чей jobId выводится из id подтверждения, поэтому решение, принятое внутри окна, чисто снимает job истечения — нет гонки, когда подтверждённый пользователем запуск через мгновение уходит в expired.
Если TTL истекает без решения, approval-expiry.worker фиксирует запрос как expired, пишет запись аудита approval.decided с decision: "expired", а если подтверждение связано с запуском пайплайна — журналирует ошибку шага APPROVAL_EXPIRED и переводит запуск в timed_out. Пользователю в его канале уведомлений приходит сообщение, что запрос истёк и был автоматически отклонён.
На практике истёкшее подтверждение эквивалентно отклонению с точки зрения запуска: шаг не выполняется, workflow не продолжается. Различие важно для аналитики и разбора инцидентов — expired означает «никто не принял решение вовремя», а не «человек сказал нет».
Ошибки
Эндпоинт и инструмент MCP используют одинаковые коды ошибок. HTTP-статус выводится runtime'ом из каждого кода; MCP возвращает isError: true с той же строкой code.
| Код | Статус | Когда |
|---|---|---|
UNAUTHORIZED | 401 | Отсутствующий, некорректный или отозванный Bearer-ключ. |
FORBIDDEN | 403 | Ключ валиден, но не имеет approvals:decide. |
RUN_NOT_FOUND | 404 | В этом рабочем пространстве нет запуска с таким id. |
BAD_REQUEST | 400 | decision не "approved" и не "rejected"; запуск не в состоянии waiting_for_approval; или подтверждение уже разрешено конкурентным вызовом. |
RATE_LIMIT_EXCEEDED | 429 | Достигнут лимит скользящего окна — отступите по заголовку retry-after. |
Случай «уже разрешено» (BAD_REQUEST с сообщением "Approval already resolved") стоит обработать аккуратно: два агента гонятся за одним запуском или пользователь и агент решают одновременно. Обновление атомарно — UPDATE … WHERE status = 'pending' — поэтому выигрывает ровно один вызывающий. Проигравшие должны заново прочитать запуск и сверить поведение с его новым статусом.
Связанное
- API-ключи — выпуск ключа с
approvals:decide. - Быстрый старт MCP — транспорт для инструмента
approve_run. - Публикация workflow как действий — где к действиям привязываются политики подтверждения.
- Обработка ошибок в workflow — пути сбоев для отклонённых и истёкших запусков.