Skip to content

Success assertions

A success assertion is an extra check on a 2xx response. Use one when a 200 isn’t proof that things worked, for example a health endpoint that answers 200 with "status": "degraded", or an API that reports errors inside a 200 body.

Here is a health check with three assertions. The run passes only if all three hold:

TypeValueChecks
Status code equals200The response is exactly 200
JSON path equalsstatus=okThe body’s status field is ok
Response time under (ms)2000The response arrived in under 2 seconds

How assertions are checked

  • Only 2xx responses are checked. A 3xx, 4xx or 5xx response, a timeout or a connection error is already a failure, and assertions are skipped. An assertion can’t turn an error into a success, so “Status code equals” 404 never passes.
  • All assertions must pass. If any one fails, the run fails.
  • Up to 10 per cron or queue. Each value is text, up to 1,024 characters.
  • The full body is checked, even when the stored copy is cut at 65,535 bytes.

Assertion types

Label in the dashboardTypeValuePasses when
Status code equalsstatus_code_equalsOne status code, e.g. 200The status code is exactly this
Status code instatus_code_inCodes separated by commas, e.g. 200,201,204The status code is in the list. Spaces are allowed.
Body containsbody_containsAny text, e.g. okThe body contains the text
Body does not containbody_not_containsAny text, e.g. errorThe body doesn’t contain the text
JSON path equalsjson_path_equalspath=expected, e.g. data.status=activeThe value at the path equals the expected value
Response time under (ms)response_time_under_msMilliseconds, e.g. 5000The run took less than this

Body contains and Body does not contain

The match is exact and case-sensitive: ok doesn’t match OK. Whatever you type is the search text, quotes included. The value "error" looks for the word with its quotes, which is a handy way to match a JSON key or string rather than any occurrence of the word.

JSON path equals

The value is a path, an =, and the expected value. The body must be JSON, otherwise the check fails with “Response body is not valid JSON”.

  • Paths use dots. status is a top-level field, data.status is a nested one, and items.0.id is the id of the first element of items.
  • Only the first = splits. In note=a=b, the path is note and the expected value is a=b. Spaces around the path and the value are ignored.
  • Write the value the way JSON writes it, without quotes around strings: status=ok (not status="ok"), count=3, ratio=1.5, healthy=true, healthy=false, error=null.
  • A missing field fails. If the path isn’t in the response, the check fails with “JSON path “healthy” not found in the response”, whatever the expected value.
  • Objects and lists compare as compact JSON. meta={"ok":true} passes when meta is exactly that object. Usually it’s simpler to point the path at a single value inside it, like meta.ok=true.

Response time under (ms)

The time runs from the start of the run until the response arrives, including the DNS lookup and connecting. It must be strictly less than the value: 5000 fails at exactly 5,000 ms. Keep it below the request timeout, or the request times out before the assertion can fail.

When an assertion fails

The run fails with the reason assertion_failed (shown as “Assertion failed”). The error message lists every assertion that didn’t pass, separated by semicolons:

Assertion failed: JSON path "status" is "degraded", expected "ok"; Response time 2341ms exceeds 2000ms limit

Other messages you may see:

Expected status code 200, got 204
Status code 202 not in [200, 204]
Response body does not contain "ok"
Response body contains "error" (should not)
Response body is not valid JSON
Invalid format. Use "path=expected_value"

The response status, headers and body are still recorded, so you can compare them with your assertions. What happens next depends on where the assertion is set:

  • Cron: the run is marked Failed and counts toward Failure alerting. Crons don’t retry, so the next attempt is the next scheduled run.
  • Job: the attempt is retried like any other failure, using the queue’s retry delays. When the retries run out, the job goes to the dead letter queue. See Queues.

Where to set assertions

Assertions are set in the dashboard, on a cron or on a queue:

  • Cron: open Cron jobs, then New cron job (or Edit on an existing cron). Under Success assertions, click Add assertion, pick a type and enter a value.
  • Queue: open Queues, then New queue (or edit a queue), and use the same Success assertions section. Queue assertions apply to every job in that queue. A single job can’t have its own.

With no assertions, the box reads “No assertions added. All HTTP responses will be considered successful.” In practice that means every 2xx response. Assertions aren’t shown on the cron or queue page; open the edit form to see them.

The API can’t set assertions

Neither POST /api/crons nor POST /api/jobs accepts assertions. A success_assertions field in the request is ignored without an error. To use assertions with API-created resources:

  • Crons: create the cron with the API, then open it in the dashboard, click Edit and add the assertions.
  • Jobs: add the assertions to the queue. Queues that the API creates for you start with none.

Examples

Health check that reports its own state. Your endpoint returns {"status": "ok", "database": {"connected": true}}:

TypeValue
Status code equals200
JSON path equalsstatus=ok
JSON path equalsdatabase.connected=true
Response time under (ms)3000

API that hides errors in a 200. Fail when the body includes an error key:

TypeValue
Body does not contain"error"

Accept “done” and “nothing to do”. Your endpoint returns 200 when it did work and 204 when there was nothing to do, and anything else in the 2xx range means something unexpected happened:

TypeValue
Status code in200,204

Page must show the right content. Catch a maintenance page or an empty template served with 200:

TypeValue
Body containsOrder history
Body does not containMaintenance

Next steps