API Errors

HTTP error codes, error response format, retry patterns, and rate limit handling for the Formael Management API

Documentation

API Errors

All errors return a standard JSON envelope. Use the statusCode and message fields for programmatic handling.

Error Response Format

{
  "statusCode": 403,
  "message": "Only organization owners and admins can manage policy rules",
  "error": "Forbidden"
}

Validation errors (400) may include a message array listing each failing field:

{
  "statusCode": 400,
  "message": ["name must be a string", "role must be one of: member, admin, owner"],
  "error": "Bad Request"
}

HTTP Status Codes

CodeMeaningCommon causes
200OKSuccessful read or mutation
201CreatedResource created
204No ContentSuccessful deletion
400Bad RequestValidation failure - check message for field details
401UnauthorizedMissing, expired, or invalid token
403ForbiddenValid token but insufficient role, or JWT-only endpoint called with management token
404Not FoundResource does not exist or belongs to a different organization
409ConflictDuplicate resource (e.g. agent name already in use)
422Unprocessable EntitySemantically invalid request (e.g. parameters don't match capability schema)
429Too Many RequestsRate limit exceeded - see below
500Internal Server ErrorUnexpected server-side error

401 - Unauthorized

Your token is missing, malformed, or expired.

Checklist:

  • The Authorization header is present: Authorization: Bearer fml_mgmt_...
  • The token has not been revoked (check dashboard → Settings → API Keys)
  • The token has not passed its expiry date
  • You are not calling a JWT-only endpoint with a management token (use the dashboard instead)

403 - Forbidden

Your token is valid but lacks the required role for this operation.

Checklist:

  • The token's role meets the minimum required (see the endpoint summary in the API reference)
  • For admin-only operations, upgrade the token role or use a different token
  • For JWT-only endpoints, authenticate via the dashboard OAuth2 flow

429 - Rate Limiting

The API applies per-organization rate limits. When exceeded, the response includes a Retry-After header:

HTTP/1.1 429 Too Many Requests
Retry-After: 12
Content-Type: application/json

{"statusCode": 429, "message": "Too Many Requests"}

Retry strategy: wait Retry-After seconds before retrying. Do not retry immediately - repeated 429s with no backoff will extend the cooldown window.

Retry Patterns

For transient errors (500, 502, 503, 504), use exponential backoff:

async function withRetry<T>(fn: () => Promise<T>, maxAttempts = 3): Promise<T> {
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      return await fn();
    } catch (err) {
      const isRetryable = err.status >= 500 || err.status === 429;
      const isLastAttempt = attempt === maxAttempts;
 
      if (!isRetryable || isLastAttempt) throw err;
 
      const delay = 2 ** attempt * 100; // 200ms, 400ms, 800ms
      await new Promise((r) => setTimeout(r, delay));
    }
  }
  throw new Error('unreachable');
}

Do not retry 400, 401, 403, 404, or 409 - these are deterministic failures that require a code or configuration fix, not a retry.