Skip to content

Browser-workflow monitors (browser automation)

A regular HTTP monitor and content monitor see only the HTML the server returned in the initial response. For modern SPAs, JavaScript-heavy pages and anything hidden behind authentication, that is not enough: the required text often appears only after logging in, several clicks, and frontend rendering.

The Browser-workflow monitor runs a specified sequence of actions in a real headless Chromium — like a small Selenium script — and verifies that the workflow still completes. If any step fails (an element disappeared, text didn’t match, the page did not load) — an alert is sent. Additionally, the monitor can extract text from the page and notify when it changes.

A monitor is described by the fields:

  • startUrl — the initial URL (required, only http/https). Each run starts from it.
  • steps — JSON array of steps, from 1 to 20 items. Executed strictly sequentially.
  • cookies — optional: captured session for authenticated scenarios (see below). Stored encrypted and never revealed externally.
  • proxy — optional egress proxy scheme://[user:pass@]host:port for sites that block datacenter IPs. Empty — direct connection.
  • timeoutSec — timeout per step. Default 60, maximum 120.
  • intervalSec — how often to run the workflow. Minimum 300 (5 minutes), maximum 86400 (one day).
  • alertMessage (required, up to 2000 characters), alertTitle, alertMessageMarkdown, alertPriority (0…10) — what and with what priority to send on failure.
  • notifyOnChange / notifyOnChangeVars — “notify on change” mode for extracted values (see below).

Each element of the steps array is an object:

FieldPurpose
namehuman-readable step name (required, included in the alert on failure)
descriptionnatural-language description of the step — used by the AI to pick action + selector
actionaction to perform (see table below)
selectorCSS selector of the target
valuevalue: URL / text / key; supports interpolation {{variable}}
extractNamevariable name for extractText
authauthentication step: run only when the session is invalid

Exactly nine actions are supported:

actionWhat it doesNeeds selectorNeeds value
navigatenavigate to URL (value = URL)yes (URL)
clickclick an elementyes
typetype value into a fieldyestext
selectselect value in a <select>yesoption value
presspress key value (e.g. Enter)key
waitForwait for a selector to appearyes
assertTextassert that the selector’s text contains valueyesexpected text
extractTextextract selector’s text into variable extractNameyes
scrollscroll the page: value = top | bottom or pixels (300 down, -300 up)

Steps click, type, select, waitFor, assertText, extractText require a non-empty selector — without it creating/updating a monitor will return 400. For navigate value (URL) is required. For extractText extractName is required.

We check that after logging in the dashboard shows a balance and it is not “0”:

[
{"name": "Open dashboard", "action": "waitFor", "selector": "#dashboard"},
{"name": "Expand account", "action": "click", "selector": "button.account-toggle"},
{"name": "Wait for balance", "action": "waitFor", "selector": ".balance-value"},
{"name": "Assert not empty", "action": "assertText", "selector": ".balance-value", "value": ""},
{"name": "Remember balance", "action": "extractText", "selector": ".balance-value", "extractName": "balance"}
]

Manually choosing CSS selectors is tedious and brittle. That’s why there’s an endpoint POST /browser-workflow/build-step: you describe a step in words (description), and the AI selects action and a stable selector, then immediately verifies the candidate in a real browser and returns the result.

How it works under the hood:

  1. The service opens startUrl, applies provided cookies and replays already confirmed steps — getting the page into the current state.
  2. The LLM, based on the description and page markup, proposes an action and up to three ranked selector candidates.
  3. Candidates are cheaply pre-validated against the HTML (syntax + presence on the page), then all are checked in a single browser run.
  4. If an action worked — verified: true is returned along with an example value (sampleValue) for extractText/assertText. If not — the last proposal with verified: false and an explanation in note.

Request body:

{
"startUrl": "https://app.example.com/login",
"steps": [ /* already confirmed steps */ ],
"description": "click the “Log in” button",
"cookies": [ /* optional: saved session */ ],
"proxy": "",
"sessionId": "",
"timeoutSec": 60
}

Response:

{
"action": "click",
"selector": "button[type=submit]",
"value": "",
"extractName": "",
"label": "Log in",
"reason": "...",
"sampleValue": "",
"verified": true,
"availableVars": ["balance"],
"cookies": [ /* resulting session cookies */ ]
}

The description field — 1 to 500 characters. availableVars shows variables already extracted by previous steps (via extractText) and available for interpolation {{var}} in this step. Returned cookies should be accumulated on the client and passed to the next build-step so you don’t have to repeat authenticated steps again.

