API reference#
The JSON API a monitor agent uses to enroll with a tenpm uptime server, sync
its assigned check set, and report results and status back. Generated from
docs-site/assets/tenpmuptime-monitor.yaml — see that file’s header for how
it’s kept in sync with the code.
The JSON API a monitor agent uses to enroll with a tenpm uptime server, sync its assigned check set, and report results and status back. This is the wire contract described in internal/model and implemented by internal/server/api_handlers.go; both binaries build against the same Go structs today, so treat this document as generated-by-hand from that code, not the other way around — if the two disagree, the code is right and this file is stale.
Every route except POST /api/enroll requires the monitor's own API key, minted at enrollment. POST /api/enroll requires an enrollment token instead (an org token or a platform token — see the security scheme below), since no monitor exists yet at that point.
Error responses across every endpoint are a plain-text body (one line) with a non-2xx status, not a JSON envelope — the handlers use Go's http.Error. The 4xx/5xx responses below document status code and body meaning; there is no shared error schema.
v1.0.0
Servers#
- Local dev server
http://127.0.0.1:8002
Security#
- A monitor's own API key, issued once by
POST /api/enrolland persisted by the agent alongside its minted monitor id. apiKeyAuthhttpbearer- An enrollment token minted by an org (from its "Add a monitor" page) or, for the shared fleet, a platform token minted by an admin — the latter expires one hour after minting. Only
POST /api/enrollaccepts this token kind; every other endpoint requires a monitor API key instead. enrollmentTokenAuthhttpbearer
API#
Sync the caller's assigned check set#
Returns every check the authenticated monitor should run whose updated_at is strictly after since — including checks that have become deleted: true or been unassigned, so the agent can stop running them, and, for a private-definition check, a row with its definition fields blank (see Check's description).
Store the response's server_time, not the caller's own clock, as the new watermark for the next call's since — using a local clock risks a sync gap under clock skew between the two machines.
Request (requires apiKeyAuth)#
GET /api/checksQuery parameters#
| Parameter name | Value | Description | Additional |
|---|---|---|---|
since | string (date-time) | RFC3339 timestamp; defaults to the Unix epoch (i.e. "everything") when omitted. Must be a server_time value returned by a previous call to this endpoint, not a locally-generated one. |
Responses#
200: The delta since since.
Schema: ChecksResponse
| Property | Type | Required | Description |
|---|---|---|---|
checks | array of Check | yes | |
server_time | string (date-time) | yes | Store this as the watermark for the next call's since. |
400: since was present but not a valid RFC3339 timestamp.
Schema: string
401: Missing/malformed Authorization header, or an unknown API key.
Schema: string
403: The monitor is disabled.
Schema: string
Enroll a monitor#
A monitor's one-time self-registration into the org (or shared fleet) named by the enrollment token it presents. Which of the two the token resolves to is a property of the credential, never of the request body — an agent cannot ask to be shared.
id is optional and means "reclaim the monitor you already know by this id" (used after an enrollment reset); a fresh agent omits it and is minted a new, globally unique id. All other fields are required.
The response's api_key is shown/returned exactly once — it is not retrievable again, only reset (which mints a new one and invalidates the old).
Request (requires enrollmentTokenAuth)#
POST /api/enrollRequest body (required)#
Schema: EnrollRequest
| Property | Type | Required | Description |
|---|---|---|---|
city | string | yes | |
country | string | yes | |
id | string | no | Omit to enroll as a new monitor. Set to reclaim a previously enrolled monitor by id after an enrollment reset. |
name | string | yes | |
region | string | yes |
Responses#
200: Enrolled (or reclaimed) successfully.
Schema: EnrollResponse
| Property | Type | Required | Description |
|---|---|---|---|
api_key | string | yes | Shown once. Persist it; it cannot be retrieved again, only reset (which invalidates this value and mints a new one). |
monitor_id | string | yes | Minted by the server (twelve characters of lowercase Crockford base32), globally unique across every org and the shared fleet. Persist this and pass it back as id only to reclaim this same monitor later. |
400: Malformed JSON, oversized body, or a required field (name/region/country/city) missing.
Schema: string
401: Missing/malformed Authorization header, or the enrollment token is unknown, revoked, or expired (all three look identical to the caller on purpose).
Schema: string
404: id was set but no monitor with that id exists in the token's organisation. The agent should omit id to enroll as new rather than silently becoming a second node.
Schema: string
409: id was set but that monitor is already enrolled.
Schema: string
Authenticated heartbeat#
A trivial call whose only effect is what the auth middleware already did before the handler ran: updating the monitor's last_seen_at.
Request (requires apiKeyAuth)#
GET /api/pingResponses#
200: OK.
Schema: OKResponse
| Property | Type | Required | Description |
|---|---|---|---|
status | string | no |
401: Missing/malformed Authorization header, or an unknown API key.
Schema: string
403: The monitor is disabled.
Schema: string
Upload a batch of check execution results#
Every result's monitor_id is stamped from the authenticated caller server-side; whatever the payload contains for it is ignored, so an agent cannot report results as a different monitor. A result for a check the caller is no longer assigned to is silently dropped (not an error) rather than rejected — the rest of the batch still lands, and a stale or racing unassignment must not surface as a client bug.
Insertion is idempotent, so a retried batch (e.g. after a timed-out response whose write actually succeeded) cannot double-count.
A shared monitor's batch may legitimately span several organisations in one call, since one shared agent runs checks assigned by many different orgs; a private monitor's batch belongs entirely to its own org.
Request (requires apiKeyAuth)#
POST /api/resultsRequest body (required)#
Schema: ResultsRequest
| Property | Type | Required | Description |
|---|---|---|---|
results | array of Result | yes | More than 500 in one call is rejected outright; a monitor with a larger local buffer loops the call instead. |
Responses#
200: Results accepted. accepted may be less than the number of results submitted, since unassigned-check results are dropped silently rather than counted or rejected.
Schema: ResultsResponse
| Property | Type | Required | Description |
|---|---|---|---|
accepted | integer | yes | May be less than the number of results submitted, since unassigned-check results are dropped silently rather than counted or rejected. |
400: Malformed JSON, or more than 500 results in one batch.
Schema: string
401: Missing/malformed Authorization header, or an unknown API key.
Schema: string
403: The monitor is disabled.
Schema: string
413: Request body exceeded the 1 MiB cap for this endpoint.
Schema: string
Report a monitor's periodic self-health snapshot#
Shown on the server's Monitors page. The monitor_id this updates is the authenticated caller's own id, never taken from the body.
Request (requires apiKeyAuth)#
POST /api/statusRequest body (required)#
Schema: MonitorStatus
| Property | Type | Required | Description |
|---|---|---|---|
last_outage_ended_at | string (date-time) | no | Absent until the agent has completed at least one internet- connectivity outage since it started (see monitor.ConnectivityGate). Only completed outages are reported — while offline the agent cannot call this endpoint at all. |
last_outage_sec | integer | no | |
unsent_results | integer | yes | How many results are stuck in the agent's local buffer. |
uptime_sec | integer | yes | |
version | string | yes |
Responses#
200: Accepted.
Schema: OKResponse
| Property | Type | Required | Description |
|---|---|---|---|
status | string | no |
400: Malformed JSON, or a negative unsent_results/uptime_sec/last_outage_sec.
Schema: string
401: Missing/malformed Authorization header, or an unknown API key.
Schema: string
403: The monitor is disabled.
Schema: string
Models#
Check#
A monitored target, as synced from server to monitor. url's scheme selects the check type: http/https fetch the URL (optional basic-auth userinfo), tcp://host:port tests that a TCP connect succeeds.
For a private-definition check (one whose definition has been wiped from the server — see private-checks-design.md), the server sends url, match_string, post_data, and headers as empty/absent: the real definition lives only in a JSON file loaded locally into the agent via -import-private-check, out of band from this API.
| Property | Type | Required | Description |
|---|---|---|---|
deleted | boolean | yes | True means stop running this check; it is not removed from the delta so the agent learns about the deletion at all. |
disable_redirects | boolean | no | Stop following an http(s) redirect chain; evaluate the first response as-is. Not meaningful for tcp checks. |
down_interval_sec | integer | no | Server-side alerting parameter; monitors receive it over sync but ignore it. |
enabled | boolean | yes | |
guid | string | yes | The only identifier for this check that crosses the wire. The server's internal integer id never does. |
headers | object | no | Extra request headers for http(s) checks, at most 10 entries. |
interval_sec | integer | yes | |
match_mode | string | no | Omitted/absent means contains, so rows predating this field keep their old semantics. |
match_string | string | yes | Empty asserts nothing about response content. Otherwise matched against the HTTP body, or (for tcp:// checks) the banner the service volunteers on connect. |
max_response_time_ms | integer | no | When set, a response arriving after this many milliseconds fails the check even if it otherwise passed. 0/absent means no such constraint. |
name | string | yes | |
post_data | string | no | When non-empty, turns an http(s) check into a POST with this body. |
result_detail_max_chars | integer | yes | Caps how many characters of a Result's error/ response_sample this check's results may carry. Always present (0 is a meaningful "send nothing", not "absent"). |
status_code_op | string | no | When set, replaces the built-in "status >= 400 fails" rule with an explicit comparison against status_code_value. Absent means the built-in rule, not "no constraint". Not meaningful for tcp checks. |
status_code_value | integer | no | |
timeout_sec | integer | no | Bounds one execution of the check. 0/absent means the default (10s); maximum 60s. |
updated_at | string (date-time) | yes | Fixed-width fractional seconds (see model.TimestampLayout) so lexical and chronological order agree; used as the since cursor. |
url | string | yes |
ChecksResponse#
| Property | Type | Required | Description |
|---|---|---|---|
checks | array of Check | yes | |
server_time | string (date-time) | yes | Store this as the watermark for the next call's since. |
EnrollRequest#
| Property | Type | Required | Description |
|---|---|---|---|
city | string | yes | |
country | string | yes | |
id | string | no | Omit to enroll as a new monitor. Set to reclaim a previously enrolled monitor by id after an enrollment reset. |
name | string | yes | |
region | string | yes |
EnrollResponse#
| Property | Type | Required | Description |
|---|---|---|---|
api_key | string | yes | Shown once. Persist it; it cannot be retrieved again, only reset (which invalidates this value and mints a new one). |
monitor_id | string | yes | Minted by the server (twelve characters of lowercase Crockford base32), globally unique across every org and the shared fleet. Persist this and pass it back as id only to reclaim this same monitor later. |
MonitorStatus#
A monitor's self-reported health snapshot, sent periodically. Shown on the server's Monitors page.
| Property | Type | Required | Description |
|---|---|---|---|
last_outage_ended_at | string (date-time) | no | Absent until the agent has completed at least one internet- connectivity outage since it started (see monitor.ConnectivityGate). Only completed outages are reported — while offline the agent cannot call this endpoint at all. |
last_outage_sec | integer | no | |
unsent_results | integer | yes | How many results are stuck in the agent's local buffer. |
uptime_sec | integer | yes | |
version | string | yes |
OKResponse#
| Property | Type | Required | Description |
|---|---|---|---|
status | string | no |
Result#
One check execution outcome, as reported from monitor to server.
| Property | Type | Required | Description |
|---|---|---|---|
check_guid | string | yes | The check this result is for. A result built on the monitor side must set this and never a server-internal id (there is none to send). |
error | string | no | Empty on success. |
http_status | integer | yes | |
latency_ms | integer | yes | |
monitor_id | string | yes | Ignored on write — the server stamps the authenticated caller's own id regardless of what is sent here. |
ran_at | string (date-time) | yes | |
response_sample | string | no | Leading characters of what the target sent back (HTTP body, or a tcp check's banner), capped both by the agent before sending and again by the server on ingest at result_detail_max_chars, regardless of what the payload claims. |
success | boolean | yes |
ResultsRequest#
| Property | Type | Required | Description |
|---|---|---|---|
results | array of Result | yes | More than 500 in one call is rejected outright; a monitor with a larger local buffer loops the call instead. |
ResultsResponse#
| Property | Type | Required | Description |
|---|---|---|---|
accepted | integer | yes | May be less than the number of results submitted, since unassigned-check results are dropped silently rather than counted or rejected. |