Account API reference#

The read-only, customer-facing JSON API for a person’s own account data — checks, monitors, results, heartbeats, notification channels, members, and plan settings — plus a full data-export endpoint. This is a different surface from the monitor API: that one is what a monitor agent uses to talk to the server, authenticated as a monitor; this one is what a person (or a script acting on their behalf) uses to read their own org’s data, authenticated with their own bearer token minted from the organisation settings page. Generated from docs-site/assets/tenpmuptime-account.yaml — see that file’s header for how it’s kept in sync with the code.

Download the raw OpenAPI spec (tenpmuptime-account.yaml)

The read-only, customer-facing JSON API for a person's own account data - checks, monitors, results, heartbeats, notification channels, members, and plan settings - plus a full data-export endpoint. This is a different surface from the monitor sync API documented in tenpmuptime-monitor.yaml: that one is what a monitor agent uses to talk to the server, authenticated as a monitor; this one is what a person (or a script acting on their behalf) uses to read their own org's data, authenticated with their own bearer token. The wire contract is implemented by internal/server/account_api_handlers.go and account_api.go; 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. Read-only in this pass. There is no way to create or edit a check, monitor, or anything else through this API yet - every route below is a GET. See account-api-design.md's "Open items" for why a write path is deferred rather than built partially. Authentication. Every route requires a personal access token (Authorization: Bearer tpk_...), scoped to exactly one organisation. There is no endpoint in this API to mint one - a token is minted from the browser UI instead, at POST /settings/api-tokens on the organisation settings page (session-authed, CSRF-protected, not part of this API), and only by that org's owner. The plaintext is shown exactly once, on the page that minted it; only its hash is ever stored, so a lost token can only be revoked and re-minted, never recovered. Minting is refused outright during an admin "login as" impersonation session, so a one-hour support session can never leave behind a standing credential. Membership is re-proved on every request (the same continuous check a browser session gets): if the user who minted a token is later removed from the org, the token stops working immediately, with no separate revocation step required. Error responses across every endpoint are a plain-text body (one line) with a non-2xx status, not a JSON envelope - same convention the monitor API uses. The 4xx/5xx responses below document status code and body meaning; there is no shared error schema. Rate limits, two layers. An outer, client-address-keyed limit (shared with the monitor API's own route table entry) catches unauthenticated noise before any database read happens. The real, per-customer budget is applied per token id once the token is resolved: 5 requests/second, burst 20, for every route except GET /account-api/v1/export, which - because it alone walks a check's entire retained result history - has its own, far tighter budget of once a minute, burst 2, on top of the general one. Exceeding either answers 429 rate limit exceeded. Versioning. Paths are versioned from the start (/account-api/v1/), landed 2026-09-27 alongside the monitor API's own move to /monitor-api/v1/ - the two were given distinct prefixes rather than sharing one /api/ root specifically because this API's /checks//checks/{guid} would otherwise collide with the monitor API's own /checks. The same additive-only rule applies here: a JSON change within v1 only ever adds a field; a removed field or a changed meaning gets /account-api/v2/ instead.

v1.0.0

Servers#

Local dev server
http://127.0.0.1:8002

Security#

A person's own personal access token, prefixed tpk_ in its plaintext form so it's recognizable on sight in a log line or a secret scanner. Minted from the browser UI at POST /settings/api-tokens (not part of this API), by the org's owner only, shown exactly once. Scoped to one organisation; the server re-proves the minting user's membership in that org on every request, so removing them from the org silently invalidates every token they minted with no separate revocation step.
accountAPIKeyAuth
http bearer

API#

List this org's checks#

Every check in the org, in the same shape as the check import/export file (checkExport) - the account API's check resource and the existing GET /checks/export format are deliberately the same shape.

Request (requires accountAPIKeyAuth)#

GET /account-api/v1/checks

Responses#

200: The org's checks.

Schema: array of Check

PropertyTypeRequiredDescription
cert_expiry_warn_daysintegernoFor https/tls checks. 0/absent means no such constraint; the boundary itself fails (14 set fails at exactly 14 days left).
disable_redirectsbooleanno
down_interval_secintegernoHow many consecutive failures before this check is considered down and an alert fires.
enabledbooleanyes
guidstringyes
headersobjectno
insecure_skip_verifybooleanno
interval_secintegeryesHow often the check runs in total, across every monitor assigned to it.
invert_resultbooleanno"Upside-down mode": flips the final pass/fail verdict.
match_modestringnoAbsent means contains.
match_stringstringyes
max_response_time_msintegerno0/absent means no such constraint.
namestringyes
post_datastringno
resend_interval_secintegernoHow often a still-failing check repeats its DOWN notification; 0/absent means never.
restrictedbooleanyesWhether this check is restricted to the org's own monitors (opted in explicitly, or derived from the target being an internal/non-public address).
result_detail_max_charsintegeryesCaps 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_opstringnoAbsent means the built-in "status >= 400 fails" rule, not "no constraint".
status_code_valueintegerno
timeout_secintegerno0/absent means the default (10s).
urlstringyes

401: Missing/malformed Authorization header, the token is unknown or revoked, the minting user's account has been disabled, or that user is no longer a member of the token's org.

Schema: string

429: Rate limit exceeded (per-token or per-client-address).

Schema: string

Get one check#

Request (requires accountAPIKeyAuth)#

GET /account-api/v1/checks/{guid}

Path parameters#

Parameter nameValueDescriptionAdditional
guidstringRequired

Responses#

200: The check.

Schema: Check

PropertyTypeRequiredDescription
cert_expiry_warn_daysintegernoFor https/tls checks. 0/absent means no such constraint; the boundary itself fails (14 set fails at exactly 14 days left).
disable_redirectsbooleanno
down_interval_secintegernoHow many consecutive failures before this check is considered down and an alert fires.
enabledbooleanyes
guidstringyes
headersobjectno
insecure_skip_verifybooleanno
interval_secintegeryesHow often the check runs in total, across every monitor assigned to it.
invert_resultbooleanno"Upside-down mode": flips the final pass/fail verdict.
match_modestringnoAbsent means contains.
match_stringstringyes
max_response_time_msintegerno0/absent means no such constraint.
namestringyes
post_datastringno
resend_interval_secintegernoHow often a still-failing check repeats its DOWN notification; 0/absent means never.
restrictedbooleanyesWhether this check is restricted to the org's own monitors (opted in explicitly, or derived from the target being an internal/non-public address).
result_detail_max_charsintegeryesCaps 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_opstringnoAbsent means the built-in "status >= 400 fails" rule, not "no constraint".
status_code_valueintegerno
timeout_secintegerno0/absent means the default (10s).
urlstringyes

401: See GET /account-api/v1/checks.

Schema: string

404: No check with this guid in the token's org.

Schema: string

429: Rate limit exceeded.

Schema: string

Page through a check's results, oldest first#

Keyset-paginated, not offset: after/after_monitor anchor on the last row a previous page actually returned ((ran_at, monitor_id), unique per check), rather than a row count that shifts underneath a caller as the results table keeps growing. Keep paging with the response's own next cursor until a page comes back with an empty results and no next.

Request (requires accountAPIKeyAuth)#

GET /account-api/v1/checks/{guid}/results

Path parameters#

Parameter nameValueDescriptionAdditional
guidstringRequired

Query parameters#

Parameter nameValueDescriptionAdditional
afterstring (date-time)RFC3339 timestamp. Omit to start from the beginning. Must be a value taken from a previous page's next.after, paired with after_monitor from the same cursor.
after_monitorstringThe monitor id half of the same cursor as after.

Responses#

200: One page of results.

Schema: CheckResultsPage

PropertyTypeRequiredDescription
nextnoPresent whenever this page returned any rows. Absent (both this field and an empty results) means there is nothing more to page.
resultsarray of Resultyes

400: after was present but not a valid RFC3339 timestamp.

Schema: string

401: See GET /account-api/v1/checks.

Schema: string

404: No check with this guid in the token's org.

Schema: string

429: Rate limit exceeded.

Schema: string

Export this org's full data as one JSON document#

The mechanism behind a customer data-export request (GDPR Art. 20 / CCPA "right to know"). Everything the other endpoints in this API can return, plus every retained result for every check, in one response - scoped to this org's own data, not a full personal-data export of the requesting user themselves (see account-api-design.md's "Open items" for that gap).

