Skip to content

What do you need to do?

Docs

Monitoring as code

Keep your http checks in a realuptime.yaml (or .json) file next to the code they monitor, and let the realuptime CLI reconcile the account to it through the REST API: a plan you can read, a diff by name, and exit codes CI can branch on.

The file

Every key is the REST API's own POST /checks field, by name and by rule: nothing is renamed and nothing is added, so a check here, a check in a curl body, and a check in the Terraform provider are written with the same words. name is the key a check is matched on, so it must be unique within the file. Omit regions for every live region. The file is validated in full before anything is sent, and every problem is listed at once.

yaml
version: 1
checks:
  - name: API health
    url: https://api.example.com/health
    intervalSeconds: 60
    regions: [iad, sjc, fra, nrt]
    assertionStatusMin: 200
    assertionStatusMax: 299
    assertionJsonPath: data.status
    assertionJsonOp: equals
    assertionJsonValue: ok

  - name: Storefront
    url: https://shop.example.com/
    regions: [iad, fra]
    assertionBodyOp: contains
    assertionBodyValue: Add to cart

  # Minimal: every live region, your plan's fastest interval.
  - name: Marketing site
    url: https://www.example.com/

YAML here is a strict, documented subset the CLI parses itself (the CLI has no dependencies): block mappings and lists, [flow, lists] of scalars, quoted and plain strings, numbers, booleans, # comments. Anchors, tags, block scalars (|/>), flow mappings, and multi-document streams are refused with a line number rather than guessed at. A .json file with the same keys is always accepted.

Plan, then apply

bash
realuptime export -f realuptime.yaml                 # bootstrap the file from your account
realuptime validate -f realuptime.yaml               # parse + validate only, no API call
realuptime apply -f realuptime.yaml --dry-run        # plan; exit 2 if anything would change
realuptime apply -f realuptime.yaml                  # create / update
realuptime apply -f realuptime.yaml --prune --yes    # also delete http checks not in the file
realuptime apply -f realuptime.yaml --allow-replace  # accept delete+recreate for url/intervalSeconds changes
realuptime apply -f realuptime.yaml --dry-run --json # the plan as JSON

apply lists the account's checks, matches each file entry by name, and prints a plan before doing anything. The prefixes are stable: + create, ~ update, ! replace, - delete, = no change, ? a note about something the plan leaves alone.

console
$ realuptime apply -f realuptime.yaml --dry-run
Plan: 1 to create, 1 to update, 0 to replace, 0 to delete, 1 unchanged.
  + check "API health" (create)
  ~ check "Storefront" (update)
      regions: [iad, sjc, fra, nrt] -> [iad, fra]
      assertionBodyOp: (none) -> "contains"
      assertionBodyValue: (none) -> "Add to cart"
      assertionBodyCaseSensitive: (none) -> true
  = check "Marketing site" (no changes)
  ? check "Legacy": on the account but not in the file (pass --prune to delete it).
  ? check "Nightly backup": type "heartbeat" is not manageable through the REST API; left alone.
(dry run; nothing applied)
$ echo $?
2

What an update can and cannot change in place

The REST API edits regions (PATCH /checks/:id) and the assertion fields (PATCH /checks/:id/assertions, a full replace) in place, so those are ordinary updates. It has no endpoint for changing url or intervalSeconds on an existing check, so a change to either is a replace: delete and recreate, which loses the check's history and id. The plan says so on that row, and apply refuses to do it unless you pass --allow-replace. Nothing is recreated by surprise.

Leaving intervalSeconds out of an entry means "don't manage it" (the account's value stands). Leaving out every assertion field means "no assertions", and an account check that has some will have them cleared, the same way the assertions endpoint itself treats an empty body.

Deleting: only with --prune, only with --yes

An http check on the account that the file doesn't name is reported as a note and left alone. Pass --prune to plan its deletion, and --yes to confirm (there is no interactive prompt; CI can't answer one). Heartbeat, TCP, DNS, SMTP, and multistep checks are never touched: the API can't create them, so the file can't declare them, so apply never deletes them.

Order and failure

Changes apply deletes first, then replaces, then creates, then updates, so allowance is freed before it is consumed. On the first API refusal the run stops, prints the API's own error message and RU-XXXX code exactly as returned, marks the remaining steps skipped, and exits 1. Re-running is safe: the plan is always recomputed from live state, so a second apply picks up where the first stopped, and a run with nothing to do prints Nothing to apply. and exits 0.

Exit codes

CodeMeaning
0Applied successfully, or nothing to do, or a dry run that found no changes.
1Invalid file, an API refusal, a plan the CLI can't carry out (a name shared with a non-http check, two account checks with one name), or a replace/prune that needed a flag you didn't pass.
2--dry-run found pending changes: the "drift detected" signal.
yaml
- name: Apply monitoring
  env:
    REALUPTIME_API_KEY: ${{ secrets.REALUPTIME_API_KEY }}
  run: |
    realuptime validate -f realuptime.yaml
    realuptime apply -f realuptime.yaml --dry-run || test $? -eq 2   # 2 = changes pending, fine on a PR
    # on main:
    # realuptime apply -f realuptime.yaml

Exporting an existing account

realuptime export -f realuptime.yaml writes your account's http checks in this format (by extension: .json for JSON; without -f it prints to stdout). It round-trips: applying the exported file straight back plans zero changes. Checks of other types are named in a header comment rather than dropped.

What the file can declare today

Version 1 declares checks only, because the REST API can create and edit http checks and nothing else: status pages are read-only through the API, and components, notification channels, and alert rules have no API resource yet. A top-level key for one of those is refused with a message saying exactly that, rather than accepted and ignored. When the API gains those resources, the file gains them under their API field names, same as checks.

Requirements

A read_write API key (REALUPTIME_API_KEY or --api-key), the same tier and rate-limit rules as the REST API itself, and Node 22 for the CLI. The CLI is the @realuptime/cli workspace package described in the API reference.