Skip to main content
All DevHelm API errors return a uniform JSON envelope. The shape is identical for every non-2xx response — only the populated fields differ by error class.

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"
    }
  ]
}
FieldTypeDescription
statusintegerHTTP status code (mirrors the response status line)
codestringCoarse machine-readable error category (e.g. NOT_FOUND, RATE_LIMITED); stable per status
messagestringHuman-readable error description; safe to surface to end users
timestamplongServer time when the error was produced (epoch milliseconds)
requestIdstring | nullOpaque per-request identifier; same value as the X-Request-Id response header. Include in support tickets.
errorsarray | nullStructured per-field rejections; populated for validation errors (400), null otherwise
Each entry in errors contains:
FieldTypeDescription
codestringStable machine-readable validation code (e.g. MONITOR_HEARTBEAT_GRACE_EXCEEDS_INTERVAL)
fieldstring | nullJSON-pointer-like path to the offending field, or null for request-wide errors
messagestringHuman-readable description of the rejection

HTTP status codes

StatusMeaningCommon causes
400Bad RequestValidation errors, malformed JSON, invalid parameters
401UnauthorizedMissing, invalid, or revoked API key
403ForbiddenValid key but insufficient permissions for the organization
404Not FoundResource doesn’t exist or isn’t accessible in the current organization
409ConflictThe request collides with current resource state (e.g. deploy lock, unique constraint)
429Too Many RequestsRate limit exceeded (see Rate Limits)
500Internal Server ErrorUnexpected server error
502Bad GatewayAn upstream provider returned an error
503Service UnavailableTemporary service disruption

Validation errors

When a request fails validation, the errors 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}")

Rate limit errors

429 responses include extra headers — see Rate Limits for details.