The header section (org name, entitlements, plan usage, checks, monitors, heartbeats, notification channels, members) is small and bounded by the org's own entitlements, so it is built and returned whole. The results section is streamed: one bracketed array per check, keyed by the check's guid, written directly from the same keyset cursor GET .../checks/{guid}/results uses - a Team-tier org's full raw-retention history can reach tens of millions of rows, too large to hold in memory as both a Go slice and its own JSON encoding at once. The accepted tradeoff: once the 200 and part of the body are already on the wire, a mid-stream store error can only be logged and the connection closed, leaving a truncated (invalid) JSON document as the only signal - a client should treat a response that fails to parse as a failed export to retry, not a partial success to salvage.

Deliberately excluded from the export: each result's own alert/notification delivery history - that describes this server's notification attempts, not the check's data.

Response is served with Content-Disposition: attachment so a browser hitting this URL directly downloads a file rather than rendering it inline.

Request (requires accountAPIKeyAuth)#

GET /account-api/v1/export

Responses#

200: The full export document: {...AccountExportHeader fields, "results": {"<check guid>": [Result, ...], ...}}. See the description above for the streaming caveat - a response that doesn't parse as valid JSON should be treated as a failed export.

Schema: AccountExport

PropertyTypeRequiredDescription
checksarray of Checkyes
entitlementsEntitlementsyesThis org's plan limits (billing-plans-design.md). Serialized with Go's default field-name-as-key behavior - this type carries no json tags of its own, since it's shared as-is with the admin UI and the tenant plan page rather than given a second, API-specific shape.
generated_atstring (date-time)yes
heartbeatsarray of Heartbeatyes
membersarray of Memberyes
monitorsarray of Monitoryes
notification_channelsarray of NotificationChannelyes
org_namestringyes
plan_usagePlanUsageyesA snapshot of how much of each entitlement above this org actually uses right now. Same no-json-tags treatment as Entitlements.
resultsobjectyesKeyed by each check's own guid; each value is the full array of that check's retained results, oldest first, with no pagination (unlike GET .../checks/{guid}/results, this endpoint walks every page internally and streams the concatenated result).

