Workflow monitors (multi-step API)
A regular HTTP monitor sends a request to a single URL. But a real API health check often requires a sequence: first log in, obtain a token, then call a protected endpoint with that token. A Workflow monitor performs exactly such a chain — up to 20 HTTP steps in a row, passing values from one step’s response into subsequent requests.
Steps are executed strictly in order. If any step fails (timeout,
wrong HTTP status, response body missing expected substring, JSONPath assert mismatch, or
an extractor didn’t find a value) — the chain stops, the monitor goes down and an alert is sent
indicating the failed step.
How a step is structured
Section titled “How a step is structured”Each step (WorkflowStep) is one HTTP request with its own checks and
extractable variables:
| Field | Type | Purpose |
|---|---|---|
name | string | human-readable step name (appears in the alert) — required |
method | string | GET / POST / PUT / PATCH / DELETE / HEAD — required |
url | string | request URL; supports {{var}} — required |
headers | string | JSON object {"Header":"Value"}; values support {{var}} |
body | string | request body; supports {{var}} |
expectedStatus | int | 0 = any 2xx; otherwise exact status in [100, 599] |
bodyContains | string | substring that must be present in the response body |
assertJsonPath | string | JSONPath to check a value in the body (see below) |
assertValue | string | expected value for assertJsonPath (required if assertJsonPath is set) |
extractors | array | list of variables extracted from this step’s response |
The steps themselves are stored in the monitor’s steps field as a JSON array serialized to a string.
Passing variables between steps
Section titled “Passing variables between steps”Each extractor is a pair {name, jsonPath}:
name— the variable name under which the result becomes available in subsequent steps;jsonPath— the path to the value in the JSON response.
The extracted value is substituted into url, headers, and body of later steps
using the syntax {{name}}. Unknown placeholders remain as-is.
JSONPath is simple: $.token, $.user.id, array indices via dot —
$.items.0.id. The root $ is optional.
Example: login → token → protected endpoint
Section titled “Example: login → token → protected endpoint”[ { "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" }]The first step logs in and extracts token and userId. The second substitutes both into the URL
and the Authorization header, expects 200 and checks that $.status == "active".
Monitor parameters
Section titled “Monitor parameters”| Field | Default | Constraints |
|---|---|---|
appid | — | the application where the alert will be sent — required |
name | — | monitor name, 1..200 characters — required |
steps | — | JSON array of steps: 1..20 items — required |
intervalSec | — | how often to check, 60..86400 sec — required |
timeoutSec | 15 | timeout per step, maximum 60 sec |
alertMessage | — | alert message text, 1..2000 characters — required |
alertTitle | empty | alert title |
alertMessageMarkdown | false | treat alertMessage as Markdown |
alertPriority | 0 | alert priority, 0..10 |
notifyOnChange | false | send notification when any extracted value changed since the last successful run |
On-change mode
Section titled “On-change mode”With notifyOnChange: true the monitor remembers the last extracted values
(extractors) and sends a notification when any of them changed. Useful to watch
not only availability but also that some field in the API response didn’t “drift”
(e.g., build version, price, stock level).
AI-assisted step build (build-step)
Section titled “AI-assisted step build (build-step)”To avoid describing an HTTP request manually, there’s POST /workflow-monitor/build-step:
you write what the step should do, in natural language — an LLM picks method,
URL, headers, body, checks and extractors, after which the candidate is actually
executed for verification.
Previously confirmed steps are passed in steps — they are replayed to
accumulate variables and give the model an example of the previous step’s response (this helps it
choose the correct JSONPath).
curl -X POST "$NOTIFLY_URL/workflow-monitor/build-step" \ -H "X-Notifly-Key: C-xxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "description": "получить профиль текущего пользователя с Bearer-токеном из прошлого шага", "steps": [ /* уже подтверждённые шаги */ ], "timeoutSec": 15 }'In the response:
| Field | Purpose |
|---|---|
step | the proposed WorkflowStep (method, URL, headers, body, checks, extractors) |
label | short name of the step |
reason | explanation why the model chose this request |
verified | true if the candidate was executed successfully |
statusCode | HTTP status of the candidate’s response |
sampleBody | a fragment of the response body |
extractedVars | variables actually extracted by this step |
availableVars | variables available at this step (from previous ones) |
note | filled if verification failed — with the error text |
Test run (test)
Section titled “Test run (test)”Before saving the monitor, run the whole chain via POST /workflow-monitor/test:
curl -X POST "$NOTIFLY_URL/workflow-monitor/test" \ -H "X-Notifly-Key: C-xxxxxxxx" \ -H "Content-Type: application/json" \ -d '{"steps": [ /* массив шагов */ ], "timeoutSec": 15}'Response:
{ "ok": true, "vars": {"token": "ey...", "userId": "42"} }On success ok: true and vars — the final set of accumulated variables. On failure:
{ "ok": false, "failedStep": 1, "error": "HTTP 401 (expected 200)" }failedStep — index of the failed step (zero-based), error — reason: wrong status,
missing substring/JSONPath, assert-value mismatch, or network timeout.
Pause and resume
Section titled “Pause and resume”POST /workflow-monitor/:id/pause stops checks (the next check
is postponed far into the future), …/resume — places the monitor in the queue for an immediate check.
Cosmetic edits (name, alert text) do not reschedule the check; changing steps, interval, timeout or notifyOnChange — reschedules to “now”.
REST API
Section titled “REST API”| Method and path | Authorization | Purpose |
|---|---|---|
GET /workflow-monitor | client-token | list of monitors |
POST /workflow-monitor | client-token (write) | create a monitor |
PUT /workflow-monitor/:id | client-token (write) | update |
DELETE /workflow-monitor/:id | client-token (write) | delete |
POST /workflow-monitor/:id/pause | client-token (write) | pause checks |
POST /workflow-monitor/:id/resume | client-token (write) | resume checks |
POST /workflow-monitor/build-step | client-token (write) | AI selection and verification of a single step |
POST /workflow-monitor/test | client-token (write) | run the chain without saving |
All endpoints are also available under the MCP code (M…): read — by any MCP code,
write — by a code with scope write. You can manage monitors via
the assistant, and from the admin UI.