Skip to content

Creating Monitors by API

POST /projects/{projectId}/monitors creates a Monitor of any type. It takes what the dashboard’s New Monitor form takes, under the same rules, and answers 201 with the Monitor as it was made, its id and its Checks included.

The examples read your token from UUP_API_TOKEN, as Your first API call sets it up. Put your own Project’s id in the path.

A Website Monitor asks a URL for an answer. Name it, give it the url and an intervalSec, and name its locations by their id:

Terminal window
curl -X POST "https://api.uup.dev/v1/projects/prj_01J9Z3K8X4M2N6P8Q0R2S4T6V8/monitors" \
-H "Authorization: Bearer $UUP_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "http",
"name": "Website",
"url": "https://www.example.com",
"intervalSec": 60,
"locations": [
"de",
"fi",
"us"
]
}'

Left out, the request is a GET that expects a 2xx answer, and every Check it can carry is on. request sets the method, headers, body and the statuses that count as up; checks turns Performance, Certificate or Domain off. The reference lists every field.

A Port Monitor checks that a port on a host takes connections. It needs a host and a port:

Terminal window
curl -X POST "https://api.uup.dev/v1/projects/prj_01J9Z3K8X4M2N6P8Q0R2S4T6V8/monitors" \
-H "Authorization: Bearer $UUP_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "tcp",
"name": "Database",
"host": "db.example.com",
"port": 5432,
"intervalSec": 60
}'

This one names no locations, so we choose them for you. See Choosing locations.

A Heartbeat Monitor waits for your job to call it. Give it a cron Schedule, or an expectedIntervalSec, never both. A graceSec is optional:

Terminal window
curl -X POST "https://api.uup.dev/v1/projects/prj_01J9Z3K8X4M2N6P8Q0R2S4T6V8/monitors" \
-H "Authorization: Bearer $UUP_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "heartbeat",
"name": "Nightly backup",
"cron": "0 3 * * *"
}'

The answer’s heartbeat Check carries the URL your job calls, in its settings.url. Heartbeat examples show the call for each place a job runs. A heartbeat has no locations: your job calls us.

A ping Monitor needs a Validation run first, as the dashboard runs one before it saves. A host that does not answer ping looks exactly like a host that is down, so the run proves a reply comes back before the Monitor exists.

  1. Send the settings to POST /projects/{projectId}/validations. It answers 202 with the run’s id, and a state of pending.
  2. Ask GET /projects/{projectId}/validations/{validationId} every few seconds until state is no longer pending.
  3. When it is ok, create the Monitor with the same settings, its name, and the run’s id as validationId.
Terminal window
# 1. Test the settings with a Validation run. It answers 202, with the run's "id".
curl -X POST "https://api.uup.dev/v1/projects/prj_01J9Z3K8X4M2N6P8Q0R2S4T6V8/validations" \
-H "Authorization: Bearer $UUP_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "ping",
"host": "gateway.example.com",
"intervalSec": 60
}'
# 2. Ask every few seconds until its "state" is no longer "pending".
curl -H "Authorization: Bearer $UUP_API_TOKEN" \
"https://api.uup.dev/v1/projects/prj_01J9Z3K8X4M2N6P8Q0R2S4T6V8/validations/val_01J9Z3K8X4M2N6P8Q0R2S4T6V8"
# 3. When it is "ok", create the Monitor: the same settings, a name and the run's "id".
curl -X POST "https://api.uup.dev/v1/projects/prj_01J9Z3K8X4M2N6P8Q0R2S4T6V8/monitors" \
-H "Authorization: Bearer $UUP_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "ping",
"host": "gateway.example.com",
"intervalSec": 60,
"name": "Gateway",
"validationId": "val_01J9Z3K8X4M2N6P8Q0R2S4T6V8"
}'

When the run’s state is failed, its errorKind, statusCode and failedAssertion say what the target answered. Fix that, and start a new run. When it is unanswered, start a new one.

A run proves only the settings it tested. A create that sends other settings is refused with validation_mismatch, and one that waits too long with validation_expired: start a new run, and create again.

A Website Monitor with assertions needs a Validation run too, so you see what the body says before the Monitor relies on it. The steps are the ones above, with the Website settings:

Terminal window
# 1. Test the settings with a Validation run. It answers 202, with the run's "id".
curl -X POST "https://api.uup.dev/v1/projects/prj_01J9Z3K8X4M2N6P8Q0R2S4T6V8/validations" \
-H "Authorization: Bearer $UUP_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "http",
"url": "https://api.example.com/health",
"intervalSec": 60,
"assertions": [
{
"kind": "json",
"path": "$.status",
"op": "eq",
"value": "ok"
}
]
}'
# 2. Ask every few seconds until its "state" is no longer "pending".
curl -H "Authorization: Bearer $UUP_API_TOKEN" \
"https://api.uup.dev/v1/projects/prj_01J9Z3K8X4M2N6P8Q0R2S4T6V8/validations/val_01J9Z3K8X4M2N6P8Q0R2S4T6V8"
# 3. When it is "ok", create the Monitor: the same settings, a name and the run's "id".
curl -X POST "https://api.uup.dev/v1/projects/prj_01J9Z3K8X4M2N6P8Q0R2S4T6V8/monitors" \
-H "Authorization: Bearer $UUP_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "http",
"url": "https://api.example.com/health",
"intervalSec": 60,
"assertions": [
{
"kind": "json",
"path": "$.status",
"op": "eq",
"value": "ok"
}
],
"name": "API health",
"validationId": "val_01J9Z3K8X4M2N6P8Q0R2S4T6V8"
}'

A keyword assertion looks for a word in the body; a json one reads one value with a path, such as $.status, and compares it. Website and API Monitors says what each one checks.

A Monitor that is not a heartbeat is checked from one or more locations. Say which in one of three ways:

  • locations: the ones you want, by their id. GET /locations lists them.
  • locationCount: how many. We choose which.
  • Neither: we choose them, as many as the Project’s Default locations in its settings.

Name locations or locationCount, never both. A Monitor has as many locations as your Plan allows, and an odd number of them. See Locations.

When we choose, a Monitor’s locations in the answer says which were chosen. Change them later with PATCH, as you would in the Monitor’s settings.

GET /projects/{projectId}/monitors lists a Project’s Monitors. Add q to find one by part of its name or target, whatever the case: checkout finds Checkout API and https://shop.example.com/checkout. It works beside type and state.

Terminal window
curl -H "Authorization: Bearer $UUP_API_TOKEN" \
"https://api.uup.dev/v1/projects/prj_01J9Z3K8X4M2N6P8Q0R2S4T6V8/monitors?q=checkout"

A Monitor more than your Plan allows is refused with 402 and not_entitled. The body says what was refused, on which Plan, its limit and how many are used, and its message says it as the dashboard does:

{
"error": {
"code": "not_entitled",
"message": "Your Free plan includes 5 Monitors, and all 5 are in use. Delete one, or move to a larger plan.",
"kind": "monitor",
"plan": "Free",
"limit": 5,
"used": 5
}
}

Delete a Monitor, or move to a larger Plan, and send it again. A paused Monitor counts too. More locations than the Plan allows are refused the same way, with kind set to location: name fewer.

Errors and limits lists every other refusal a create can meet.