⌁ Checkpulse

REST API

The API manages one project's checks, incidents, and alert channels. The machine-readable description is the OpenAPI 3.1 document. AI agents can use the same operations over MCP.

Authentication

Create a key under Project settings → API keys and send it as a bearer token:

curl https://checkpulse.foo/api/v1 -H "Authorization: Bearer cpk_…"

X-Api-Key: cpk_… also works. Keys are read-only or read-write and belong to a single project. GET /api/v1 describes the project, the key's scope, limits, and usage, and is a good first call.

Requests

  • Send JSON with Content-Type: application/json. Unknown fields are rejected, so typos surface as errors.
  • {check} in a path is a numeric ID or a slug.
  • Durations (period, grace, duration) accept seconds or strings such as "90s", "5m", "1h", "1d".
  • tags and failure_keywords accept a string or an array of strings.
  • channels accepts "*" (every channel) or an array of channel IDs.
  • Times are RFC 3339 in UTC.

Errors

Errors use RFC 9457 problem details with Content-Type: application/problem+json:

{
  "type": "about:blank",
  "title": "Bad Request",
  "status": 400,
  "code": "invalid_period",
  "field": "period",
  "detail": "Interval must be at least 1m on this account."
}

code is stable for programs; detail and, when present, hint explain what to do. Common codes:

CodeStatusMeaning
unauthorized401Missing, invalid, expired, or revoked key.
read_only_key403The key cannot make changes.
limit_reached403An account limit (checks, channels, keys) is reached.
check_not_found, not_found404No such resource in this project.
slug_taken409Another check uses that slug.
invalid_<field>, invalid_json400The request is not valid; see field and detail.
idempotency_key_reused422The Idempotency-Key was used for a different request.
rate_limited429Too many requests; wait for Retry-After seconds.

Idempotency

Send Idempotency-Key: <unique value> with any write. Retrying with the same key within 24 hours returns the stored response, including error responses with their application/problem+json type, marked Idempotent-Replayed: true instead of repeating the change. A retry that arrives while the first request is still running gets 409 request_in_progress; a request that never finished releases its key after two minutes. PUT /api/v1/checks/{slug} is idempotent by design.

Rate limits

Each key may make 60 requests a minute on free accounts. Responses include RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset; a 429 includes Retry-After.

Pagination

Lists of pings and audit entries return newest first. Pass the next_before value (pings) or the last entry's id (audit) as before to fetch older items.

Endpoints

Method and pathScopePurpose
GET /api/v1readProject, key, counts, limits, usage, and links.
GET /api/v1/checksreadList checks. Filters: status, tag, slug, q.
POST /api/v1/checkswriteCreate a check.
GET /api/v1/checks/{check}readGet a check.
PUT /api/v1/checks/{slug}writeCreate or update by slug (201 created, 200 updated).
PATCH /api/v1/checks/{check}writeChange some fields.
DELETE /api/v1/checks/{check}writeDelete; restorable for 7 days.
POST /api/v1/checks/{id}/restorewriteRestore a deleted check.
POST /api/v1/checks/{check}/pausewritePause.
POST /api/v1/checks/{check}/resumewriteResume.
POST /api/v1/checks/{check}/silencewriteHold alerts: {"duration": "2h", "reason": "…"}.
DELETE /api/v1/checks/{check}/silencewriteEnd a silence.
POST /api/v1/checks/{check}/rotate-tokenwriteIssue a new ping URL.
GET /api/v1/checks/{check}/pingsreadRecent pings. limit, before, include_system.
GET /api/v1/incidentsreadIncidents. status (open, closed, all), check.
GET /api/v1/incidents/{id}readOne incident with notes.
POST /api/v1/incidents/{id}/acknowledgewriteAcknowledge, optionally with {"note": "…"}.
POST /api/v1/incidents/{id}/noteswriteAdd {"note": "…"}.
GET /api/v1/channelsreadAlert channels.
POST /api/v1/channelswriteAdd a channel (see Alert channels).
PATCH /api/v1/channels/{id}writeenabled, notify_down, notify_up, repeat_minutes.
DELETE /api/v1/channels/{id}writeRemove a channel.
POST /api/v1/channels/{id}/testwriteSend a test alert.
GET /api/v1/auditreadRecent changes and who made them.
GET /api/v1/badgesreadStatus badge URLs.
GET /api/v1/metricsreadPrometheus metrics.
GET /api/v1/openapi.jsonnoneThe OpenAPI document.

Check fields

FieldNotes
name, slugThe slug is unique in the project, uses lowercase letters, digits, and hyphens, and must contain a letter.
description, runbookThe runbook is Markdown included in alerts; write it for whoever responds, people or agents.
tagsUsed for filtering, badges, and metrics.
kindinterval (with period) or cron (with cron and timezone).
graceSee Schedules.
methodsany or post.
manual_resumeWhen true, pings do not resume a paused check.
failure_keywordsOutput containing any of these fails a success ping.
channelsChannels that receive this check's alerts.

Responses add status, last_ping, next_due, started_at, last_duration_seconds, silenced_until, open_incident, and, for read-write keys only, the secret ping_url and slug_ping_url.

Examples

Create or update a cron check:

curl -X PUT https://checkpulse.foo/api/v1/checks/db-backup \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"name": "DB backup", "kind": "cron", "cron": "0 3 * * *", "timezone": "Europe/Paris", "grace": "30m", "runbook": "Check free disk space on db-1, then rerun."}'

List checks that are down:

curl "https://checkpulse.foo/api/v1/checks?status=down" -H "Authorization: Bearer $KEY"

Acknowledge an incident:

curl -X POST https://checkpulse.foo/api/v1/incidents/42/acknowledge \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"note": "Disk full on db-1; clearing old WAL files."}'

View as Markdown