401: See GET /account-api/v1/checks.

Schema: string

429: Rate limit exceeded. /export has its own much tighter budget (once a minute, burst 2) than every other route in this API, independent of and in addition to the general per-token budget.

Schema: string

List this org's heartbeat checks#

Never includes a heartbeat's own ingest token - that credential is shown once, on the tenant detail page, and is not re-derivable from a listing anywhere, including here.

Request (requires accountAPIKeyAuth)#

GET /account-api/v1/heartbeats

Responses#

200: The org's heartbeats.

Schema: array of Heartbeat

PropertyTypeRequiredDescription
down_sincestring (date-time)no
enabledbooleanyesTrue only when both the owner's own enabled bit and the platform's kill switch (admin_enabled) are true.
grace_secintegeryesExtra time allowed past interval_sec before a missed beat is considered a failure.
guidstringyes
interval_secintegeryes
last_heartbeat_atstring (date-time)no
namestringyes
resend_interval_secintegernoHow often a still-down heartbeat repeats its DOWN notification; 0/absent means never.
show_on_status_pagebooleanyes
statestringyesThe stored state. Note this can read stale/misleading when enabled is false - see Heartbeat.Status()'s own reasoning: a disabled or paused heartbeat is never swept, so its stored state is not a live fact.

401: See GET /account-api/v1/checks.

Schema: string

429: Rate limit exceeded.

Schema: string

List this org's members#

Request (requires accountAPIKeyAuth)#

GET /account-api/v1/members

Responses#

200: The org's members, owner first, then alphabetically by email.

Schema: array of Member

PropertyTypeRequiredDescription
emailstringyes
idinteger (int64)yes
is_ownerbooleanyes
joined_atstring (date-time)yes

401: See GET /account-api/v1/checks.

Schema: string

429: Rate limit exceeded.

Schema: string

List monitors available to this org#

This org's own monitors (enabled or not) plus every currently enabled shared-fleet monitor - the same set the tenant Monitors page shows (ListMonitorsAndSharedFleet).

Request (requires accountAPIKeyAuth)#

GET /account-api/v1/monitors

Responses#

200: The monitor list.

Schema: array of Monitor

PropertyTypeRequiredDescription
citystringyes
classstringyes
countrystringyes
enabledbooleanyesTrue only when both the org's own enabled bit and the platform's own kill switch (admin_enabled) are true - the same AND-of-both-flags convention the monitor sync API reports to an agent as its own single enabled field.
idstringyesGlobally unique across every org and the shared fleet.
namestringyes
regionstringyes

401: See GET /account-api/v1/checks.

Schema: string

429: Rate limit exceeded.

Schema: string

List this org's notification channels#

