Error response format
{
"status": 400,
"code": "VALIDATION_FAILED",
"message": "Monitor name must not be blank",
"timestamp": 1712956800000,
"requestId": "5b6f7a8c-1234-4d5e-9f0a-1b2c3d4e5f6a",
"errors": [
{
"code": "MONITOR_NAME_BLANK",
"field": "name",
"message": "must not be blank"
}
]
}
| Field | Type | Description |
|---|---|---|
status | integer | HTTP status code (mirrors the response status line) |
code | string | Coarse machine-readable error category (e.g. NOT_FOUND, RATE_LIMITED); stable per status |
message | string | Human-readable error description; safe to surface to end users |
timestamp | long | Server time when the error was produced (epoch milliseconds) |
requestId | string | null | Opaque per-request identifier; same value as the X-Request-Id response header. Include in support tickets. |
errors | array | null | Structured per-field rejections; populated for validation errors (400), null otherwise |
errors contains:
| Field | Type | Description |
|---|---|---|
code | string | Stable machine-readable validation code (e.g. MONITOR_HEARTBEAT_GRACE_EXCEEDS_INTERVAL) |
field | string | null | JSON-pointer-like path to the offending field, or null for request-wide errors |
message | string | Human-readable description of the rejection |
HTTP status codes
| Status | Meaning | Common causes |
|---|---|---|
400 | Bad Request | Validation errors, malformed JSON, invalid parameters |
401 | Unauthorized | Missing, invalid, or revoked API key |
403 | Forbidden | Valid key but insufficient permissions for the organization |
404 | Not Found | Resource doesn’t exist or isn’t accessible in the current organization |
409 | Conflict | The request collides with current resource state (e.g. deploy lock, unique constraint) |
429 | Too Many Requests | Rate limit exceeded (see Rate Limits) |
500 | Internal Server Error | Unexpected server error |
502 | Bad Gateway | An upstream provider returned an error |
503 | Service Unavailable | Temporary service disruption |
Validation errors
When a request fails validation, theerrors array contains one entry per rejected field. Each entry carries a stable code you can programmatically match on:
{
"status": 400,
"code": "VALIDATION_FAILED",
"message": "Request validation failed",
"timestamp": 1712956800000,
"requestId": "a1b2c3d4-5678-90ab-cdef-111111111111",
"errors": [
{
"code": "MONITOR_NAME_BLANK",
"field": "name",
"message": "must not be blank"
},
{
"code": "MONITOR_FREQUENCY_OUT_OF_RANGE",
"field": "frequencySeconds",
"message": "must be between 30 and 86400"
}
]
}
Common error scenarios
Authentication errors
API
# Missing API key
curl https://api.devhelm.io/api/v1/monitors
# → 401: {"status":401,"code":"UNAUTHORIZED","message":"Full authentication is required",...}
# Invalid key
curl https://api.devhelm.io/api/v1/monitors \
-H "Authorization: Bearer dh_live_invalid"
# → 401: {"status":401,"code":"UNAUTHORIZED","message":"Invalid or revoked API key",...}
Resource not found
API
curl https://api.devhelm.io/api/v1/monitors/nonexistent-id \
-H "Authorization: Bearer $DEVHELM_API_TOKEN"
# → 404: {"status":404,"code":"NOT_FOUND","message":"Monitor not found",...}
Entitlement limits
API
# Exceeding monitor limit on your plan
curl -X POST https://api.devhelm.io/api/v1/monitors \
-H "Authorization: Bearer $DEVHELM_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{ ... }'
# → 400: {"status":400,"code":"ENTITLEMENT_EXCEEDED","message":"Monitor limit reached for your plan",...}
Handling errors in code
import { DevhelmApiError, DevhelmRateLimitError } from "@devhelm/sdk";
try {
const monitor = await client.monitors.create({ ... });
} catch (error) {
if (error instanceof DevhelmRateLimitError) {
const retryAfter = error.headers?.get("Retry-After");
await sleep(parseInt(retryAfter ?? "5") * 1000);
} else if (error instanceof DevhelmApiError) {
console.error(`${error.code}: ${error.message}`);
}
}
from devhelm import DevhelmApiError, DevhelmRateLimitError
try:
monitor = client.monitors.create(...)
except DevhelmRateLimitError as e:
print(f"Rate limited (code={e.code}), retry shortly")
except DevhelmApiError as e:
print(f"API error {e.status} [{e.code}]: {e.message}")