API Reference
Opsentry's HTTP API lets you read status, create incidents, and schedule maintenance from your own systems — CI pipelines, on-call runbooks, internal dashboards. Every request is scoped to a single tenant via an API token.
Overview
- Base URL:
https://opsentry.io/api/v1— replace with your own host if self-hosted. - Content type: requests and responses are JSON (
application/json). - Authentication: bearer token in the
Authorizationheader — see below. - Versioning: the URL path is versioned (
/api/v1/…). Breaking changes go to a new version; existing versions stay supported for at least 12 months after deprecation notice.
Authentication
The API is authenticated with tenant-scoped API tokens. Tokens are bound to a single tenant and a set of scopes that limit what they can do. Sessions (the browser cookie used by the admin UI) cannot be used for API calls — token and session auth are deliberately separate.
Creating tokens
- Sign in and go to API tokens in the admin sidebar.
- Click Create token, give it a name (e.g.
CI pipeline), pick the scopes it needs, and optionally an expiry. - Copy the token immediately. It looks like
ops_3f8b…and is shown only once — we store a hash, not the value. - Treat it like a password: never commit it to source control, never paste it into shared chat. If a token leaks, revoke it from the same page.
Sending the header
Include the token in the Authorization header on every request:
Authorization: Bearer ops_3f8b1c2d…
Example with curl:
curl https://opsentry.io/api/v1/incidents/active \
-H "Authorization: Bearer ops_3f8b1c2d…"JavaScript (fetch):
const res = await fetch("https://opsentry.io/api/v1/incidents/active", { headers: { "Authorization": `Bearer ${token}` } }); const data = await res.json();
Python (requests):
import requests r = requests.get( "https://opsentry.io/api/v1/incidents/active", headers={"Authorization": f"Bearer {token}"}, ) data = r.json()
Go:
req, _ := http.NewRequest("GET", "https://opsentry.io/api/v1/incidents/active", nil) req.Header.Set("Authorization", "Bearer "+token) resp, err := http.DefaultClient.Do(req)
Scopes
Each token carries one or more scopes. The API rejects requests with 403 Forbidden if the token lacks the scope required by the endpoint.
| Scope | What it allows |
|---|---|
read:status | Read services, incidents, maintenance, uptime statistics. |
write:incidents | Create, update, and resolve incidents. |
write:maintenance | Schedule and update maintenance windows. |
Pick the minimum set the integration needs. A CI pipeline that only declares failed deploys as incidents should hold write:incidents — never read or admin scopes.
Errors
The API uses standard HTTP status codes and returns a JSON body with a short error message:
{
"error": "missing required scope: write:incidents"
}| Status | Meaning |
|---|---|
200 / 201 | Success. |
400 | Malformed request — bad JSON, missing required field. |
401 | Missing Authorization header or unknown/expired token. |
403 | Token is valid but lacks the required scope. |
404 | Resource doesn't exist or doesn't belong to the token's tenant. We don't distinguish — leaks no information about other tenants. |
429 | Rate-limited. See Rate limits. |
5xx | Server problem. Includes a request_id — quote it when reporting. |
Rate limits
The API is rate-limited per token's source IP. Limits are conservative by default; contact support if your integration needs more.
When you hit the limit you get 429 Too Many Requests. Back off and retry — don't tight-loop on 429s.
Endpoints
All paths are relative to https://opsentry.io/api/v1. Responses are JSON. Errors follow the standard shape. Pagination uses limit + offset query parameters where applicable.
Health
Four health endpoints exist, each tuned for a different consumer:
| Endpoint | Auth | Use case |
|---|---|---|
GET /health | none | Load balancer probe. Cheapest possible response — plain text OK, no DB hit, no JSON parsing. Use this from AWS Target Groups, HAProxy, Cloudflare, Nginx upstream checks, etc. |
GET /health/live | none | Kubernetes-style liveness probe. Returns JSON; equivalent semantics to /health but in the shape K8s expects. |
GET /health/ready | none | Readiness probe — 200 only when DB, Redis, and the notification outbox are healthy. Returns 503 with per-check details otherwise. Use from orchestrators so traffic drains during dependency outages. |
GET /api/v1/health | token | Token-verifying ping. Returns the tenant ID + token prefix + scopes so integrations can confirm "is my key valid and what does it carry?" in one round trip. |
/health as the primary check (it's cheap and always 200 if the process can serve traffic). If you also want the LB to drain a replica when its DB connection is failing, use /health/ready as a secondary deep check on a longer interval.
Load-balancer probe. Plain text OK, no JSON, no dependency check. Sub-millisecond.
Unauthenticated.
curl -i https://opsentry.io/health HTTP/1.1 200 OK Cache-Control: no-store Content-Type: text/plain; charset=utf-8 OK
Liveness — 200 with JSON {"status":"ok"} while the process is running.
Unauthenticated.
curl https://opsentry.io/health/live
Readiness — 200 when all dependency checks pass, 503 with per-check status when any fail.
Unauthenticated.
{
"status": "ready",
"checks": {
"postgres": { "status": "ok", "duration_ms": 2400000 },
"redis": { "status": "ok", "duration_ms": 810000 },
"outbox": { "status": "ok", "duration_ms": 5100000 }
}
}The outbox check fails when the notification backlog exceeds 1000 pending rows or the oldest pending row is older than 5 minutes — at that point your orchestrator should drain the replica.
Authenticated ping. Returns 200 with the token's metadata. Handy for CI/CD pipelines: a single call confirms the API is reachable and the token still works.
Requires a valid token; no specific scope required.
curl https://opsentry.io/api/v1/health \
-H "Authorization: Bearer ops_3f8b1c2d…"{
"ok": true,
"tenant_id": 17,
"token_prefix": "3f8b1c2d",
"scopes": ["read:status", "write:incidents"]
}A 401 response means the token is invalid, expired, or revoked — even before you bother making real requests.
Services
List services for the tenant. Query params: status, visibility, search, limit (default 100, max 200), offset.
Requires scope read:status
curl https://opsentry.io/api/v1/services?status=degraded_performance \
-H "Authorization: Bearer ops_…"{
"services": [{
"id": 42,
"slug": "main-api",
"name": "Main API",
"current_status": "degraded_performance",
"visibility": "public",
"tags": ["prod", "customer-facing"]
}],
"total": 1, "limit": 100, "offset": 0
}Fetch one service by ID. 404 if the ID doesn't belong to your tenant.
Requires scope read:status
Returns 24h / 7d / 30d uptime percentages for the service.
Requires scope read:status
{
"service_id": 42,
"uptime_24h": 99.97,
"uptime_7d": 99.94,
"uptime_30d": 99.91
}Incidents
Paginated incident list. Query params: status, severity, limit (default 50, max 200), offset.
Requires scope read:status
Returns only non-resolved incidents — shortcut for the dashboards/alerters that don't want to paginate.
Requires scope read:status
Fetch one incident including update history and affected services.
Requires scope read:status
Create a new incident. The tenant is taken from the token — any tenant_id in the body is ignored.
Requires scope write:incidents
curl -X POST https://opsentry.io/api/v1/incidents \ -H "Authorization: Bearer ops_…" \ -H "Content-Type: application/json" \ -d '{ "title": "Deploy failed — rolling back", "description": "Health checks failing post-deploy", "severity": "degraded", "visibility": "public", "service_ids": [42] }'
| Field | Type | Notes |
|---|---|---|
title | string | Required. Public headline. |
description | string | Optional context for the timeline. |
severity | string | One of informational, degraded, partial_outage, major_outage, critical. |
visibility | string | public (default) or internal. |
service_ids | integer[] | Services to link as affected. |
Returns 201 Created with the new incident.
Mutate an incident. All fields optional — only the ones present in the body are changed.
Requires scope write:incidents
curl -X PATCH https://opsentry.io/api/v1/incidents/2041 \ -H "Authorization: Bearer ops_…" \ -H "Content-Type: application/json" \ -d '{"status": "monitoring", "severity": "informational"}'
Status transitions are validated server-side; resolved ↔ investigating is allowed (re-open). See the admin UI for the full state machine.
Append a status-update message. The message also transitions the incident's status.
Requires scope write:incidents
curl -X POST https://opsentry.io/api/v1/incidents/2041/updates \ -H "Authorization: Bearer ops_…" \ -H "Content-Type: application/json" \ -d '{ "status": "monitoring", "message": "Failover complete. Watching for 15 minutes.", "is_public": true }'
Mark the incident resolved with optional root cause + postmortem.
Requires scope write:incidents
curl -X POST https://opsentry.io/api/v1/incidents/2041/resolve \ -H "Authorization: Bearer ops_…" \ -H "Content-Type: application/json" \ -d '{ "root_cause": "Stale DNS cache after failover.", "postmortem": "https://internal.notes/inc-2041" }'
Maintenance
Paginated list of maintenance windows. Query params: status, limit, offset.
Requires scope read:status
Future scheduled windows only. No pagination — typical tenant has few.
Requires scope read:status
Fetch one maintenance window including linked services.
Requires scope read:status
Schedule a maintenance window. Times are RFC 3339 timestamps in UTC.
Requires scope write:maintenance
curl -X POST https://opsentry.io/api/v1/maintenance \ -H "Authorization: Bearer ops_…" \ -H "Content-Type: application/json" \ -d '{ "title": "Database failover drill", "description": "Customer Portal will be read-only for ~6 minutes.", "scheduled_start": "2026-06-01T02:00:00Z", "scheduled_end": "2026-06-01T04:00:00Z", "visibility": "public", "service_ids": [17], "exclude_from_sla": true, "suppress_alerts": true }'
:action is one of start, complete, cancel. Transition rules are validated server-side — e.g. complete only works on active windows.
Requires scope write:maintenance
curl -X POST https://opsentry.io/api/v1/maintenance/812/start \
-H "Authorization: Bearer ops_…"Uptime
Per-service uptime statistics are exposed under /services/:id/uptime (see above) to keep all service-scoped data under a single resource. Tenant-wide rollups are coming.
Changelog
- v1 — health endpoints (load-balancer probe, K8s liveness + readiness, token-verifying ping); services (list/get/uptime); incidents (list/get/create/update/add-update/resolve); maintenance (list/upcoming/get/create/transitions); token auth with three scopes.