API
Errors & rate limits
The error envelope, every error code, and how per-key rate limiting works.
Error envelope
Errors always return the same JSON shape — switch on the machine-readable
code, show the message to humans:
{
"error": {
"code": "not_found",
"message": "Monitor not found"
}
}| HTTP | code | When |
|---|---|---|
| 400 | invalid_request | Malformed JSON, failed validation, bad cursor |
| 401 | unauthorized | Missing, unknown, revoked or expired API key |
| 403 | forbidden | Key lacks the write scope, or the plan has no API access |
| 404 | not_found | Resource doesn't exist (or belongs to another organization) |
| 409 | conflict | Invalid state transition, or an Idempotency-Key reused with a different request |
| 405 | method_not_allowed | HTTP method not supported on this endpoint (see the Allow header) |
| 429 | rate_limited | Request budget exhausted |
| 500 | internal_error | Something broke on our side — safe to retry |
| 501 | not_implemented | Endpoint is on the roadmap but not available yet |
Validation errors put the field path in the message:
{
"error": {
"code": "invalid_request",
"message": "durationMinutes: Number must be greater than or equal to 5"
}
}Idempotency & safe retries
Every endpoint is safe to retry:
- GET / DELETE / PATCH are idempotent by nature — repeating them yields
the same end state (a repeated
DELETEreturns404, which your retry logic can treat as success). - POST actions (
pause,resume,acknowledge,resolve) are idempotent no-ops when the resource is already in the target state — they return200with the current object instead of erroring. - POST creates accept an optional
Idempotency-Keyheader (any unique string up to 255 chars, e.g. a UUID). If a request with the same key already succeeded within 24 hours, the stored response is replayed with anIdempotency-Replay: trueheader and no duplicate resource is created. Reusing a key with a different request body returns409.
curl -X POST https://watchfor.io/api/v1/maintenance-windows \
-H "Authorization: Bearer wf_live_YOUR_KEY" \
-H "Idempotency-Key: 3f7a2c9e-retry-safe" \
-H "Content-Type: application/json" \
-d '{ "name": "DB upgrade", "…": "…" }'Rate limits
Each API key has a per-minute request budget determined by your plan (sliding window). Every response reports where you stand:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 42When the budget is exhausted you get 429 with a Retry-After header
(seconds). Back off at least that long — ideally exponentially with jitter,
and never retry a 429 immediately.
{
"error": {
"code": "rate_limited",
"message": "Rate limit exceeded — slow down"
}
}