Guides / Monitoring guide

Monitoring guide

Practical recipes for monitors that catch real failures — REST APIs, third-party dependencies, heartbeats, SSL, and the assertion mental model.

On this page

Monitoring Guide

Practical recipes for setting up monitors that catch the right kinds of failures. Skip to the section that matches your endpoint.

The mental model

Every monitor is a scheduled HTTP request + a list of assertions. The probe is "up" only when every assertion passes. The monitor is "down" globally only when ≥ quorum regions report "up" failure within the last failureThreshold consecutive checks. (Tunable per monitor.)

When a monitor flips down, the IncidentCoordinatorDO opens an incident and dispatches to every attached alert channel. When it flips back up, you get a recovery notification. Incidents dedupe — you won't get spammed.

Recipe: REST API health endpoint

You have GET https://api.example.com/health returning { "ok": true, "version": "..." }.

FieldValue
URLhttps://api.example.com/health
MethodGET
Interval60s
Timeout5000ms
RegionsAll 5 (default)
Quorum2
Failure threshold2
Assertionssee below
status_code            eq        200
response_time_ms       lt        2000
header[content-type]   contains  application/json
json_path: $.ok        eq        true

The first three guard against the obvious failure modes (HTTP error, slowness, content-type drift). The json_path assertion turns "the server returned 200 OK with junk" into a real failure.

Recipe: Public JSON API (third-party check)

You depend on https://api.example.com/v1/products/123 and want to know if it goes down or starts returning the wrong shape.

status_code             eq        200
response_time_ms        lt        5000
json_path: $.id         eq        123
json_path: $.name       exists
json_path: $.price      gt        0

The exists operator is your friend for fields you require but don't know the exact value of. The gt 0 on price catches the "API returned an object but all fields are null" failure mode.

Recipe: Static site / landing page

You have https://example.com and want to know it serves your real HTML, not a CDN error page.

status_code             eq        200
response_time_ms        lt        3000
header[content-type]   contains   text/html
body_text               contains  Acme Corporation

The body_text contains of a known phrase from your hero copy is the cheapest way to detect "site got replaced with a 200-OK error page" or "deploy half-shipped a partially rendered template."

Recipe: Authenticated endpoint

You want to monitor an endpoint that requires a token. Don't put production secrets in monitor headers — create a dedicated read-only monitoring API token at your service.

URL: https://api.example.com/v1/me
Method: GET
Headers:
  Authorization: Bearer <monitoring-token>
  X-Probed-By: thump

Assertions:
  status_code           eq        200
  response_time_ms      lt        2000
  json_path: $.id       exists

If your API has rate limits, set the interval to ≥ 5 min to stay polite.

Recipe: GraphQL endpoint

GraphQL is POST to a single URL with a query in the body.

URL: https://api.example.com/graphql
Method: POST
Headers:
  Content-Type: application/json
Body:
  {"query":"{ health { status } }"}

Assertions:
  status_code                          eq        200
  json_path: $.data.health.status      eq        ok
  json_path: $.errors                  not_exists

The not_exists on $.errors catches GraphQL's "200 OK with errors in the body" pattern. Critical — GraphQL almost never returns non-200 for query failures.

Recipe: Status / heartbeat ping

You have a backend job that pings https://your-service.dev/heartbeat every 5 minutes. You want to know if it stops.

This is "inverted monitoring" — thump probes a URL. For now, the recommendation is to:

  1. Have your job write last_heartbeat to a public endpoint like https://your-service.dev/last-ping that returns { "ts": <unix-ms> }
  2. Monitor that endpoint and assert the timestamp is recent:
    json_path: $.ts        gt        <some-stale-threshold-ms>

Real "missing heartbeat" detection is on the roadmap (we'll let you POST to a thump endpoint and alert on absence), but it's not implemented yet.

Pricing-style assertions you might want

Picking interval + quorum

Start with 60s interval + 5 regions + quorum 2. That gives you:

Tune down to 30s + quorum 3 for production-critical endpoints. Tune up to 5min + quorum 1 for non-critical or rate-limited targets.

Setting up alerts

Once your monitor is created, add an alert channel under /app/channels:

Then go back to the monitor and attach the channel under "Alert channels" — or attach the channel to N monitors at once from the channel detail page.

Testing before saving

The monitor-create form has a "Test now" button that runs a one-off probe with your current draft (URL + assertions) and shows pass/fail per assertion + the captured actual value. Use it to tune your json_path expressions before saving — they're notoriously easy to typo.

Public playground

Anyone can probe a URL at https://thump.dev — paste it into the "Try it right now" widget on the landing page. Rate-limited to 20 probes / 5 min per IP. No signup. Useful for one-off "is this URL up?" checks.