Перейти к содержимому

Workflow-мониторы (multi-step API)

Обычный HTTP-монитор дёргает один URL. Но реальная проверка живости API часто требует последовательности: сначала залогиниться, получить токен, потом дёрнуть защищённый endpoint с этим токеном. Workflow-монитор выполняет именно такую цепочку — до 20 HTTP-шагов подряд, передавая значения из ответа одного шага в запросы следующих.

Шаги выполняются строго по порядку. Если любой шаг не прошёл (timeout, неверный HTTP-статус, тело не содержит ожидаемого, JSONPath-assert не совпал или extractor не нашёл значение) — цепочка останавливается, монитор переходит в статус down и приходит alert с указанием упавшего шага.

app.notifly.ru → Мониторы → Создать → «Что вы хотите проверять?» → «Сценарий из нескольких шагов» → «HTTP-запросы (API)». Готовые мониторы живут на вкладке Workflow.

В диалоге шаги можно заполнить вручную, собрать ИИ-помощником по описанию (кнопка «Подобрать шаг», см. ниже) или начать «Из шаблона…»:

ШаблонШаги
Проверка REST API с авторизациейGET /health → POST /login (извлечь token) → GET /me с Bearer
Проверка формы логинаGET /login (тело содержит <form) → POST /login с учётными данными
Цепочка с JSONPath-извлечениемсписок → детальный endpoint по извлечённому id

Шаблон только предзаполняет форму: замените URL и данные на свои. Кнопка «Тестовый прогон» выполняет всю цепочку без сохранения (см. Тестовый прогон).

Каждый шаг (WorkflowStep) — это один HTTP-запрос со своими проверками и извлекаемыми переменными:

ПолеТипНазначение
namestringчеловеко-читаемое имя шага (попадёт в alert) — обязательно
methodstringGET / POST / PUT / PATCH / DELETE / HEAD — обязательно
urlstringURL запроса; поддерживает {{var}} — обязательно
headersstringJSON-объект {"Header":"Value"}; значения поддерживают {{var}}
bodystringтело запроса; поддерживает {{var}}
expectedStatusint0 = любой 2xx; иначе точный статус в [100, 599]
bodyContainsstringподстрока, которая должна присутствовать в теле ответа
assertJsonPathstringJSONPath для проверки значения в теле (см. ниже)
assertValuestringожидаемое значение для assertJsonPath (обязательно, если задан assertJsonPath)
extractorsarrayсписок переменных, извлекаемых из ответа этого шага

Сами шаги хранятся в поле steps монитора как JSON-массив, сериализованный в строку. Метод при сохранении приводится к верхнему регистру.

Каждый extractor — это пара {name, jsonPath}:

  • name — имя переменной, под которым результат станет доступен в следующих шагах;
  • jsonPath — путь к значению в JSON-ответе.

Извлечённое значение подставляется в url, headers и body последующих шагов по синтаксису {{name}}. Неизвестные плейсхолдеры остаются как есть.

JSONPath — простой: $.token, $.user.id, индексы массива через точку — $.items.0.id. Корень $ опционален. Скобочная запись ($.items[0].id), фильтры и * не поддерживаются.

[
{
"name": "Login",
"method": "POST",
"url": "https://api.example.com/auth/login",
"headers": "{\"Content-Type\":\"application/json\"}",
"body": "{\"email\":\"bot@example.com\",\"password\":\"s3cret\"}",
"expectedStatus": 200,
"extractors": [
{"name": "token", "jsonPath": "$.access_token"},
{"name": "userId", "jsonPath": "$.user.id"}
]
},
{
"name": "Профиль пользователя",
"method": "GET",
"url": "https://api.example.com/users/{{userId}}",
"headers": "{\"Authorization\":\"Bearer {{token}}\"}",
"expectedStatus": 200,
"assertJsonPath": "$.status",
"assertValue": "active"
}
]

Первый шаг логинится и извлекает token и userId. Второй подставляет оба в URL и заголовок Authorization, ждёт 200 и проверяет, что $.status == "active".

ПолеПо умолчаниюОграничения
appid—канал, в который уйдёт alert — обязательно
name—название монитора, 1..200 символов — обязательно
steps—JSON-массив шагов (строкой): 1..20 штук — обязательно
intervalSec—как часто проверять, 60..86400 сек — обязательно
timeoutSec15timeout на один шаг, максимум 60 сек
alertMessage—текст уведомления, 1..2000 символов — обязательно
alertTitleпустозаголовок уведомления
alertMessageMarkdownfalseтрактовать alertMessage как Markdown
alertPriority0приоритет уведомления, 0..10
notifyOnChangefalseслать уведомление, когда любое извлечённое значение изменилось с прошлого успешного прогона

Канал должен быть вашим или расшаренным вам с правом отправки.

При падении приходит сообщение с заголовком alertTitle (пустой — «Workflow «‹название›» failed») и текстом alertMessage, к которому дописаны упавший шаг и причина:

