Monitors

API & multi-step checks

Monitor APIs with assertions on status, headers, JSON body and latency, and chain requests into multi-step checks with variables and encrypted secrets.

SutramX has two monitor types for APIs. An API endpoint monitor sends one request (with an optional body) and checks the response against a list of assertions. A Multi-step API check runs several requests in order and passes values between them, so you can test a login flow or a create-then-read sequence end to end.

API monitors#

An API monitor has every setting of an HTTP monitor (method, headers, authentication, expected status codes, keywords, timeouts, redirects, size limits, TLS verification and alert thresholds). On top of that it adds:

  • Request body, sent with any method other than GET and HEAD
  • Assertions, rules about the status code, headers, JSON body and response time

To create one:

  1. Go to Monitors → New monitor.
  2. Set What do you want to check? to API endpoint.
  3. Choose the Method and enter the API endpoint URL.
  4. Click Show advanced options. Add headers or authentication, a Request body if needed, and your Assertions.
  5. Click Test to see whether the assertions pass against the live endpoint, then Create monitor.

The Guided preset list (JSON API and Webhook endpoint) sets up common API checks for you. See Guided presets.

Request body#

The body is sent exactly as you type it. Set a Content-Type header under Request headers if your server needs one, for example Content-Type: application/json. The dashboard only shows the Request body field for methods other than GET and HEAD.

Assertions#

Each assertion has a type, an operator, sometimes a target, and a value. Every assertion must pass for the check to pass.

Type (dashboard label)TargetWhat it compares
Status codenoneThe HTTP status code
Response headerHeader name, e.g. content-type (case-insensitive)The header's value. Repeated headers are joined with ,
JSON body / textA JSON path such as data.items[0].status, or $ for the whole body as textThe value at that path
Response time (ms)noneTotal response time in milliseconds
Operator (dashboard label)API valuePasses when
equalsequalsThe value is exactly equal
does not equalnot_equalsThe value differs
containscontainsThe value, as text, contains your text
does not containnot_containsThe value, as text, doesn't contain your text
is greater thangreater_thanThe value is numerically greater
is less thanless_thanThe value is numerically smaller
matches regexmatches_regexThe value matches your regular expression
existsexistsThe path or header is present (not missing or null). No value needed
is emptyis_emptyThe value is missing, empty or only whitespace. No value needed

In the dashboard, Status code and Response time (ms) offer only equals, does not equal, is greater than and is less than.

JSON paths. Use dots for keys and brackets for array indexes: user.id, items[2].name, $.data.token, $[0]. Quote keys that contain dots: data["odd.key"]. A path that doesn't exist resolves to nothing, so exists fails and equals fails.

Comparing values. equals is strict. In the dashboard, a value of true, false or a number is compared as a JSON boolean or number, so "ok": true matches true but not "true". Numeric strings are compared as numbers when the actual value is a number (status codes, response time, JSON numbers).

Regex limits. All matches_regex assertions in one check share a 100 ms time budget. A pattern that is invalid, runs too long, or doesn't get to run because the budget is spent counts as failed, and the error says so.

How status codes and assertions work together#

  1. If Expected status codes is set, it decides which statuses pass.
  2. If it's empty and you have Status code assertions, those decide. For example, status code is less than 500 accepts a 404.
  3. If neither is set, any status below 400 passes.

The dashboard pre-fills Expected status codes with 200. Clear it if you want your status assertions to decide.

Assertions run against every response, including error responses. If any non-status assertion fails, the check fails with Invalid response body and a message such as 2 assertions failed (first: body "data.status" equals "ok").

Edit as JSON#

Tick Edit as JSON to edit assertions as a JSON array. This is handy for copying assertions between monitors.

json
[
  { "type": "status_code", "operator": "equals", "value": 200 },
  { "type": "header", "target": "content-type", "operator": "contains", "value": "application/json" },
  { "type": "body", "target": "status", "operator": "equals", "value": "ok" },
  { "type": "body", "target": "data.items", "operator": "exists", "value": "" },
  { "type": "body", "target": "data.version", "operator": "matches_regex", "value": "^2\\." },
  { "type": "response_time", "operator": "less_than", "value": 800 }
]

Limits: up to 50 assertions per monitor, and each value can be up to 1,000 characters. Through the API you can also set "enabled": false on an assertion to keep it but skip it.

Example: create an API monitor through the API#

bash
curl -X POST "https://api.sutramx.com/monitors" \
  -H "Authorization: Bearer sk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Orders API",
    "type": "api",
    "url": "https://api.example.com/v1/orders/health",
    "interval_seconds": 60,
    "config": {
      "method": "POST",
      "headers": { "Authorization": "Bearer abc123", "Content-Type": "application/json" },
      "body": "{\"probe\": true}",
      "expected_status_codes": [200],
      "timeout": 10000,
      "assertions": [
        { "type": "body", "target": "status", "operator": "equals", "value": "ok" },
        { "type": "response_time", "operator": "less_than", "value": 1000 }
      ]
    }
  }'

Multi-step API checks#

A multi-step check runs a list of HTTP requests in order from every check location. Each step can check its response and save values (a token, an id) for later steps. The first failing step fails the whole check, and the remaining steps are marked as skipped.

Multi-step checks and the number of steps you can use depend on your plan; the hard maximum is 20 steps. The step editor shows how many you have, for example "2 of 5 steps on your plan". See Plans & limits.

