Docs / Account & API / The REST API

The REST API

Automate CompleteStatus with the JSON REST API — create API keys and scopes, manage monitors and incidents, and mute alerts during deploys from CI.
Last updated · 5 min read

CompleteStatus's REST API lets you manage monitors, read and acknowledge incidents, and mute alerts during deploys — everything you need to wire monitoring into CI/CD.

Note: API access requires the Business plan or above. API keys can only be created once your organization is on a plan that includes API access — the "Create key" action is unavailable on Free and Pro.

Creating an API key

  1. Go to /settings/api-keys (you must be an organization owner or admin).
  2. Give the key a name (e.g. "CI deploy"), pick its scopes, and optionally set an expiry in days.
  3. Click create. The plaintext token is shown exactly once — copy it now. CompleteStatus stores only a SHA-256 hash, so the token can never be displayed again.

Tokens look like wt_a1b2c3d4_… (a wt_ prefix, 8 random characters, an underscore, then 40 random characters). Revoke a key at any time from the same page; the list shows each key's scopes, expiry and last-used time.

Scopes

Scope Grants
monitors:read List monitors, read one, read its check results
monitors:write Create, update, delete, pause, resume, trigger checks
incidents:read List and read incidents
incidents:write Acknowledge and resolve incidents
maintenance:write Create and end maintenance windows
hooks:manage Subscribe, list and delete automation hooks
mcp Use the MCP server

Grant the minimum a job needs — a deploy pipeline usually only needs maintenance:write.

Authentication

Send the token on every request, either way works:

curl -H "Authorization: Bearer wt_xxxxxxxx_..." https://completestatus.com/api/v1/ping
# or
curl -H "X-Api-Key: wt_xxxxxxxx_..." https://completestatus.com/api/v1/ping

All data is scoped to the key's organization — a key can never see another tenant's data. Verify a key with GET /api/v1/ping (alias /api/v1/me):

{
  "authenticated": true,
  "organization": { "id": 1, "name": "Acme Inc.", "plan": "business" },
  "api_key": { "id": 3, "name": "CI deploy", "prefix": "wt_a1b2c3d4",
               "scopes": ["maintenance:write"], "expires_at": null }
}

Rate limits and errors

Each key may make 120 requests per minute; beyond that you get 429 with a Retry-After header. Other errors: 401 (missing/invalid/expired key), 403 (plan doesn't include API access, or the key lacks a required scope), 404 (resource not in your organization), 422 (validation or plan-limit failure, e.g. monitor cap reached or interval below your plan's minimum).

Monitors

GET    /api/v1/monitors                     ?type=&status=&project_id=&per_page=   (paginated, default 25)
POST   /api/v1/monitors
GET    /api/v1/monitors/{id}
PATCH  /api/v1/monitors/{id}
DELETE /api/v1/monitors/{id}                → 204
POST   /api/v1/monitors/{id}/check          → 202, queues a check now
POST   /api/v1/monitors/{id}/pause
POST   /api/v1/monitors/{id}/resume
GET    /api/v1/monitors/{id}/results        ?limit= (1–200, default 50)

Create a monitor (requires monitors:write):

curl -X POST https://completestatus.com/api/v1/monitors \
  -H "Authorization: Bearer $WT_TOKEN" -H "Content-Type: application/json" \
  -d '{
    "project_id": 1,
    "type": "uptime",
    "name": "Marketing site",
    "target": "https://example.com",
    "interval_sec": 300,
    "ownership_attested": true
  }'

interval_sec must be one of 60, 300, 900, 1800, 3600, 21600, 86400 (and no faster than your plan allows). ownership_attested: true is required on create — you must own or be authorized to monitor the target. type cannot be changed after creation. Response (201):

{
  "data": {
    "id": 42, "project_id": 1, "type": "uptime",
    "name": "Marketing site", "target": "https://example.com",
    "status": "pending", "interval_sec": 300,
    "consecutive_failures": 0, "consecutive_successes": 0,
    "last_checked_at": null, "next_check_at": "2026-07-04T10:00:00+00:00",
    "ownership_attested_at": "2026-07-04T10:00:00+00:00",
    "created_at": "2026-07-04T10:00:00+00:00", "updated_at": "2026-07-04T10:00:00+00:00"
  }
}

Incidents

GET  /api/v1/incidents            ?status=open|acknowledged|resolved|all (default open) &monitor_id=&per_page=
GET  /api/v1/incidents/{id}
POST /api/v1/incidents/{id}/ack
POST /api/v1/incidents/{id}/resolve

Acknowledge from a chatops script (requires incidents:write):

curl -X POST https://completestatus.com/api/v1/incidents/17/ack \
  -H "Authorization: Bearer $WT_TOKEN"

Acknowledgements and resolutions are stamped on the incident timeline as api:<key name>.

Maintenance windows (mute during deploys)

POST   /api/v1/maintenance-windows
DELETE /api/v1/maintenance-windows/{id}     → 204

Mute a project's alerts for the length of a deploy (requires maintenance:write):

curl -X POST https://completestatus.com/api/v1/maintenance-windows \
  -H "Authorization: Bearer $WT_TOKEN" -H "Content-Type: application/json" \
  -d '{"project_id": 1, "duration_min": 15, "reason": "Deploying v2"}'

