API reference
Base URL https://sysops247.io. Everything is under /api/v1, takes and returns JSON, and needs an API key.
Prefer to try before you read? The API console lets you paste a key and send any of these requests from your browser.
This is the same API the dashboard uses. There is no reduced “public API” — if you can do it in the UI, you can do it here, under the same permissions and the same plan limits.
Conventions
Collections
{
"data": [ ... ],
"pagination": { "total": 132, "limit": 50, "offset": 0 }
}?limit= (default 50, max 200) and ?offset=. Most list endpoints also take ?search=.
Errors
{
"error": {
"code": "validation",
"message": "monitor limit reached for the free plan",
"fields": { "target": "must be a valid URL" },
"request_id": "01HQ..."
}
}Quote the request_id if you ever need to ask us what happened — it is how we find your request in the logs.
| Field | Type | Notes |
|---|---|---|
| 400 / 422 | validation | The request was malformed, or it hit a plan limit. |
| 401 | unauthorized | Missing, invalid, revoked or expired credential. |
| 403 | forbidden | Authenticated, but your role does not allow it. |
| 404 | not_found | No such resource — or it belongs to another organization. The two are indistinguishable by design. |
| 429 | rate_limited | Slow down; honour Retry-After. |
Tenancy
Every resource is scoped to the organization the credential belongs to. Another tenant's monitor is not found, never “forbidden” — telling you it exists would be the leak.
Monitors
search, status, type, project_id.Create a monitor
curl -X POST https://sysops247.io/api/v1/monitors \
-H "Authorization: Bearer $BEACON_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"project_id": "b1f2...",
"name": "api",
"type": "https",
"target": "https://api.example.com",
"interval_seconds": 60,
"settings": { "valid_status_codes": [200], "body_keyword": "ok" }
}'| Field | Type | Notes |
|---|---|---|
| project_idrequired | uuid | Which project it belongs to. |
| namerequired | string | How you will recognise it. |
| typerequired | string | See monitor types. |
| target | string | Required for everything except heartbeat. |
| interval_seconds | number | How often to check. Floor depends on your plan. |
| timeout_seconds | number | How long to wait before calling it a failure. |
| grace_seconds | number | Heartbeat only: slack before a missed ping alerts. |
| enabled | boolean | Defaults to true. |
| public | boolean | Publish on your status page. Defaults to false — nothing is exposed by accident. |
| settings | object | Per-type options; see monitor types. |
Sync (declarative)
The endpoint to use from CI. It takes the monitors you want and works out the difference, so running it on every push is safe. Full guide →
Projects
{ "name", "description?", "environment?" }Alerts
?severity=critical.curl -s "https://sysops247.io/api/v1/alerts" \
-H "Authorization: Bearer $BEACON_API_KEY" \
| jq -r '.data[] | "\(.severity)\t\(.monitor_name)\t\(.target)"'Notification channels
telegram, slack, email, webhook.Maintenance windows
Prefer a maintenance window to pausing a monitor during a deploy. Alerts stay quiet, but the probes keep running — so you still have the history of what happened while you were working.
Status page
Billing & diagnosis
Operational
Rate limits
Limits are per organization for authenticated calls and per address for anything anonymous. A refusal is a 429 with a Retry-After header saying how long to wait — respect it and you will not be refused twice.
Normal use does not come close. If you are hitting them, you are probably polling where you could be reacting: a webhook channel tells you the moment something breaks, without asking every few seconds.