Create and list monitors and notification channels programmatically — the same operations as the dashboard, scriptable from CI or an agent. Every write runs through the same validation and plan checks as the web app, so an API-created monitor behaves exactly like one made in the UI. API access is included from the Starter plan up.
Authentication
Create a personal access token under Account → API tokens (it's shown once). Send it as a bearer token on every request:
curl -H "Authorization: Bearer cmk_…" \ https://cronheart.com/api/v1/monitors
A missing, invalid, expired, or revoked token returns
401. A token whose plan no longer includes API access returns
402. Requests are rate-limited per token; every response carries
X-RateLimit-Limit, X-RateLimit-Remaining, and
X-RateLimit-Reset.
Create a monitor
POST /api/v1/monitors — returns 201 with a
Location header and the monitor (including its ping_url).
curl -X POST https://cronheart.com/api/v1/monitors \
-H "Authorization: Bearer cmk_…" \
-H "Content-Type: application/json" \
-d '{
"name": "nightly-backup",
"schedule_kind": "interval",
"schedule_expr": "3600",
"tz": "UTC",
"grace_seconds": 60,
"channel_ids": []
}'
schedule_kind is cron (a 5-field expression),
interval (integer seconds, 30–31,622,400), or simple
(e.g. "daily at 03:00"). channel_ids is optional —
leave it empty to fan out to every verified channel in your project.
List & fetch monitors
GET /api/v1/monitors?limit=50&offset=0 returns
{ "data": [...], "total": N }.
GET /api/v1/monitors/{uuid} returns a single monitor (or
404 if it isn't yours).
Create a channel
POST /api/v1/channels — the body is keyed by kind:
{"kind":"telegram","label":"ops","chat_id":"-100123456789"}
{"kind":"slack","label":"alerts","webhook_url":"https://hooks.slack.com/services/…"}
{"kind":"webhook","label":"my-hook","webhook_url":"https://example.com/hook","secret":"<16+ chars>"}
{"kind":"email","label":"on-call","address":"ops@example.com"}
Channels are created unverified. An email channel triggers a verification
link to the address; the others verify on their first successful test send
from the dashboard. Webhook URLs must be public HTTPS endpoints — loopback
and private addresses are rejected. GET /api/v1/channels lists
them (secrets are redacted).
Errors
Errors are application/problem+json (RFC 7807):
{"type":"about:blank","title":"Unprocessable Entity","status":422,
"detail":"One or more fields are invalid.","errors":{"name":"This value is too short."}}
Idempotency: a retried
POST /api/v1/monitors creates a second monitor — the endpoint is
not idempotent in v1. Guard retries on your side.
PHP integration
If you're on Symfony or Laravel, the
cron-monitor/php-sdk wraps the
ping endpoint with first-class scheduler integrations —
composer require cron-monitor/php-sdk.