Шаг: #2 Профиль пользователя
Ошибка: HTTP 401 (expected 200)
  • Алерт уходит при переходе в down и ещё раз — если цепочка начала падать на другом шаге. Пока падает тот же шаг, повторных алертов нет: отметка отправленного алерта хранится в поле монитора alertedAt.
  • Re-arm: если изменить проверочные параметры (steps, intervalSec, timeoutSec, notifyOnChange) через PUT, отметка alertedAt сбрасывается и проверка перепланируется «на сейчас» — если цепочка всё ещё падает, придёт свежий алерт. Правка названия или текстов алерта ничего не перезапускает.
  • Отдельного уведомления о восстановлении workflow-мониторы не шлют: при успешном прогоне монитор просто возвращается в up.
  • В окне обслуживания прогоны идут и пишутся в историю, но алерт о падении откладывается: alertedAt не ставится, и если после окна цепочка всё ещё падает, первая же проверка пришлёт алерт. Уведомления on-change в окне не отправляются.

Алерт — обычное сообщение канала: его досылка во внешние каналы подчиняется тихим часам (высокий alertPriority пробивает тишину). Если монитор добавлен на статус-страницу, переходы up↔down открывают и закрывают на ней авто-инцидент.

При notifyOnChange: true монитор запоминает значения всех extractor-ов последнего успешного прогона и шлёт уведомление, когда что-то из них изменилось. В тексте — строки вида token: старое → новое; заголовок по умолчанию — «Workflow «‹название›»: данные изменились». Первое наблюдение значения уведомления не даёт, а при падении базовые значения сохраняются. Удобно следить не только за доступностью, но и за тем, что какое-то поле в ответе API «поехало» (например, версия билда, цена, остаток на складе).

Чтобы не описывать HTTP-запрос руками, есть POST /workflow-monitor/build-step: вы пишете, что должен делать шаг, на естественном языке — LLM подбирает метод, URL, заголовки, тело, проверки и extractors, после чего кандидат реально выполняется для верификации.

Ранее подтверждённые шаги передаются в steps (не больше 20) — они воспроизводятся, чтобы накопить переменные и дать модели пример ответа предыдущего шага (это помогает ей выбрать правильный JSONPath).

Окно терминала
curl -X POST "$NOTIFLY_URL/workflow-monitor/build-step" \
-H "X-Notifly-Key: <api-token>" \
-H "Content-Type: application/json" \
-d '{
"description": "получить профиль текущего пользователя с Bearer-токеном из прошлого шага",
"steps": [ /* уже подтверждённые шаги */ ],
"timeoutSec": 15
}'

В ответе:

ПолеНазначение
stepпредложенный WorkflowStep (метод, URL, заголовки, тело, проверки, extractors)
labelкороткое имя шага
reasonпояснение, почему модель выбрала такой запрос
verifiedtrue, если кандидат удалось выполнить успешно
statusCodeHTTP-статус ответа кандидата
sampleBodyфрагмент тела ответа (до 400 символов)
extractedVarsпеременные, реально извлечённые этим шагом
availableVarsпеременные, доступные на этом шаге (из предыдущих)
noteзаполняется, если верификация не удалась — с текстом ошибки

Перед сохранением монитора прогоните всю цепочку через POST /workflow-monitor/test:

Окно терминала
curl -X POST "$NOTIFLY_URL/workflow-monitor/test" \
-H "X-Notifly-Key: <api-token>" \
-H "Content-Type: application/json" \
-d '{"steps": [ /* массив шагов */ ], "timeoutSec": 15}'

Здесь steps — обычный JSON-массив (не строка), 1..20 шагов; timeoutSec — на шаг, по умолчанию 15, больше 60 урезается до 60. Ответ:

{"ok": true, "failedStep": 0, "vars": {"token": "ey...", "userId": "42"}}

При успехе ok: true и vars — итоговый набор накопленных переменных. При сбое:

{"ok": false, "failedStep": 1, "error": "HTTP 401 (expected 200)"}

failedStep — индекс упавшего шага (с нуля; имеет смысл только при ok: false), error — причина: неверный статус, отсутствие подстроки/JSONPath, несовпадение assert-значения, сетевая ошибка или timeout.

POST /workflow-monitor/:id/pause ставит статус paused и останавливает проверки, …/resume возвращает pending и ставит монитор в очередь на проверку немедленно; оба отвечают объектом монитора. Изменение шагов, интервала, timeout-а или notifyOnChange тоже перепланирует проверку на «сейчас».

Вкладка История монитора показывает прогоны и график средней длительности каждого шага со сбоями по шагам; через API — GET /monitor-history/workflow_monitor/:id. При падении в самом мониторе видны lastError и lastFailedStep.

Каждый прогон цепочки — одно событие дневной квоты dailyEvents, независимо от числа шагов. Если квота исчерпана, проверки откладываются до 00:05 МСК следующего дня — см. Квоты и тарифы.

Метод и путьПрава API-токенаНазначение
GET /workflow-monitorreadсписок мониторов
POST /workflow-monitorwriteсоздать монитор
PUT /workflow-monitor/:idwriteобновить
DELETE /workflow-monitor/:idwriteудалить
POST /workflow-monitor/:id/pausewriteприостановить проверки
POST /workflow-monitor/:id/resumewriteвозобновить проверки
POST /workflow-monitor/build-stepwriteИИ-подбор и верификация одного шага
POST /workflow-monitor/testwriteпрогнать цепочку без сохранения

Авторизация — заголовок X-Notifly-Key с API-токеном (M…): чтение — любым токеном, изменения — токеном с доступом write (иначе 403). Токен, ограниченный по каналам, видит и меняет только мониторы своих каналов. Те же операции есть в Go/Python SDK и MCP-сервере.