Skip to content

Assertions and validation rules

View as Markdown

For Website/HTTPS and API monitors, Assertion mode replaces the classic status-code and keyword fields with a list of rules written in HostTracker’s assertion language. Each rule is one line; the check passes only when every rule passes. Use it when “the page answered 200” is not enough: an API must say "status":"ok", a page must contain its checkout button, a response must arrive within 2 seconds, a redirect must stay on your domain.

Setting (app label) API field (settings.*) Type / allowed values Default Plan limits What it does for you
Assertion mode assertMode Boolean Off Available on every plan that includes the monitor type Makes the rule list the response verdict.
The rule list assertsSource (write) Text, one rule per line, optional label: rule None Up to 20 rules per monitor; your plan may allow fewer The rules, as you would type them. Parsed and checked on save.
- asserts (read/write) Array of rule objects { name, sub, op, not, val, vals, valSub, valT, nocase } None Same The compiled form. Send either asserts or assertsSource, not both.
- assertsText (read-only) Array of strings - - Your rules rendered back as text.

A rule is subject [not] predicate, optionally labelled and optionally followed by nocase:

status isOk
"json ok": body.json.path("$.status") eq "ok"
time lt 2s
body not contains "Error"
header("Content-Type") isJson
  • Strings need double quotes; numbers can carry units (2s, 500ms, 10kb, 1mb).
  • not goes after the subject (body not contains "x"); a leading not does not parse.
  • A rule that fails names itself in the alert - short labels tell you which expectation broke.
  • There is no or between rules; in [...], containsAny [...] and ranges such as in [200..299] cover most cases.
Area Example Catches
Status status isOk, status in [200, 301, 302], status not in [500..599] Errors, or a status outside what you allow
Body text body contains "Add to cart", body containsAll ["Checkout", "Total"], body matches "Order #[0-9]{6}" A 200 page that lost its content - the failure a status check cannot see
Body size body.size gt 1kb A blank or truncated page
JSON body.json.path("$.status") eq "ok", body.json.path("$.items").count ge 1, body isJson The API’s own verdict, empty results, an HTML error page instead of JSON
Headers header("Content-Type") isJson, header("Cache-Control") matches "no-store" Wrong content type or caching
Timing time lt 2s, time.connect lt 300ms, time.tls lt 500ms, time.dns lt 200ms, time.head lt 1s Slowness, and which stage causes it (DNS is not part of time)
Redirects redirects.count le 2, url.scheme eq "https", url.host eq url.original.host, requests[0].status eq 301 Chains that grow, leave your domain or change type
DNS dns.ips.count ge 2, dns.ips contains "203.0.113.10" Losing redundancy, or an address change
Certificate cert.days.left gt 14 A certificate close to expiry, with time to act
Change detection previous.body.hash eq body.hash, delta(body.json.path("$.orders")) lt 100 A page that changed, or a value that jumped since the last check

The full grammar, every subject and predicate, and a recipe catalogue are in the assertion language reference.

  1. On Sites, open a Website/HTTPS or API monitor and expand Response Validation.
  2. Switch on Assertion mode. The classic keyword and status fields are hidden, and the list starts with a status rule.
  3. Add rules with the rule editor, or pick one under Examples - click to add:.
  4. Save. Rules that do not compile are refused line by line with the reason.

Switching the mode off before saving brings the classic settings back. Saving with the mode off removes the rules from the monitor.

Terminal window
# Scope monitor:write
curl -X PATCH "https://api2.host-tracker.com/monitor/$MONITOR_ID" \
-H "Authorization: Bearer $HT_TOKEN" -H "Content-Type: application/json" \
-d '{ "settings": {
"assertMode": true,
"assertsSource": "status isOk\n\"json ok\": body.json.path(\"$.status\") eq \"ok\"\ntime lt 2s"
} }'

The response shows the stored rules as asserts (compiled) and assertsText (readable). While assertMode is on, keywords, keywordMode, ignoredStatuses, errorStatuses, errorOnRedirect and preset are refused - clear them in the same request if the monitor had them. MCP: update_monitor / create_monitor with settingsJson carrying the same members.

Every check evaluates all rules and reports every failed one in the result (AssertError). A connection failure - DNS, connect, TLS, timeout, a redirect loop - still fails the check whatever the rules say. A failed check is re-checked like any other before the monitor turns Down.

  • Rule count. At most 20 rules per monitor; your plan may set a lower number. More is refused with 403 package_limit (“Your package allows up to N assertion rules per check”).
  • Change detection is a plan feature. Rules using previous., delta(...) or delta2(...) need the stateful-assertions entitlement (“Stateful assertions are not available on your package” otherwise). Their first check has nothing to compare against, so it counts as a pass marked “warming” (delta2 needs three checks).
  • Large pages. Only the downloaded part of the body (the max response size, 1 MB by default) is judged. A rule whose outcome depends on not finding something in a cut-off body - body contains "x" failing, or body not contains "x" passing - cannot be proven, and the check fails with ContentTooLarge instead. Raise the max response size or match text nearer the top.
  • No implicit “any” or “all”. A list compared with a single value (body.json.path("$.items[*].price") gt 0) does not pass - say which item you mean with .first / .last, count with .count, or test every item with in [...].
  • redirects.* rules need Follow redirects on; Redirect is error cannot be combined with assertion mode.
  • Through the API, rules sent with assertMode: false are evaluated in addition to the classic status and keyword fields.