Files
Checkmate/docs/api-content/errors.md
T
gorkem-bwl 9aa2594df7 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
2026-05-22 10:21:10 -04:00

125 lines
3.5 KiB
Markdown

---
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).