Includes each channel's real destination/secret (ntfy_topic/email/webhook_url/webhook_secret) unredacted - only the org's owner can mint the token that reaches this endpoint, and the owner can already read every one of these values from the organisation settings page, so redacting them here would protect nothing.

Request (requires accountAPIKeyAuth)#

GET /account-api/v1/notification-channels

Responses#

200: The org's notification channels.

Schema: array of NotificationChannel

PropertyTypeRequiredDescription
emailstringno
enabledbooleanyes
idinteger (int64)yes
kindstringyes
namestringyes
ntfy_topicstringno
webhook_secretstringnoMeaningful only for the webhook kind. Empty is the deliberate "send unsigned" state, not an omitted field.
webhook_urlstringnoThe destination for both webhook and slack kinds.

401: See GET /account-api/v1/checks.

Schema: string

429: Rate limit exceeded.

Schema: string

Get this org's name, plan entitlements, and current usage#

Request (requires accountAPIKeyAuth)#

GET /account-api/v1/settings

Responses#

200: The org's settings.

Schema: AccountSettings

PropertyTypeRequiredDescription
entitlementsEntitlementsyesThis org's plan limits (billing-plans-design.md). Serialized with Go's default field-name-as-key behavior - this type carries no json tags of its own, since it's shared as-is with the admin UI and the tenant plan page rather than given a second, API-specific shape.
org_namestringyes
plan_usagePlanUsageyesA snapshot of how much of each entitlement above this org actually uses right now. Same no-json-tags treatment as Entitlements.

401: See GET /account-api/v1/checks.

Schema: string

429: Rate limit exceeded.

Schema: string

Models#

AccountExport#

The full export document: every other resource in this API plus a results object keyed by check guid. See GET /account-api/v1/export's own description for the streaming caveat that makes this shape hand-assembled rather than a single marshaled struct.

PropertyTypeRequiredDescription
checksarray of Checkyes
entitlementsEntitlementsyesThis org's plan limits (billing-plans-design.md). Serialized with Go's default field-name-as-key behavior - this type carries no json tags of its own, since it's shared as-is with the admin UI and the tenant plan page rather than given a second, API-specific shape.
generated_atstring (date-time)yes
heartbeatsarray of Heartbeatyes
membersarray of Memberyes
monitorsarray of Monitoryes
notification_channelsarray of NotificationChannelyes
org_namestringyes
plan_usagePlanUsageyesA snapshot of how much of each entitlement above this org actually uses right now. Same no-json-tags treatment as Entitlements.
resultsobjectyesKeyed by each check's own guid; each value is the full array of that check's retained results, oldest first, with no pagination (unlike GET .../checks/{guid}/results, this endpoint walks every page internally and streams the concatenated result).

AccountSettings#

PropertyTypeRequiredDescription
entitlementsEntitlementsyesThis org's plan limits (billing-plans-design.md). Serialized with Go's default field-name-as-key behavior - this type carries no json tags of its own, since it's shared as-is with the admin UI and the tenant plan page rather than given a second, API-specific shape.
org_namestringyes
plan_usagePlanUsageyesA snapshot of how much of each entitlement above this org actually uses right now. Same no-json-tags treatment as Entitlements.

Check#

The same shape as GET /checks/export's file format (checkExport) - the account API's check resource is that format, not a new one. For a private-definition check (one whose definition has been wiped from the server - see private-checks-design.md), url/match_string/post_data/headers and the other content-group fields are empty/absent, the same treatment the monitor sync API gives such a check.

PropertyTypeRequiredDescription
cert_expiry_warn_daysintegernoFor https/tls checks. 0/absent means no such constraint; the boundary itself fails (14 set fails at exactly 14 days left).
disable_redirectsbooleanno
down_interval_secintegernoHow many consecutive failures before this check is considered down and an alert fires.
enabledbooleanyes
guidstringyes
headersobjectno
insecure_skip_verifybooleanno
interval_secintegeryesHow often the check runs in total, across every monitor assigned to it.
invert_resultbooleanno"Upside-down mode": flips the final pass/fail verdict.
match_modestringnoAbsent means contains.
match_stringstringyes
max_response_time_msintegerno0/absent means no such constraint.
namestringyes
post_datastringno
resend_interval_secintegernoHow often a still-failing check repeats its DOWN notification; 0/absent means never.
restrictedbooleanyesWhether this check is restricted to the org's own monitors (opted in explicitly, or derived from the target being an internal/non-public address).
result_detail_max_charsintegeryesCaps 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_opstringnoAbsent means the built-in "status >= 400 fails" rule, not "no constraint".
status_code_valueintegerno
timeout_secintegerno0/absent means the default (10s).
urlstringyes

