docs(api): add public API documentation site (Astro Starlight)
## Changes - Add narrative API documentation in docs/api-content/ (introduction, quickstart, authentication, conventions, errors, rate limiting) - Scaffold Astro Starlight site at docs/api/ that consumes prose from the sibling api-content folder and auto-generates the endpoint reference from server/openapi.json via starlight-openapi - Wire Checkmate brand color (#13715B) into Starlight via custom CSS - Add vercel.json so the docs site deploys cleanly as an Astro project with root directory docs/api/ ## Benefits - Public API docs ready to publish at checkmate.so/docs - Endpoint reference stays in sync with the OpenAPI spec automatically - Zero third-party SaaS dependency: site is fully self-hosted on Vercel - Contributors edit plain markdown; no Astro knowledge needed for prose
This commit is contained in:
1 parent
9027707c92
commit
9aa2594df7
16 files changed
+991
No files matched your search
@@ -0,0 +1,114 @@
|
||||
# Checkmate API documentation
|
||||
|
||||
Public-facing API documentation that powers **https://checkmate.so/docs**.
|
||||
|
||||
The site is built with [Astro Starlight](https://starlight.astro.build/) — a static-site generator that produces a fast, modern docs site with zero JavaScript by default. Starlight is open source and self-hostable; there is no third-party account required to publish.
|
||||
|
||||
## Repository layout
|
||||
|
||||
```
|
||||
docs/
|
||||
├── api-content/ ← markdown prose (this folder — edit here)
|
||||
│ ├── introduction.md
|
||||
│ ├── quickstart.md
|
||||
│ ├── authentication.md
|
||||
│ ├── conventions.md
|
||||
│ ├── errors.md
|
||||
│ ├── rate-limiting.md
|
||||
│ └── api-reference/
|
||||
│ └── overview.md
|
||||
└── api/ ← Astro Starlight project (build tool — leave alone unless changing the site)
|
||||
├── package.json
|
||||
├── astro.config.mjs
|
||||
├── src/content.config.ts
|
||||
└── src/styles/checkmate.css
|
||||
```
|
||||
|
||||
**Contributors editing prose only need to touch `docs/api-content/`**. Astro's content loader picks up the markdown automatically.
|
||||
|
||||
The endpoint reference (every URL + schema) is **auto-generated from [`server/openapi.json`](../../server/openapi.json)** via the `starlight-openapi` plugin. Adding or changing an endpoint means updating that JSON; the public docs pick it up on the next Vercel build.
|
||||
|
||||
## Local preview
|
||||
|
||||
```bash
|
||||
cd docs/api
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Visit http://localhost:4321/docs. Hot-reload works for markdown changes in `docs/api-content/`.
|
||||
|
||||
To produce the static site:
|
||||
|
||||
```bash
|
||||
npm run build # outputs docs/api/dist/
|
||||
npm run preview # serve the built output locally
|
||||
```
|
||||
|
||||
## Deploying to checkmate.so/docs
|
||||
|
||||
Astro Starlight builds a static site. Deploy on Vercel and reverse-proxy `checkmate.so/docs/*` to it.
|
||||
|
||||
### One-time Vercel setup
|
||||
|
||||
1. Go to https://vercel.com → New Project → import the `bluewave-labs/Checkmate` GitHub repo.
|
||||
2. **Root Directory**: set to `docs/api`.
|
||||
3. **Framework Preset**: Vercel auto-detects Astro from `vercel.json`.
|
||||
4. **Build & Output**: leave defaults (`npm run build` → `dist/`).
|
||||
5. **Production Branch**: `develop`.
|
||||
6. Deploy. Vercel returns a URL like `checkmate-docs.vercel.app`.
|
||||
7. Verify the site renders correctly.
|
||||
|
||||
The deployment auto-rebuilds on every push to `develop` and on every PR (as a preview deploy).
|
||||
|
||||
### Reverse-proxy /docs to Vercel
|
||||
|
||||
In whichever edge proxy fronts `checkmate.so`, route `/docs/*` to the Vercel deployment.
|
||||
|
||||
**Cloudflare Workers:**
|
||||
|
||||
```javascript
|
||||
addEventListener("fetch", (event) => {
|
||||
const url = new URL(event.request.url);
|
||||
if (url.pathname === "/docs" || url.pathname.startsWith("/docs/")) {
|
||||
const target = new URL(url.pathname, "https://checkmate-docs.vercel.app");
|
||||
target.search = url.search;
|
||||
return event.respondWith(fetch(target.toString(), event.request));
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**nginx:**
|
||||
|
||||
```nginx
|
||||
location /docs/ {
|
||||
proxy_pass https://checkmate-docs.vercel.app/docs/;
|
||||
proxy_set_header Host checkmate-docs.vercel.app;
|
||||
proxy_ssl_server_name on;
|
||||
}
|
||||
```
|
||||
|
||||
The `base: "/docs"` setting in `astro.config.mjs` ensures every internal link and asset URL is prefixed correctly so the site works at `checkmate.so/docs/...` rather than the root.
|
||||
|
||||
## Updating the docs
|
||||
|
||||
| Task | What to edit |
|
||||
|---|---|
|
||||
| Fix a typo, clarify wording | `docs/api-content/*.md` |
|
||||
| Add a new narrative page | Add the file to `docs/api-content/`, then add a `slug` entry to `astro.config.mjs` → `sidebar` |
|
||||
| Fix something in the API reference | Edit `server/openapi.json` (or the route spec it's built from) — never edit the generated reference pages |
|
||||
| Change branding / colors | `docs/api/src/styles/checkmate.css` |
|
||||
| Change site config (title, logo, social) | `docs/api/astro.config.mjs` |
|
||||
|
||||
Two rules:
|
||||
|
||||
1. **Never hand-edit the API reference.** It's regenerated on every build. Fix the spec.
|
||||
2. **Test code samples against a real instance before merging.** Wrong code in docs is worse than missing code.
|
||||
|
||||
## Why Astro Starlight (and not Mintlify, Docusaurus, etc.)
|
||||
|
||||
- **No third-party account required.** Self-hostable on any static host.
|
||||
- **Best looking** of the modern docs frameworks per multiple 2026 reviews — Distr, among others, migrated away from Docusaurus specifically for Starlight's design.
|
||||
- **Zero JavaScript by default.** Pages are pure HTML — fast load, perfect Lighthouse scores.
|
||||
- **OpenAPI integration is first-class** via `starlight-openapi`.
|
||||
- **Markdown is the source of truth.** No proprietary formats. Easy to migrate later if needed.
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
title: API reference overview
|
||||
description: All endpoints, grouped by resource. Auto-generated from the OpenAPI spec.
|
||||
---
|
||||
|
||||
# API reference
|
||||
|
||||
The full machine-readable specification is at [`server/openapi.json`](https://github.com/bluewave-labs/Checkmate/blob/develop/server/openapi.json). Mintlify renders an interactive reference from it — each endpoint below is fully documented with request/response schemas, examples, and a built-in "Try it" form.
|
||||
|
||||
## Endpoint groups
|
||||
|
||||
| Group | Description |
|
||||
|---|---|
|
||||
| **auth** | Register, log in, password recovery, manage users |
|
||||
| **invite** | Send and redeem team invites |
|
||||
| **monitors** | Create, update, pause, and delete monitors |
|
||||
| **checks** | Read individual check results and team-wide summaries |
|
||||
| **incidents** | List, filter, and resolve downtime incidents |
|
||||
| **maintenance-window** | Schedule maintenance windows to silence checks |
|
||||
| **notifications** | Configure email, Slack, Discord, and webhook channels |
|
||||
| **status-page** | Manage public status pages |
|
||||
| **geo-checks** | Multi-region check results |
|
||||
| **logs** | Audit and operational logs |
|
||||
| **settings** | Global Checkmate settings |
|
||||
| **queue** | Background-job queue health (operational) |
|
||||
| **diagnostic** | System health and version info |
|
||||
|
||||
## Authentication
|
||||
|
||||
Every endpoint except the unauthenticated auth/recovery routes requires:
|
||||
|
||||
```
|
||||
Authorization: Bearer <jwt>
|
||||
```
|
||||
|
||||
See [Authentication](/api/authentication) for how to obtain a token.
|
||||
|
||||
## Conventions recap
|
||||
|
||||
- Base URL: `https://your-checkmate-host/api/v1`
|
||||
- Response envelope: `{ success, msg, data }`
|
||||
- IDs: 24-character MongoDB ObjectIds
|
||||
- Timestamps: ISO 8601 UTC
|
||||
- Durations: integers in milliseconds
|
||||
- Most resources are team-scoped — pass `teamId` where the path includes `/team/...`
|
||||
|
||||
See [Conventions](/api/conventions) for full details.
|
||||
@@ -0,0 +1,137 @@
|
||||
---
|
||||
title: Authentication
|
||||
description: Obtain a JWT from /auth/login and send it as a Bearer token on every subsequent request.
|
||||
---
|
||||
|
||||
# Authentication
|
||||
|
||||
Checkmate uses **JWT bearer tokens**. There are no API keys, no OAuth, and no client secrets. Every API user is a regular Checkmate user — the credentials you log in to the web UI with are the credentials you use to obtain a token.
|
||||
|
||||
## The flow
|
||||
|
||||
1. **Get a token** by calling `POST /auth/login` with your email and password.
|
||||
2. **Send the token** as `Authorization: Bearer <token>` on every subsequent request.
|
||||
3. **Refresh** by calling `POST /auth/login` again before the token expires.
|
||||
|
||||
## Obtaining a token
|
||||
|
||||
```bash
|
||||
curl -X POST https://your-checkmate-host/api/v1/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"email": "you@example.com",
|
||||
"password": "YourPass1!"
|
||||
}'
|
||||
```
|
||||
|
||||
A successful response returns the token plus the authenticated user record:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"msg": "OK",
|
||||
"data": {
|
||||
"user": {
|
||||
"_id": "65f1c2a4d8b9e0123456789a",
|
||||
"firstName": "Ada",
|
||||
"lastName": "Lovelace",
|
||||
"email": "you@example.com",
|
||||
"role": ["admin"],
|
||||
"teamId": "65f1c2a4d8b9e01234567890",
|
||||
"createdAt": "2026-04-01T10:00:00.000Z",
|
||||
"updatedAt": "2026-04-15T14:30:00.000Z"
|
||||
},
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Store both `token` (for the `Authorization` header) and `user.teamId` (most resource endpoints are scoped by team).
|
||||
|
||||
## Using a token
|
||||
|
||||
Every authenticated request must include:
|
||||
|
||||
```
|
||||
Authorization: Bearer <token>
|
||||
```
|
||||
|
||||
```bash
|
||||
curl https://your-checkmate-host/api/v1/monitors/team/<teamId> \
|
||||
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||||
```
|
||||
|
||||
```javascript
|
||||
const res = await fetch("https://your-checkmate-host/api/v1/monitors/team/<teamId>", {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
```
|
||||
|
||||
## Token lifetime
|
||||
|
||||
Tokens expire after the duration configured by the operator via the `TOKEN_TTL` environment variable. The default is **99 days**.
|
||||
|
||||
There is no separate refresh-token endpoint. When a token is close to expiry — or after a 401 response — call `POST /auth/login` again to obtain a new one.
|
||||
|
||||
## Roles
|
||||
|
||||
The `user.role` claim is an array. Available roles:
|
||||
|
||||
| Role | Capability |
|
||||
|---|---|
|
||||
| `superadmin` | Full access to all teams and global settings. Can promote other users. |
|
||||
| `admin` | Full access within their team. |
|
||||
| `user` | Read access within their team. |
|
||||
| `demo` | Limited demo-mode access. |
|
||||
|
||||
A user can hold multiple roles simultaneously.
|
||||
|
||||
## Registering the first user
|
||||
|
||||
A fresh Checkmate instance has no users. The first call to `POST /auth/register` creates the first **superadmin** and provisions a team for them:
|
||||
|
||||
```bash
|
||||
curl -X POST https://your-checkmate-host/api/v1/auth/register \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"firstName": "Ada",
|
||||
"lastName": "Lovelace",
|
||||
"email": "ada@example.com",
|
||||
"password": "Hunter2!",
|
||||
"role": ["superadmin"],
|
||||
"teamId": ""
|
||||
}'
|
||||
```
|
||||
|
||||
After the first user exists, additional users are usually created via the invite flow rather than `POST /auth/register`:
|
||||
|
||||
```
|
||||
POST /invite/send # superadmin/admin sends an invite
|
||||
POST /auth/register # invitee redeems the invite token
|
||||
```
|
||||
|
||||
You can check whether a superadmin already exists with:
|
||||
|
||||
```bash
|
||||
curl https://your-checkmate-host/api/v1/auth/users/superadmin
|
||||
```
|
||||
|
||||
```json
|
||||
{ "success": true, "data": true }
|
||||
```
|
||||
|
||||
## Password recovery
|
||||
|
||||
The password-recovery flow is three calls, all unauthenticated:
|
||||
|
||||
```
|
||||
POST /auth/recovery/request # user requests a recovery email
|
||||
POST /auth/recovery/validate # client validates the recovery token
|
||||
POST /auth/recovery/reset # user sets a new password
|
||||
```
|
||||
|
||||
## Logging out
|
||||
|
||||
JWTs are stateless — there is no server-side logout. Drop the token from your client storage. If you need to invalidate a token before its expiry (e.g. a compromised device), the operator must rotate the `JWT_SECRET` environment variable, which invalidates every outstanding token.
|
||||
|
||||
Next: [Conventions](/api/conventions).
|
||||
@@ -0,0 +1,122 @@
|
||||
---
|
||||
title: Conventions
|
||||
description: Response envelope, request format, identifiers, pagination, and team scoping.
|
||||
---
|
||||
|
||||
# Conventions
|
||||
|
||||
A small number of conventions apply to every endpoint. Once you've internalized them, the per-resource reference is mostly self-explanatory.
|
||||
|
||||
## Request format
|
||||
|
||||
- **Content-Type**: `application/json` for all POST/PUT/PATCH bodies, except multipart uploads (logos, profile images), which use `multipart/form-data`.
|
||||
- **Charset**: UTF-8.
|
||||
- **Method semantics**:
|
||||
- `GET` — read, never mutates state.
|
||||
- `POST` — create, or action that doesn't fit GET/PATCH/DELETE.
|
||||
- `PATCH` — partial update.
|
||||
- `PUT` — full replace.
|
||||
- `DELETE` — delete.
|
||||
|
||||
## Response envelope
|
||||
|
||||
Every JSON response uses the same shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"msg": "OK",
|
||||
"data": { /* endpoint-specific payload */ }
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Meaning |
|
||||
|---|---|---|
|
||||
| `success` | boolean | `true` on 2xx, `false` on 4xx/5xx |
|
||||
| `msg` | string | Human-readable summary — safe to surface in UIs |
|
||||
| `data` | object \| array \| null | Endpoint-specific payload. Absent or `null` for `204 No Content`. |
|
||||
|
||||
Error responses use the same envelope with `success: false` and `data` typically omitted. See [Errors](/api/errors).
|
||||
|
||||
## Identifiers
|
||||
|
||||
- All resource IDs are **MongoDB ObjectIds** (24-character hex strings):
|
||||
```
|
||||
65f1c2a4d8b9e0123456789a
|
||||
```
|
||||
- Treat IDs as opaque strings. Do not attempt to parse the timestamp or counter components.
|
||||
|
||||
## Team scoping
|
||||
|
||||
Most resources belong to a **team**. After login, the user record contains a `teamId`; pass it as a path parameter wherever the endpoint name includes `/team/...`:
|
||||
|
||||
```
|
||||
GET /monitors/team/65f1c2a4d8b9e01234567890
|
||||
GET /incidents/team
|
||||
GET /checks/team
|
||||
```
|
||||
|
||||
Users only see resources belonging to their own team, except for `superadmin`, who can read across teams.
|
||||
|
||||
## Date and time
|
||||
|
||||
- All timestamps in JSON are **ISO 8601** strings in UTC:
|
||||
```
|
||||
"createdAt": "2026-04-01T10:00:00.000Z"
|
||||
```
|
||||
- Durations are typically integers in **milliseconds** (intervals, response times, time windows).
|
||||
|
||||
## Pagination
|
||||
|
||||
Endpoints that return collections accept these optional query parameters:
|
||||
|
||||
| Parameter | Type | Default | Notes |
|
||||
|---|---|---|---|
|
||||
| `limit` | integer | endpoint-dependent | Maximum rows to return. |
|
||||
| `page` | integer | `0` | Zero-indexed page number. |
|
||||
| `sortBy` | string | endpoint-dependent | Field to sort by. |
|
||||
| `sortOrder` | `asc` \| `desc` | `desc` | Sort direction. |
|
||||
| `filter` | string | — | Endpoint-specific filter (e.g. status). |
|
||||
|
||||
When a collection supports pagination, the response `data` includes the rows plus the total count:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"msg": "OK",
|
||||
"data": {
|
||||
"monitors": [ /* … */ ],
|
||||
"filteredMonitors": [ /* … */ ],
|
||||
"summary": { /* counters */ }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Exact shapes vary per endpoint. See the [API reference](/api/reference) for each endpoint's response schema.
|
||||
|
||||
## File uploads
|
||||
|
||||
Two endpoints accept files:
|
||||
|
||||
- Status page logo: `POST /status-page` (multipart, field `logo`)
|
||||
- User profile picture: `POST /auth/register`, `PATCH /auth/user/:id` (multipart, field `profileImage`)
|
||||
|
||||
Use `multipart/form-data` and include the JSON fields as separate form parts.
|
||||
|
||||
## CORS
|
||||
|
||||
The server's CORS policy is configured via the `CLIENT_HOST` environment variable. Only requests whose `Origin` matches `CLIENT_HOST` are accepted, and credentials (cookies) are allowed.
|
||||
|
||||
If you're calling the API from a browser on a different origin, the operator must add that origin to `CLIENT_HOST`. Server-to-server calls (curl, your backend) are not subject to CORS.
|
||||
|
||||
## Versioning
|
||||
|
||||
The base path includes the API version:
|
||||
|
||||
```
|
||||
/api/v1/...
|
||||
```
|
||||
|
||||
Within `v1`, additive changes (new endpoints, new optional fields) ship without notice. Breaking changes (removed endpoints, changed field types) only ship under a new major version.
|
||||
|
||||
Next: [Errors](/api/errors).
|
||||
@@ -0,0 +1,124 @@
|
||||
---
|
||||
title: Errors
|
||||
description: HTTP status codes Checkmate returns, what they mean, and how to handle them.
|
||||
---
|
||||
|
||||
# Errors
|
||||
|
||||
Failure responses share the same envelope as successful ones, with `success: false` and `data` omitted (or `null`):
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"msg": "Validation failed: email must be a valid email"
|
||||
}
|
||||
```
|
||||
|
||||
Always treat the **HTTP status code** as the source of truth for handling logic, and surface `msg` to humans.
|
||||
|
||||
## Status codes
|
||||
|
||||
| Status | Meaning | When you'll see it |
|
||||
|---|---|---|
|
||||
| `200 OK` | Request succeeded with a body | Most GETs, successful mutations that return the new resource |
|
||||
| `204 No Content` | Request succeeded with no body | DELETEs, successful actions like `/status-page/:url/unlock` |
|
||||
| `400 Bad Request` | Malformed request | Missing required path param, malformed JSON, invalid file upload |
|
||||
| `401 Unauthorized` | Missing or invalid JWT | Token expired, token missing, token signed with a different secret |
|
||||
| `403 Forbidden` | Authenticated, but not allowed | Accessing a resource that belongs to a different team, or insufficient role |
|
||||
| `404 Not Found` | Resource doesn't exist | Wrong ID, deleted resource, mistyped path |
|
||||
| `409 Conflict` | Operation would violate a constraint | Duplicate email on register, slug already taken on status page |
|
||||
| `413 Payload Too Large` | File upload exceeded limit | Logo or profile image too big |
|
||||
| `422 Unprocessable Entity` | Request was well-formed but failed validation | Zod / Joi schema rejected the body |
|
||||
| `429 Too Many Requests` | Rate limit exceeded | See [Rate limiting](/api/rate-limiting) |
|
||||
| `500 Internal Server Error` | Unexpected server error | A bug — report it with the request ID |
|
||||
|
||||
## Common shapes
|
||||
|
||||
### 401 — missing or invalid token
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"msg": "No auth token provided"
|
||||
}
|
||||
```
|
||||
|
||||
Re-authenticate by calling `POST /auth/login`.
|
||||
|
||||
### 422 — validation error
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"msg": "\"email\" must be a valid email"
|
||||
}
|
||||
```
|
||||
|
||||
The `msg` describes the first failing field. Re-send the request with the fix.
|
||||
|
||||
### 403 — forbidden
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"msg": "Forbidden"
|
||||
}
|
||||
```
|
||||
|
||||
You're authenticated but the resource isn't visible to you. Common cause: passing a `teamId` that isn't your team's.
|
||||
|
||||
### 429 — rate limited
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"msg": "Too many requests"
|
||||
}
|
||||
```
|
||||
|
||||
Inspect `RateLimit-*` response headers and back off. See [Rate limiting](/api/rate-limiting).
|
||||
|
||||
## Error handling pattern
|
||||
|
||||
Client code should branch on status code, not on `msg`:
|
||||
|
||||
```javascript
|
||||
const res = await fetch(url, opts);
|
||||
const body = await res.json().catch(() => ({}));
|
||||
|
||||
switch (res.status) {
|
||||
case 200:
|
||||
case 204:
|
||||
return body.data;
|
||||
|
||||
case 401:
|
||||
// Token expired or missing — re-authenticate, then retry once.
|
||||
await reauthenticate();
|
||||
return retry();
|
||||
|
||||
case 422:
|
||||
// Surface the validation error to the user.
|
||||
throw new ValidationError(body.msg);
|
||||
|
||||
case 429:
|
||||
// Back off using Retry-After if present.
|
||||
const retryAfter = Number(res.headers.get("Retry-After") ?? 60);
|
||||
await sleep(retryAfter * 1000);
|
||||
return retry();
|
||||
|
||||
default:
|
||||
throw new Error(`Checkmate API ${res.status}: ${body.msg ?? res.statusText}`);
|
||||
}
|
||||
```
|
||||
|
||||
## Reporting bugs
|
||||
|
||||
If you see a `500`, capture:
|
||||
|
||||
- The exact request (method, path, body — **redact secrets**)
|
||||
- The full response body and headers
|
||||
- A timestamp in UTC
|
||||
|
||||
Open an issue at https://github.com/bluewave-labs/Checkmate/issues with that information.
|
||||
|
||||
Next: [Rate limiting](/api/rate-limiting).
|
||||
@@ -0,0 +1,84 @@
|
||||
---
|
||||
title: Introduction
|
||||
description: Use the Checkmate REST API to manage monitors, incidents, status pages, notifications, and users programmatically.
|
||||
---
|
||||
|
||||
# Checkmate API
|
||||
|
||||
Checkmate exposes a REST API that mirrors everything the web UI can do. Use it to provision monitors from infrastructure-as-code, build internal dashboards, automate incident workflows, or integrate Checkmate into your existing alerting stack.
|
||||
|
||||
The web UI you log in to is itself just a client of this same API.
|
||||
|
||||
## Base URL
|
||||
|
||||
```
|
||||
https://<your-checkmate-host>/api/v1
|
||||
```
|
||||
|
||||
Replace `<your-checkmate-host>` with the domain where you deployed Checkmate. For local development:
|
||||
|
||||
```
|
||||
http://localhost:52345/api/v1
|
||||
```
|
||||
|
||||
All endpoints are relative to this base. Every path on this page is shown without the `/api/v1` prefix — prepend it when making requests.
|
||||
|
||||
## API stability
|
||||
|
||||
The API is at version **`v1`** and is considered stable. Breaking changes will only ship under a new major version (`/api/v2`).
|
||||
|
||||
## What you can do
|
||||
|
||||
| Resource | What it covers |
|
||||
|---|---|
|
||||
| `monitors` | Create, update, pause, and delete monitors of any type (HTTP, Ping, Port, DNS, Docker, PageSpeed, gRPC, WebSocket, Game, Hardware) |
|
||||
| `checks` | Read individual check results and aggregated stats |
|
||||
| `incidents` | List, filter, and resolve downtime incidents |
|
||||
| `notifications` | Manage notification channels (email, Slack, Discord, webhooks) |
|
||||
| `status-page` | Configure public status pages |
|
||||
| `maintenance-window` | Schedule maintenance windows so checks don't fire incidents |
|
||||
| `geo-checks` | Multi-region check results |
|
||||
| `auth` | Register, log in, manage users |
|
||||
| `invite` | Invite teammates |
|
||||
| `logs` | Audit and operational logs |
|
||||
| `settings` | Tune global Checkmate settings |
|
||||
| `queue` | Inspect the background-job queue (operational) |
|
||||
| `diagnostic` | Health and system info |
|
||||
|
||||
## What you'll need
|
||||
|
||||
1. A running Checkmate instance you administer.
|
||||
2. A user account on that instance.
|
||||
3. A JWT obtained by calling `POST /auth/login` — see [Authentication](/api/authentication).
|
||||
|
||||
There is no separate "API key" concept in Checkmate. The same JWT that authenticates the web UI authenticates every API call.
|
||||
|
||||
## Response shape
|
||||
|
||||
Every endpoint returns the same envelope:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"msg": "OK",
|
||||
"data": { /* endpoint-specific payload */ }
|
||||
}
|
||||
```
|
||||
|
||||
See [Conventions](/api/conventions) for the full contract and [Errors](/api/errors) for failure shapes.
|
||||
|
||||
## Quick example
|
||||
|
||||
```bash
|
||||
# 1. Log in to get a token
|
||||
TOKEN=$(curl -s -X POST https://your-checkmate-host/api/v1/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"email":"you@example.com","password":"YourPass1!"}' \
|
||||
| jq -r '.data.token')
|
||||
|
||||
# 2. List your team's monitors
|
||||
curl https://your-checkmate-host/api/v1/monitors/team/<teamId> \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
Next: [Authentication](/api/authentication).
|
||||
@@ -0,0 +1,117 @@
|
||||
---
|
||||
title: Quickstart
|
||||
description: From zero to a working monitor in under five minutes.
|
||||
---
|
||||
|
||||
# Quickstart
|
||||
|
||||
This walkthrough creates a Checkmate user, logs in, provisions an HTTP monitor, and reads its checks — all via the API.
|
||||
|
||||
It assumes you have a Checkmate instance running and reachable. Substitute `https://your-checkmate-host` with your actual host throughout.
|
||||
|
||||
## 1. Register the first user
|
||||
|
||||
If your instance is fresh, the first user becomes a superadmin:
|
||||
|
||||
```bash
|
||||
curl -X POST https://your-checkmate-host/api/v1/auth/register \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"firstName": "Ada",
|
||||
"lastName": "Lovelace",
|
||||
"email": "ada@example.com",
|
||||
"password": "Hunter2!Hunter2!",
|
||||
"role": ["superadmin"],
|
||||
"teamId": ""
|
||||
}'
|
||||
```
|
||||
|
||||
The response includes a JWT and a freshly-created `teamId`. Save both.
|
||||
|
||||
If a superadmin already exists, skip to step 2 and use existing credentials.
|
||||
|
||||
## 2. Log in
|
||||
|
||||
If you already have an account:
|
||||
|
||||
```bash
|
||||
TOKEN=$(curl -s -X POST https://your-checkmate-host/api/v1/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"email":"ada@example.com","password":"Hunter2!Hunter2!"}' \
|
||||
| jq -r '.data.token')
|
||||
|
||||
TEAM_ID=$(curl -s -X POST https://your-checkmate-host/api/v1/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"email":"ada@example.com","password":"Hunter2!Hunter2!"}' \
|
||||
| jq -r '.data.user.teamId')
|
||||
|
||||
echo "Token: $TOKEN"
|
||||
echo "Team: $TEAM_ID"
|
||||
```
|
||||
|
||||
## 3. Create a monitor
|
||||
|
||||
This creates an HTTP monitor that probes example.com every 60 seconds:
|
||||
|
||||
```bash
|
||||
curl -X POST https://your-checkmate-host/api/v1/monitors \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"name\": \"example.com\",
|
||||
\"type\": \"http\",
|
||||
\"url\": \"https://example.com\",
|
||||
\"interval\": 60000,
|
||||
\"teamId\": \"$TEAM_ID\",
|
||||
\"isActive\": true
|
||||
}"
|
||||
```
|
||||
|
||||
A successful response returns the new monitor including its `_id`.
|
||||
|
||||
## 4. List your team's monitors
|
||||
|
||||
```bash
|
||||
curl https://your-checkmate-host/api/v1/monitors/team/$TEAM_ID \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
You should see the monitor you just created. Within a minute, the background worker will have run a few checks against it.
|
||||
|
||||
## 5. Read recent checks
|
||||
|
||||
Replace `<MONITOR_ID>` with the `_id` from step 3:
|
||||
|
||||
```bash
|
||||
curl "https://your-checkmate-host/api/v1/checks/<MONITOR_ID>?limit=10" \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
You'll see an array of check results — status code, response time, timestamp, region (if applicable).
|
||||
|
||||
## 6. Pause and delete the monitor
|
||||
|
||||
Pause:
|
||||
|
||||
```bash
|
||||
curl -X PATCH https://your-checkmate-host/api/v1/monitors/<MONITOR_ID> \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"isActive": false}'
|
||||
```
|
||||
|
||||
Delete:
|
||||
|
||||
```bash
|
||||
curl -X DELETE https://your-checkmate-host/api/v1/monitors/<MONITOR_ID> \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
## What's next
|
||||
|
||||
- **Set up notifications** — `POST /notifications` to wire email / Slack / Discord / webhook channels, then attach them to monitors.
|
||||
- **Build a status page** — `POST /status-page` to publish your monitors publicly.
|
||||
- **Schedule maintenance** — `POST /maintenance-window` to silence checks during planned downtime.
|
||||
- **Browse the full API reference** — see every endpoint with request/response schemas.
|
||||
|
||||
See the [reference](/api/reference) for the complete list of endpoints.
|
||||
@@ -0,0 +1,83 @@
|
||||
---
|
||||
title: Rate limiting
|
||||
description: Per-IP request limits, headers, and how to back off.
|
||||
---
|
||||
|
||||
# Rate limiting
|
||||
|
||||
Checkmate applies two per-IP rate limiters, both with a **60-second window**:
|
||||
|
||||
| Limiter | Applies to | Limit |
|
||||
|---|---|---|
|
||||
| General | All requests under `/api/v1/*` | **600 requests / 60 seconds** |
|
||||
| Auth-sensitive | `/auth/login`, `/auth/register`, `/auth/recovery/*`, `/status-page/:url/unlock` | **15 requests / 60 seconds** |
|
||||
|
||||
The two limiters are **independent**. Auth-sensitive endpoints count against both buckets — you can hammer `/auth/login` 15 times in a minute before being rate-limited there, but a single client can never exceed 600 total requests per minute regardless of which endpoints are called.
|
||||
|
||||
## Headers
|
||||
|
||||
Every response includes the standard `RateLimit-*` headers so you can pace yourself proactively:
|
||||
|
||||
```
|
||||
RateLimit-Limit: 600
|
||||
RateLimit-Remaining: 587
|
||||
RateLimit-Reset: 42
|
||||
```
|
||||
|
||||
| Header | Meaning |
|
||||
|---|---|
|
||||
| `RateLimit-Limit` | The cap for the current window. |
|
||||
| `RateLimit-Remaining` | Requests left before you hit `429`. |
|
||||
| `RateLimit-Reset` | Seconds until the window resets. |
|
||||
|
||||
## When you exceed the limit
|
||||
|
||||
You receive `HTTP 429 Too Many Requests`:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"msg": "Too many requests"
|
||||
}
|
||||
```
|
||||
|
||||
The response also includes `Retry-After`:
|
||||
|
||||
```
|
||||
Retry-After: 42
|
||||
```
|
||||
|
||||
Wait that many seconds before retrying. If `Retry-After` is missing, fall back to the `RateLimit-Reset` header.
|
||||
|
||||
## Recommended backoff
|
||||
|
||||
For one-off scripts, simple sleep-then-retry is fine:
|
||||
|
||||
```javascript
|
||||
async function callWithRetry(url, opts, attempts = 3) {
|
||||
for (let i = 0; i < attempts; i++) {
|
||||
const res = await fetch(url, opts);
|
||||
if (res.status !== 429) return res;
|
||||
const retry = Number(res.headers.get("Retry-After") ?? 60);
|
||||
await new Promise(r => setTimeout(r, retry * 1000));
|
||||
}
|
||||
throw new Error("Rate limit retry exhausted");
|
||||
}
|
||||
```
|
||||
|
||||
For higher-throughput integrations, watch `RateLimit-Remaining` and pace your sender to stay below the limit rather than waiting for `429`.
|
||||
|
||||
## Limits and proxies
|
||||
|
||||
Rate-limiting is per **client IP**, derived from the connection — or, when Checkmate is behind a reverse proxy with `trust proxy` configured, from the leftmost public IP in `X-Forwarded-For`.
|
||||
|
||||
If many of your clients share an egress IP (NAT, corporate proxy), they share the bucket. In that case, an operator should either:
|
||||
|
||||
- Increase the limits (server code change required), or
|
||||
- Make sure each client connects from a distinct IP.
|
||||
|
||||
## Limits are not contractual
|
||||
|
||||
The numbers above reflect the current defaults. An operator can change them at any time (they're hardcoded in `server/src/middleware/rateLimiter.ts`). If you're building an integration that depends on a higher limit, coordinate with the operator first.
|
||||
|
||||
Next: [API reference](/api/reference) — auto-generated from the OpenAPI spec.
|
||||
@@ -0,0 +1,16 @@
|
||||
# Astro build
|
||||
dist/
|
||||
.astro/
|
||||
|
||||
# Dependencies
|
||||
node_modules/
|
||||
|
||||
# Logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Env
|
||||
.env
|
||||
.env.production
|
||||
@@ -0,0 +1,74 @@
|
||||
import { defineConfig } from "astro/config";
|
||||
import starlight from "@astrojs/starlight";
|
||||
import starlightOpenAPI, { openAPISidebarGroups } from "starlight-openapi";
|
||||
|
||||
// The OpenAPI spec lives in the server package. The Starlight integration
|
||||
// reads it at build time and generates the full /api/reference/* tree.
|
||||
const OPENAPI_PATH = "../../server/openapi.json";
|
||||
|
||||
export default defineConfig({
|
||||
// Served at https://checkmate.so/docs — the leading slash is included so
|
||||
// every internal link resolves correctly when fronted by a reverse proxy.
|
||||
base: "/docs",
|
||||
site: "https://checkmate.so",
|
||||
|
||||
integrations: [
|
||||
starlight({
|
||||
title: "Checkmate API",
|
||||
description: "Use the Checkmate REST API to manage monitors, incidents, status pages, notifications, and users programmatically.",
|
||||
|
||||
// Prose markdown is loaded from /docs/api-content via the custom
|
||||
// docsLoader in src/content.config.ts. Keeping content outside the
|
||||
// Astro project tree means contributors can edit it without dealing
|
||||
// with Astro / Starlight tooling.
|
||||
|
||||
logo: {
|
||||
src: "./src/assets/logo.svg",
|
||||
replacesTitle: true,
|
||||
},
|
||||
favicon: "/favicon.svg",
|
||||
customCss: ["./src/styles/checkmate.css"],
|
||||
|
||||
social: {
|
||||
github: "https://github.com/bluewave-labs/Checkmate",
|
||||
},
|
||||
|
||||
sidebar: [
|
||||
{
|
||||
label: "Getting started",
|
||||
items: [
|
||||
{ label: "Introduction", slug: "introduction" },
|
||||
{ label: "Quickstart", slug: "quickstart" },
|
||||
{ label: "Authentication", slug: "authentication" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Core concepts",
|
||||
items: [
|
||||
{ label: "Conventions", slug: "conventions" },
|
||||
{ label: "Errors", slug: "errors" },
|
||||
{ label: "Rate limiting", slug: "rate-limiting" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "API reference",
|
||||
items: [
|
||||
{ label: "Overview", slug: "api-reference/overview" },
|
||||
// The OpenAPI plugin appends every endpoint under this group.
|
||||
...openAPISidebarGroups,
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
plugins: [
|
||||
starlightOpenAPI([
|
||||
{
|
||||
base: "api-reference",
|
||||
label: "REST API",
|
||||
schema: OPENAPI_PATH,
|
||||
},
|
||||
]),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "checkmate-docs-api",
|
||||
"type": "module",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"description": "Public API documentation for Checkmate. Built with Astro Starlight.",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/starlight": "^0.30.0",
|
||||
"astro": "^5.0.0",
|
||||
"sharp": "^0.33.5",
|
||||
"starlight-openapi": "^0.13.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 32" fill="none">
|
||||
<rect x="2" y="6" width="20" height="20" rx="4" fill="#13715B"/>
|
||||
<path d="M7 16l3.5 3.5L17 12" stroke="white" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<text x="30" y="22" font-family="-apple-system, BlinkMacSystemFont, 'Inter', sans-serif" font-size="18" font-weight="600" fill="currentColor">Checkmate</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 424 B |
@@ -0,0 +1,13 @@
|
||||
import { defineCollection } from "astro:content";
|
||||
import { docsLoader } from "@astrojs/starlight/loaders";
|
||||
import { docsSchema } from "@astrojs/starlight/schema";
|
||||
|
||||
// Load markdown from a sibling folder so prose lives outside the Astro
|
||||
// project tree. Contributors edit /docs/api-content/*.md without touching
|
||||
// the build tool.
|
||||
export const collections = {
|
||||
docs: defineCollection({
|
||||
loader: docsLoader({ base: "../api-content" }),
|
||||
schema: docsSchema(),
|
||||
}),
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
/* Checkmate brand colors layered on top of Starlight defaults. */
|
||||
:root {
|
||||
--sl-color-accent-low: #0e5a48;
|
||||
--sl-color-accent: #13715b;
|
||||
--sl-color-accent-high: #1a8a6f;
|
||||
|
||||
--sl-color-white: #ffffff;
|
||||
--sl-color-gray-1: #eef2f0;
|
||||
--sl-color-gray-2: #c9d3cf;
|
||||
--sl-color-gray-3: #95a39d;
|
||||
--sl-color-gray-4: #5c6d66;
|
||||
--sl-color-gray-5: #3a4844;
|
||||
--sl-color-gray-6: #2a3531;
|
||||
--sl-color-black: #1a201e;
|
||||
}
|
||||
|
||||
/* Light mode tweaks so the green accent reads against white. */
|
||||
:root[data-theme="light"] {
|
||||
--sl-color-accent-low: #d0e4dc;
|
||||
--sl-color-accent: #13715b;
|
||||
--sl-color-accent-high: #0e5a48;
|
||||
--sl-color-text-accent: #0e5a48;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"extends": "astro/tsconfigs/strict",
|
||||
"include": [".astro/types.d.ts", "**/*"],
|
||||
"exclude": ["dist"]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "https://openapi.vercel.sh/vercel.json",
|
||||
"buildCommand": "npm run build",
|
||||
"outputDirectory": "dist",
|
||||
"framework": "astro",
|
||||
"installCommand": "npm install"
|
||||
}
|
||||
Reference in new issue
Block a user