Authenticated scenarios: capturing cookies

Section titled “Authenticated scenarios: capturing cookies”

To monitor pages behind login you need a valid session. The endpoint POST /browser-workflow/login performs login steps (type password, click button, etc.) in a headless browser and returns the final cookies in plaintext — you then pass them in the cookies field when creating/updating a monitor, where they are encrypted before storage.

Окно терминала
curl -X POST "$NOTIFLY_URL/browser-workflow/login" \
-H "X-Notifly-Key: C..." \
-H "Content-Type: application/json" \
-d '{
"startUrl": "https://app.example.com/login",
"steps": [
{"action": "type", "selector": "#email", "value": "me@example.com"},
{"action": "type", "selector": "#password", "value": "secret"},
{"action": "click", "selector": "button[type=submit]"}
],
"timeoutSec": 60
}'

Response:

{ "cookies": [ /* array of cookies */ ], "count": 7, "url": "https://app.example.com/dashboard" }

Before saving, the entire workflow can be run via POST /browser-workflow/test-step — this is the “Check” button. The service executes all steps and reports whether it reached the end.

Окно терминала
curl -X POST "$NOTIFLY_URL/browser-workflow/test-step" \
-H "X-Notifly-Key: C..." \
-H "Content-Type: application/json" \
-d '{
"startUrl": "https://app.example.com/dashboard",
"cookies": [ /* saved session */ ],
"steps": [ /* full workflow */ ],
"timeoutSec": 60
}'

Response:

{
"ok": true,
"failedStep": -1,
"error": "",
"url": "https://app.example.com/dashboard",
"cookies": [ /* resulting cookies for the whole session */ ],
"count": 7,
"vars": { "balance": "12 340 ₽" }
}

If the workflow failed, ok: false, and failedStep will point to the index of the failing step and error — the reason. On success vars contains all extracted extractText values — handy to immediately verify that the correct data is being captured.

If you enable notifyOnChange, the monitor doesn’t act as a simple “up/down” check, but as a value tracker: after each successful run it compares extracted (extractText) variables with the previous successful run and sends a notification when any of them changed.

  • notifyOnChangeVars — JSON array of variable names to track. An empty array = track all extracted variables.

This lets you detect price changes, order status updates, or inventory levels on a page that is rendered only in the browser.

A monitor is created with status pending and an initial check scheduled “now”. Then it moves to up / down (and also degraded) based on run results. You can pause and resume using the endpoints POST /browser-workflow/:id/pause (status paused) and POST /browser-workflow/:id/resume (returns to pending). While paused the workflow is not run and no alerts are sent.

On failure the monitor card shows lastError and lastFailedStep — the text of the last error and the name of the failing step.

All endpoints require a client-token (where marked “write” — a token with write rights or an MCP code with write access). In list responses encrypted cookies are stripped out; instead a hasCookies flag is returned.

Method & pathAuthorizationPurpose
GET /browser-workflowclient-tokenlist monitors (without cookies)
POST /browser-workflowclient-token (write)create a monitor
PUT /browser-workflow/:idclient-token (write)update a monitor
DELETE /browser-workflow/:idclient-token (write)delete a monitor
POST /browser-workflow/:id/pauseclient-token (write)pause (status=paused)
POST /browser-workflow/:id/resumeclient-token (write)resume (status=pending)
POST /browser-workflow/build-stepclient-token (write)AI selection and verification of one step
POST /browser-workflow/loginclient-token (write)run login and return session cookies
POST /browser-workflow/test-stepclient-token (write)test run of the whole workflow
Окно терминала
curl -X POST "$NOTIFLY_URL/browser-workflow" \
-H "X-Notifly-Key: C..." \
-H "Content-Type: application/json" \
-d '{
"appid": 12,
"name": "Account balance",
"startUrl": "https://app.example.com/dashboard",
"steps": "[{\"name\":\"Wait for balance\",\"action\":\"waitFor\",\"selector\":\".balance-value\"},{\"name\":\"Remember balance\",\"action\":\"extractText\",\"selector\":\".balance-value\",\"extractName\":\"balance\"}]",
"cookies": "[ /* plaintext cookies from /login */ ]",
"intervalSec": 900,
"timeoutSec": 60,
"notifyOnChange": true,
"alertMessage": "Workflow “Account balance” failed"
}'

See also: Active monitors · Content monitor · Workflow monitor (HTTP) · Full API.