CheckResultsPage#

PropertyTypeRequiredDescription
nextnoPresent whenever this page returned any rows. Absent (both this field and an empty results) means there is nothing more to page.
resultsarray of Resultyes

Entitlements#

This org's plan limits (billing-plans-design.md). Serialized with Go's default field-name-as-key behavior - this type carries no json tags of its own, since it's shared as-is with the admin UI and the tenant plan page rather than given a second, API-specific shape.

PropertyTypeRequiredDescription
AllowChannelEmailbooleanno
AllowChannelNtfybooleanno
AllowChannelSlackbooleanno
AllowChannelWebhookbooleanno
AllowStatusPagebooleanno
CustomPriceCentsintegerno
CustomPricePeriodstringnoEmpty means no price has been recorded, independent of Plan.
MaxChecksintegerno0 = unlimited.
MaxHeartbeatsintegerno0 means the plan includes no heartbeats at all - unlike the other Max* fields here, there is no unlimited value for this one.
MaxMembersintegerno0 = unlimited.
MaxNotificationChannelsintegerno0 = unlimited.
MaxTimeoutSecintegerno
MinIntervalSecintegerno
Planstringno
RetentionRawDaysintegerno
RetentionRollupDaysintegerno

Heartbeat#

PropertyTypeRequiredDescription
down_sincestring (date-time)no
enabledbooleanyesTrue only when both the owner's own enabled bit and the platform's kill switch (admin_enabled) are true.
grace_secintegeryesExtra time allowed past interval_sec before a missed beat is considered a failure.
guidstringyes
interval_secintegeryes
last_heartbeat_atstring (date-time)no
namestringyes
resend_interval_secintegernoHow often a still-down heartbeat repeats its DOWN notification; 0/absent means never.
show_on_status_pagebooleanyes
statestringyesThe stored state. Note this can read stale/misleading when enabled is false - see Heartbeat.Status()'s own reasoning: a disabled or paused heartbeat is never swept, so its stored state is not a live fact.

Member#

PropertyTypeRequiredDescription
emailstringyes
idinteger (int64)yes
is_ownerbooleanyes
joined_atstring (date-time)yes

Monitor#

PropertyTypeRequiredDescription
citystringyes
classstringyes
countrystringyes
enabledbooleanyesTrue only when both the org's own enabled bit and the platform's own kill switch (admin_enabled) are true - the same AND-of-both-flags convention the monitor sync API reports to an agent as its own single enabled field.
idstringyesGlobally unique across every org and the shared fleet.
namestringyes
regionstringyes

NotificationChannel#

PropertyTypeRequiredDescription
emailstringno
enabledbooleanyes
idinteger (int64)yes
kindstringyes
namestringyes
ntfy_topicstringno
webhook_secretstringnoMeaningful only for the webhook kind. Empty is the deliberate "send unsigned" state, not an omitted field.
webhook_urlstringnoThe destination for both webhook and slack kinds.

PlanUsage#

A snapshot of how much of each entitlement above this org actually uses right now. Same no-json-tags treatment as Entitlements.

PropertyTypeRequiredDescription
Checksintegerno
ChecksWithCertExpiryintegerno
Heartbeatsintegerno
MaxTimeoutSecintegernoThe loosest timeout actually configured; 0 when Checks is 0.
Membersintegerno
MinIntervalSecintegernoThe tightest interval actually configured across this org's checks; 0 when Checks is 0.
NotificationChannelsintegerno
StatusPageEnabledbooleannoThe org's own current setting, independent of whether the plan allows it.
UsedChannelKindsLabelstringnoHuman-readable, comma-separated list of channel kinds this org has actually created (not which kinds the plan permits).

Result#

One check execution outcome. Deliberately narrower than the store's internal result row - no alert-delivery fields.

PropertyTypeRequiredDescription
errorstringnoEmpty on success.
http_statusintegerno
latency_msintegeryes
monitor_idstringyes
monitor_namestringnoEmpty if the monitor was enrolled with no name.
ran_atstring (date-time)yes
response_samplestringnoLeading characters of what the target sent back, capped at the check's own result_detail_max_chars.
successbooleanyes

ResultsCursor#

PropertyTypeRequiredDescription
afterstring (date-time)yes
after_monitorstringyes