Skip to content

Errors

The /api/v1/* surface uses a flat envelope:

{
"error": "url is required"
}

The error field is always a single human-readable string for these endpoints. There is no code, message, or nested object — clients should match on the HTTP status and (where useful) the literal error string.

There are two documented exceptions:

  • The /oauth2/* endpoints (/oauth2/register, /oauth2/token, /oauth2/revoke) follow RFC 7591 / RFC 6749 and return {"error":"<machine_code>", "error_description":"<message>"} (e.g. {"error":"invalid_client_metadata","error_description":"redirect_uris is required"}).
  • The monitor-test endpoint’s kill-switch 503 carries {error, message} — see 503 Kill-switch below.

A representative — not exhaustive — list of the literal error strings the API returns today, grouped by HTTP status. New endpoints add new strings; pin clients on the HTTP status first and only fall back to literal-string matching where the wording is part of an established contract (validator output, rate-limit hints).

Message When
name is required Create monitor body missing name
url is required Create monitor body missing url
interval_sec must be at least 60 interval_sec below the 60-second minimum
expected_status must be a single integer between 100 and 599 expected_status non-integer or out of range
type must be one of: down, flapping Invalid incident type query value
limit must be between 1 and 100 Pagination limit out of range
invalid monitor ID Path parameter is non-numeric on monitor endpoints (e.g. /monitors/abc)
invalid incident ID Path parameter is non-numeric on /api/v1/incidents/{id}
invalid key ID Path parameter is non-numeric on key endpoints
invalid request body Body is not valid JSON
label must be at least 3 characters Key label below the minimum length
label must be 100 characters or less Key label above the maximum length
missing code OAuth callback missing code query parameter
invalid or expired code OAuth callback code rejected by GitHub
url rejected: ip <addr> is in a blocked range Target URL resolves to a blocked IP range (SSRF guard)
Message When
missing authorization No Authorization header
invalid credentials API key not recognised or revoked
Message When
account suspended; contact support Account suspended (e.g. by the port-scan heuristic). Returned on /api/v1/*.
forbidden Admin-only route hit by a non-admin caller. Opaque on purpose.
Message When
monitor not found Monitor ID is numeric but does not exist on this account
incident not found Incident ID is numeric but does not exist on this account
key not found API key ID does not exist on this account
Message When
cannot delete last key Tried to delete the only remaining API key
key limit reached Account already at the 10-key cap

Rate-limit responses carry a route-specific message in the flat envelope plus a Retry-After header (seconds). Examples:

Limiter Message
Account-wide /api/v1/* cap account rate limit exceeded; retry in <N>s
Monitor-creation cap monitor creation rate limit exceeded; retry in <N>s
Per-monitor manual-test minimum interval too many tests for this monitor; retry in <N>s
Per-domain cross-account test cap domain rate limit exceeded; retry in <N>s

Don’t pin clients on a single literal string; honour Retry-After and treat any 429 as transient.

The body is {"error":"internal error"}. Retry with exponential backoff.

503 Kill-switch active test-monitor endpoint only

Section titled “503 Kill-switch active test-monitor endpoint only”

POST /api/v1/monitors/{id}/test is the one endpoint where the 503 body deviates from the flat envelope. When the dispatcher’s global kill switch is engaged, the test endpoint returns:

{
"error": "kill_switch_active",
"message": "check dispatches temporarily disabled"
}

with a Retry-After header. Both error and message are present. Other 503s (e.g. kill switch not configured on admin endpoints) keep the flat {"error":"..."} shape.

{
"error": "url rejected: ip 10.0.0.5 is in a blocked range"
}

Private IPs (10.x, 172.16-31.x, 192.168.x), loopback, and link-local addresses are blocked. See Security for details.

{
"error": "expected_status must be a single integer between 100 and 599"
}

The body is the literal validator output — useful to surface back to the user.