Error Envelope

All error responses follow a consistent JSON envelope so your code can handle failures uniformly:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "status": 422,
    "message": "Validation failed",
    "details": [
      {
        "field": "name",
        "message": "Name is required and must be between 1 and 255 characters"
      }
    ],
    "requestId": "req_clx9abc123def"
  }
}

Envelope Fields

FieldTypeAlways PresentDescription
codestringYesMachine-readable error code (e.g., NOT_FOUND, UNAUTHORIZED)
statusnumberYesHTTP status code (mirrors the response status)
messagestringYesHuman-readable description of what went wrong
detailsarrayNoDetailed validation errors — only present for 422 responses
details[].fieldstring—The input field that failed validation
details[].messagestring—Explanation of why the field is invalid
requestIdstringYesUnique identifier for the request, useful for support tickets

Always include the requestId when contacting support. It lets the ProBeya team trace your exact request through the system logs.

Error Code Reference

400 Bad Request

Returned when the request is malformed — invalid JSON, missing required query parameters, or a cursor value that cannot be decoded.

{
  "error": {
    "code": "BAD_REQUEST",
    "status": 400,
    "message": "Invalid JSON in request body",
    "requestId": "req_clx9abc123def"
  }
}

Common causes:

  • Malformed JSON body (missing quotes, trailing commas)
  • Invalid cursor value in pagination
  • limit outside the allowed 1–200 range
  • Missing required query parameters

401 Unauthorized

Returned when the request has no credentials or the provided credentials are invalid.

{
  "error": {
    "code": "UNAUTHORIZED",
    "status": 401,
    "message": "Invalid or expired API key",
    "requestId": "req_clx9abc123def"
  }
}

Common causes:

  • Missing Authorization header
  • Expired API token
  • Revoked API token
  • Malformed Bearer token format

Fix: Generate a new token from Settings > API Tokens and ensure the Authorization: Bearer <token> header is present on every request.


403 Forbidden

Returned when the authenticated user or token does not have permission to perform the requested action.

{
  "error": {
    "code": "FORBIDDEN",
    "status": 403,
    "message": "Insufficient permissions: requires 'write:board' scope",
    "requestId": "req_clx9abc123def"
  }
}

Common causes:

  • Token is missing the required scope (e.g., trying to write with a read-only token)
  • User’s role does not have permission for the action
  • Attempting to access a resource in a different organization than the token’s scope
  • Organization plan does not include the requested feature

404 Not Found

Returned when the requested resource does not exist or the authenticated user does not have visibility into it.

{
  "error": {
    "code": "NOT_FOUND",
    "status": 404,
    "message": "Item not found: itm_clx9abc123",
    "requestId": "req_clx9abc123def"
  }
}

Common causes:

  • The resource ID is incorrect or does not exist
  • The resource was deleted
  • The resource belongs to a different organization (for security, ProBeya returns 404 instead of 403 to prevent resource enumeration)

409 Conflict

Returned when the request conflicts with the current state of the resource — typically due to concurrent modifications or uniqueness violations.

{
  "error": {
    "code": "CONFLICT",
    "status": 409,
    "message": "A workspace with slug 'quality-lab' already exists",
    "requestId": "req_clx9abc123def"
  }
}

Common causes:

  • Duplicate slug or unique field value
  • Concurrent update conflict (another user modified the record simultaneously)
  • Attempting to create a resource that already exists

Fix: Fetch the latest version of the resource with a GET request, resolve the conflict, and retry with the updated data.


422 Validation Error

Returned when the request body or parameters fail schema validation. The details array describes every field that failed.

{
  "error": {
    "code": "VALIDATION_ERROR",
    "status": 422,
    "message": "Validation failed",
    "details": [
      {
        "field": "name",
        "message": "Name is required"
      },
      {
        "field": "priority",
        "message": "Invalid enum value. Expected 'low' | 'medium' | 'high' | 'critical', received 'urgent'"
      },
      {
        "field": "dueDate",
        "message": "Expected ISO 8601 date string, received '2026/13/45'"
      }
    ],
    "requestId": "req_clx9abc123def"
  }
}

Common causes:

  • Missing required fields
  • Field value outside allowed range or enum
  • Invalid date format (must be ISO 8601)
  • String exceeding maximum length

Fix: Inspect the details array and correct each flagged field before retrying.


429 Too Many Requests

Returned when the API key has exceeded its rate limit for the current window. See Rate Limiting for full details.

{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "status": 429,
    "message": "Rate limit exceeded. Retry after 12 seconds.",
    "retryAfter": 12,
    "requestId": "req_clx9abc123def"
  }
}

Response headers:

Retry-After: 12
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1711032060

Fix: Wait the number of seconds specified in the Retry-After header, then retry. Implement exponential backoff with jitter for robust handling.


500 Internal Server Error

Returned when an unexpected error occurs on the server. These are never expected — if you encounter one consistently, please contact support.

{
  "error": {
    "code": "INTERNAL_SERVER_ERROR",
    "status": 500,
    "message": "An unexpected error occurred. Please try again or contact support.",
    "requestId": "req_clx9abc123def"
  }
}

What to do:

  1. Retry the request after a short delay (the error may be transient)
  2. If the error persists, open a support ticket with the requestId
  3. Check status.probeya.com for any ongoing incidents

Handling Errors in Code

async function callProBeyaAPI(url, options) {
  const response = await fetch(url, options);

  if (!response.ok) {
    const body = await response.json();
    const error = body.error;

    switch (error.status) {
      case 401:
        // Token expired or invalid — refresh or prompt re-auth
        throw new Error(`Auth failed: ${error.message}`);

      case 403:
        // Insufficient permissions — check token scopes
        throw new Error(`Forbidden: ${error.message}`);

      case 404:
        // Resource not found — may have been deleted
        return null;

      case 422:
        // Validation error — inspect details for field-level messages
        const fieldErrors = error.details
          ?.map((d) => `${d.field}: ${d.message}`)
          .join(", ");
        throw new Error(`Validation: ${fieldErrors}`);

      case 429:
        // Rate limited — caller should implement retry logic
        throw new Error(`Rate limited. Retry after ${error.retryAfter}s`);

      default:
        throw new Error(`API error ${error.status}: ${error.message}`);
    }
  }

  return response.json();
}