---
title: Assertions
description: Go beyond "did it respond" — assert on status codes, JSON fields, body text, headers and response time.
sidebar:
  label: Assertions
---

{/* Source of truth: packages/shared/src/monitors.ts — AssertionSources,
    AssertionComparisons, ValidComparisonsPerSource, assertionSchema,
    AssertionSeverities. */}

A 200 response is not the same as a working service. Assertions let an
[HTTP monitor](/monitors/http) judge the *content* of a response, not just the
fact that one arrived.

Each monitor can carry up to **20 assertions**. Every one is evaluated on every
check.

## Anatomy of an assertion

| Field | Meaning |
| --- | --- |
| `source` | What part of the response to look at |
| `property` | Which key or header within that source (up to 500 characters) |
| `comparison` | How to compare it |
| `target` | The value to compare against (up to 10,000 characters) |
| `severity` | `warn` → `DEGRADED`, `fail` → `DOWN` (default `fail`) |

## Sources and their comparisons

Not every comparison makes sense for every source, and Glowo rejects the ones
that do not.

| Source | Available comparisons |
| --- | --- |
| `status_code` | `equals`, `not_equals`, `any_of`, `greater_than`, `less_than` |
| `json_body` | `equals`, `not_equals`, `contains`, `not_contains`, `is_empty`, `is_not_empty`, `is_null`, `is_not_null`, `regex` |
| `text_body` | `contains`, `not_contains`, `equals`, `not_equals`, `is_empty`, `is_not_empty`, `regex` |
| `headers` | `equals`, `not_equals`, `contains`, `not_contains`, `is_empty`, `is_not_empty`, `is_null`, `is_not_null`, `regex` |
| `response_time` | `less_than`, `greater_than` |

### `property` by source

- **`json_body`** — the path to the field, for example `status` or `data.region`.
- **`headers`** — the header name, for example `content-type`.
- **`status_code`, `text_body`, `response_time`** — not used; leave empty.

## Severity: warn or fail

This is the most useful field on the page, and the one most often left at its
default.

- **`fail`** → the check is `DOWN`. Alerts fire. Uptime drops.
- **`warn`** → the check is `DEGRADED`. Visible on dashboards and status pages,
  but nobody gets paged at 3am.

Use `warn` for things that are getting worse but are not yet an outage — a
slow endpoint, a cache hit-rate field drifting, a deprecation header appearing.
Use `fail` for things that mean the service is not doing its job.

## Examples

**A health endpoint must report ok**

| Field | Value |
| --- | --- |
| source | `json_body` |
| property | `status` |
| comparison | `equals` |
| target | `ok` |
| severity | `fail` |

**Warn when the API gets slow**

| Field | Value |
| --- | --- |
| source | `response_time` |
| comparison | `greater_than` |
| target | `1000` |
| severity | `warn` |

**Catch a maintenance page replacing your app**

| Field | Value |
| --- | --- |
| source | `text_body` |
| comparison | `not_contains` |
| target | `We'll be right back` |
| severity | `fail` |

**Accept either of two valid status codes**

| Field | Value |
| --- | --- |
| source | `status_code` |
| comparison | `any_of` |
| target | `200,204` |
| severity | `fail` |

## Practical advice

- **Assert on something that breaks when your service breaks.** Matching your
  site's `<title>` proves HTML was served, not that logins work. A health
  endpoint that touches the database is far better evidence.
- **Keep `regex` simple.** A complex pattern against a large body is slow and
  hard to debug from an alert at 3am.
- **Do not assert on text you change often.** Marketing copy in an assertion
  means a copy edit pages the on-call engineer.