duration_min defaults to 30 and is capped at 1440 (24 h). Pass monitor_ids to mute only specific monitors; omit it to cover the whole project. Incidents are still recorded during a window — only the notifications are suppressed. Delete the window to end the mute early.

Client examples

The API is plain JSON over HTTPS, so any HTTP client works — the examples below use each language's most common one. All four snippets do the same thing: authenticate with the Authorization: Bearer header (the X-Api-Key header shown under Authentication above works identically), list monitors, create a heartbeat monitor, then pause and resume it. For heartbeat monitors, interval_sec is the expected ping interval and config.grace_sec the grace period in seconds; the API response doesn't include the ping token, so open the monitor in the UI to copy its ping URL. Keep the token itself in an environment variable, never in code.

PHP

Plain PHP with the curl extension (in a Laravel app, Http::withToken($token)->get(...) does the same in one line). The helper below throws on any HTTP error so a failing call can't be silently ignored.

<?php

function wt(string $method, string $path, ?array $body = null): array
{
    $ch = curl_init('https://completestatus.com/api/v1'.$path);

    $headers = [
        'Authorization: Bearer '.getenv('WT_TOKEN'),
        'Accept: application/json',
    ];

    if ($body !== null) {
        $headers[] = 'Content-Type: application/json';
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
    }

    curl_setopt_array($ch, [
        CURLOPT_CUSTOMREQUEST => $method,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 15,
        CURLOPT_HTTPHEADER => $headers,
    ]);

    $json = curl_exec($ch);
    $status = (int) curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    curl_close($ch);

    if ($json === false || $status >= 400) {
        throw new RuntimeException("CompleteStatus API error: HTTP {$status} {$json}");
    }

    return json_decode($json, true) ?? [];
}

// List monitors (requires monitors:read)
$monitors = wt('GET', '/monitors')['data'];

// Create a heartbeat monitor (requires monitors:write)
$monitor = wt('POST', '/monitors', [
    'project_id' => 1,
    'type' => 'heartbeat',
    'name' => 'Nightly backup',
    'target' => 'nightly-backup',
    'interval_sec' => 86400,
    'config' => ['grace_sec' => 1200],
    'ownership_attested' => true,
])['data'];

// Pause and resume
wt('POST', "/monitors/{$monitor['id']}/pause");
wt('POST', "/monitors/{$monitor['id']}/resume");

Python

Using requests with a Session, so the auth header is set once. raise_for_status() turns 4xx/5xx responses into exceptions.

import os
import requests

BASE = "https://completestatus.com/api/v1"

session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['WT_TOKEN']}"

def wt(method, path, json=None):
    resp = session.request(method, BASE + path, json=json, timeout=15)
    resp.raise_for_status()
    return resp.json() if resp.content else {}

# List monitors (requires monitors:read)
monitors = wt("GET", "/monitors")["data"]

# Create a heartbeat monitor (requires monitors:write)
monitor = wt("POST", "/monitors", json={
    "project_id": 1,
    "type": "heartbeat",
    "name": "Nightly backup",
    "target": "nightly-backup",
    "interval_sec": 86400,
    "config": {"grace_sec": 1200},
    "ownership_attested": True,
})["data"]

# Pause and resume
wt("POST", f"/monitors/{monitor['id']}/pause")
wt("POST", f"/monitors/{monitor['id']}/resume")

Node.js

Built-in fetch (Node 18+), no dependencies. The wrapper rejects on non-2xx responses so errors surface as exceptions.

const BASE = "https://completestatus.com/api/v1";

async function wt(method, path, body) {
  const res = await fetch(BASE + path, {
    method,
    headers: {
      Authorization: `Bearer ${process.env.WT_TOKEN}`,
      Accept: "application/json",
      ...(body && { "Content-Type": "application/json" }),
    },
    body: body ? JSON.stringify(body) : undefined,
    signal: AbortSignal.timeout(15_000),
  });

  if (!res.ok) {
    throw new Error(`CompleteStatus API error: HTTP ${res.status} ${await res.text()}`);
  }

  return res.status === 204 ? null : res.json();
}

// List monitors (requires monitors:read)
const { data: monitors } = await wt("GET", "/monitors");

// Create a heartbeat monitor (requires monitors:write)
const { data: monitor } = await wt("POST", "/monitors", {
  project_id: 1,
  type: "heartbeat",
  name: "Nightly backup",
  target: "nightly-backup",
  interval_sec: 86400,
  config: { grace_sec: 1200 },
  ownership_attested: true,
});

// Pause and resume
await wt("POST", `/monitors/${monitor.id}/pause`);
await wt("POST", `/monitors/${monitor.id}/resume`);

Related guides

Ready to try it?
10 monitors, security grading and email-authentication checks on the free tier — commercial use allowed.
Start free Run a free check
Uptime is table stakes. We watch the rest — security headers, email authentication, certs and DNS, with the fix attached.
Start free
Company
© 2026 CompleteStatus. All rights reserved. CompleteStatus — operated in the United States · support@completestatus.com