Documentation

Reference for Keel's webhooks, API, and monitor settings

Everything you need to integrate Keel with your stack. No framework lock-in — just HTTP and JSON.

Webhook alerts

When an incident opens, closes, or a certificate is about to expire, Keel POSTs a JSON payload to each subscribed channel.

Events

Payload

Shape targets Discord-style consumers: content mirrors text for easy Slack/Discord ingestion. Plain JSON — works with Slack, Discord, curl, or any HTTP endpoint.

{
  "event": "down",
  "text": "Keel: example.com is DOWN - timeout (https://example.com/)",
  "content": "Keel: example.com is DOWN - timeout (https://example.com/)",
  "data": {
    "monitor": { "id": "…", "name": "example.com", "url": "https://example.com/", "method": "GET", "expectedStatus": 200, "intervalSeconds": 300, "timeoutMs": 10000, "paused": false, "lastStatus": "down", "failCount": 2, "nextCheckAt": 1727892000000, "createdAt": 1727890800000, "sslExpiresAt": null, "sslIssuer": null, "sslWarnedAt": null },
    "incident": { "id": "…", "monitorId": "…", "openedAt": 1727892000000, "closedAt": null, "reason": "timeout" },
    "check": { "monitorId": "…", "ts": 1727892000000, "status": "down", "statusCode": null, "latencyMs": null, "error": "timeout" },
    "sentAt": 1727892000123
  }
}

Delivery semantics

Quick test with curl

curl -X POST "https://your-webhook.example.com" \
  -H "Content-Type: application/json" \
  -d '{"event":"test","text":"Keel: test alert","content":"Keel: test alert","data":{"monitor":{"name":"test"},"incident":null,"check":null,"sentAt":1234567890}}'

Public API

REST endpoints under /api/v1/ for programmatic access to monitors, checks, incidents, and API tokens.

Authentication

  1. Open the console at /app.html and sign in.
  2. Create an API token via the API: POST /api/v1/tokens with a session Bearer token. The token is shown once only — copy it immediately.
  3. Use it as a Bearer token: Authorization: Bearer keel_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Tokens are prefixed with keel_; session tokens never carry this prefix. API tokens cannot create or list other tokens — use the console session for token management.

Endpoints

MethodPathDescription
POST/api/v1/tokensCreate an API token (session auth required)
GET/api/v1/tokensList your API tokens (session auth required)
DELETE/api/v1/tokens/:idRevoke an API token (session auth required)
GET/api/v1/monitorsList all monitors
POST/api/v1/monitorsCreate a monitor
GET/api/v1/monitors/:idGet a single monitor
PATCH/api/v1/monitors/:idUpdate a monitor
DELETE/api/v1/monitors/:idDelete a monitor
GET/api/v1/monitors/:id/checks?limit=50List recent checks for a monitor (default 50, max 200)
GET/api/v1/incidents?open=true&monitor_id=List incidents; filter by open state and/or monitor

Example: list monitors

# Request
curl -H "Authorization: Bearer keel_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
  "https://keelmonitor.com/api/v1/monitors"

# Response
{
  "monitors": [
    { "id": "abc123", "name": "Main site", "url": "https://example.com/", "method": "GET", "expectedStatus": 200, "intervalSeconds": 300, "timeoutMs": 10000, "paused": false, "createdAt": "2026-09-01T12:00:00.000Z" }
  ]
}

Example: create a monitor

# Request
curl -X POST "https://keelmonitor.com/api/v1/monitors" \
  -H "Authorization: Bearer keel_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
  -H "Content-Type: application/json" \
  -d '{"name":"API health","url":"https://api.example.com/health","method":"GET","expectedStatus":200,"intervalSeconds":60,"timeoutMs":5000}'

# Response
{
  "monitor": {
    "id": "xyz789",
    "name": "API health",
    "url": "https://api.example.com/health",
    "method": "GET",
    "expectedStatus": 200,
    "intervalSeconds": 60,
    "timeoutMs": 5000,
    "paused": false,
    "createdAt": 1768465800000
  }
}
    

Monitor knobs

Fields you can set when creating or updating a monitor via the console or API.

FieldTypeDefaultConstraints
methodstringGETGET or HEAD
expectedStatusinteger200100–599 (any valid HTTP status code)
timeoutMsinteger100001000–30000 (milliseconds)
intervalSecondsinteger300 (Free) / 60 (Pro)Positive integer; plan-enforced minimum
pausedbooleanfalseWhen true, checks are skipped

The console enforces the same validation. Free plan minimum interval is 300s (5 minutes); Pro is 60s.

Plan limits

Limits apply at the account level, not per monitor.

FreePro (£5/month)
Monitors350
Check intervalEvery 5 minutes (300s)Every 60 seconds
Webhook channels210
API tokens15
Check history30 days30 days

Pro plan is not yet purchasable — payments are wired to Creem but not live. All accounts currently run on the Free plan.

Certificate expiry monitoring

Keel checks TLS certificates on HTTPS monitors and fires a cert_expiry webhook event when a certificate is about to expire.

  • Warning window: 14 days before expiry
  • Re-warn interval: at most once per 24 hours per monitor
  • Event type: cert_expiry (subscribe in channel settings)
  • Payload includes monitor.sslExpiresAt (ISO timestamp) and monitor.sslIssuer when available
  • Only fires for HTTPS monitors where the certificate expiry can be determined
  • Current Workers runtime provides no certificate access — cert_expiry events do not fire in production today. This is a silent gap, not an error; the code path is in place for when the platform adds cert surface.

Last updated: 3 October 2026.