To create one:

  1. Go to Monitors → New monitor and set What do you want to check? to Multi-step API check.
  2. For the first step, choose a method and enter the URL.
  3. Expand Headers, body and checks to add request headers, a body, expected status codes, assertions and values to save.
  4. Click Add step for the next request. Use the arrows to reorder steps.
  5. Add any passwords or API keys under Secrets.
  6. Click Test to run the whole chain once, then Create monitor.

Step fields#

FieldWhat it doesDefault / limits
Step nameLabel shown in results"Step N". Up to 100 characters
MethodHTTP methodGET. GET, POST, PUT, PATCH, DELETE, HEAD or OPTIONS
URLFull http:// or https:// URLRequired, up to 2048 characters. Variables are allowed in the path and query string only, never in the host. No user:password@ in the URL
Request headersHeaders for this step; values can use variablesUp to 30 per step
Request bodyBody for this step; can use variablesUp to 16 KB. Not allowed for GET and HEAD. If you don't set Content-Type, JSON bodies get application/json and anything else gets text/plain; charset=utf-8
Expected status codese.g. 200 or 200-299Empty: any status below 400 passes (or what your status assertions allow)
AssertionsSame types and operators as API monitors; values can use variablesUp to 20 per step
Save values for later stepsSaves part of the response into a variableUp to 10 per step

Each step follows up to 5 redirects. Unless you set them, steps send User-Agent: Mozilla/5.0 (compatible; SutramX-Monitor/1.0; +https://sutramx.com/bot) and Accept: application/json, text/plain, */*.

Check-level settings#

These are under Show advanced options.

FieldWhat it doesDefault / limits
Timeout (seconds)Time for all steps together30 s. 1–60 s, capped at the interval minus 3 s. Each step gets whatever time is left
Max total time (ms)Fails the check if the whole run is slowerOff. 1–600,000 ms
Degraded above (ms)Marks the check Degraded if the whole run is slowerOff. Lower than the max total time
Verify TLS certificatesApplies to every stepOn

Variables#

Click Save a value on a step to extract a value from its response.

Source (dashboard label)ExpressionExample
JSON pathPath into the JSON bodydata.token
Response headerHeader namex-request-id
Regex on bodyRegular expression. The first capture group is saved, or the whole match if there is nonecsrf" value="([^"]+)"

Use a saved value in any later step as {{name}}. Names use letters, digits and _, can't start with a digit, and can be up to 64 characters. Objects and arrays are saved as JSON text, and saved values are cut at 8 KB.

If a value can't be extracted (path missing, header absent, pattern didn't match), that step fails with a message such as Could not extract token: JSON path data.token was not found in the response.

Built-in variables are available in every step:

VariableValue
{{$timestamp}}Current Unix time in seconds
{{$timestamp_ms}}Current Unix time in milliseconds
{{$iso_time}}Current time in ISO 8601
{{$uuid}}A random UUID

The step editor lists the variables available at each step under Available here. Saving fails if a step uses a variable that no earlier step saves.

Secrets#

Under Secrets, add passwords, API keys and tokens, and reference them as {{secrets.NAME}}.

  • Secrets are stored encrypted, sent only to the check locations, and never shown again.
  • When editing, a stored secret shows as stored. Leave its value empty to keep it, enter a new value to replace it, or remove it.
  • Up to 20 secrets per check, each up to 4,096 characters.
  • Saving fails if a step uses a secret that hasn't been set.

Secret values and saved variable values (4 characters or longer) are replaced with [redacted] in check results, error messages and alerts. Saved values are never stored.

Example: log in, then call an authenticated endpoint#

json
{
  "name": "Login and fetch profile",
  "type": "multistep",
  "interval_seconds": 300,
  "config": {
    "timeout": 30000,
    "verify_tls": true,
    "steps": [
      {
        "name": "Log in",
        "method": "POST",
        "url": "https://api.example.com/auth/login",
        "body": "{\"email\":\"probe@example.com\",\"password\":\"{{secrets.PASSWORD}}\"}",
        "expected_status_codes": [200],
        "extract": [{ "name": "token", "source": "json", "expression": "data.token" }]
      },
      {
        "name": "Get profile",
        "method": "GET",
        "url": "https://api.example.com/me?t={{$timestamp}}",
        "headers": { "Authorization": "Bearer {{token}}" },
        "assertions": [{ "type": "body", "target": "email", "operator": "equals", "value": "probe@example.com" }]
      }
    ],
    "secrets": { "PASSWORD": "correct-horse-battery-staple" }
  }
}

Send this to POST https://api.sutramx.com/monitors. In secrets, a string sets or replaces a secret and null deletes it. Through the API, each step also accepts max_redirects (0–5), and the check accepts max_response_bytes (up to 10 MB, 2 MB by default).

Reading results#

The detail page's Latest run card shows each step with its status (passed, failed or skipped), HTTP status, duration, assertion counts and which variables it saved. The response time of the check is the time for the whole run. A failure message names the step, for example Step 2 (Get profile): Received HTTP 401.

Troubleshooting#

My status assertion is ignored. Expected status codes takes priority. Clear it to let status assertions decide.

equals fails although the value looks right. Check the type. 200 (number) and "200" (string) differ for JSON body values, and string comparison is case-sensitive.

Later steps show as skipped. A step before them failed, for example because a value couldn't be extracted. Open the Latest run card to see which step failed first and why.

"ran out of time". The whole chain must finish within the check timeout. Raise Timeout (seconds), increase the interval, or remove slow steps.

"variables can be used in the path and query string, not in the host". Each step's host is fixed when you save, so SutramX can validate it. Use a separate step URL for each host.

Last updated . Something unclear or missing on this page? Tell us at support@sutramx.com.