Skip to main content
Every error response includes a machine-readable code field. Use this — not the HTTP status or error message — to determine how to handle the error in your application.

Error Code Reference

CodeHTTP StatusDescriptionFrontend Action
AUTHENTICATION_ERROR401Missing, malformed, or invalid tokenRedirect to login
TOKEN_EXPIRED401Access token has expiredRefresh the token, then retry the request
AUTHORIZATION_ERROR403User’s role lacks permission for this endpointShow “access denied” message
NOT_FOUND404The requested resource does not existShow “not found” or redirect
VALIDATION_ERROR422Request body failed validationDisplay field-level errors from details
CONFLICT409Resource already exists (e.g. duplicate email)Inform user of the conflict
RATE_LIMIT_EXCEEDED429Too many requests in the time windowBack off and retry after a delay
TENANT_ERROR400Tenant could not be resolved from the requestCheck configuration / contact support
EXTERNAL_SERVICE_ERROR502A downstream service (AI, LMS, etc.) failedRetry or show a “try again later” message
DATABASE_ERROR500Internal database failureRetry or show a generic error
INTERNAL_ERROR500Unhandled server errorShow a generic error message

Handling Logic

The recommended handling order:
function handleError(response: APIError) {
  switch (response.code) {
    case "TOKEN_EXPIRED":
      // Try refreshing the access token
      return refreshAndRetry();

    case "AUTHENTICATION_ERROR":
      // Token is invalid or missing entirely
      return redirectToLogin();

    case "AUTHORIZATION_ERROR":
      // User doesn't have the right role
      return showAccessDenied();

    case "VALIDATION_ERROR":
      // Field-level errors in response.details
      return showFieldErrors(response.details);

    case "NOT_FOUND":
      return showNotFound();

    case "CONFLICT":
      return showConflictMessage(response.error);

    case "RATE_LIMIT_EXCEEDED":
      return retryWithBackoff();

    default:
      // INTERNAL_ERROR, DATABASE_ERROR, EXTERNAL_SERVICE_ERROR, TENANT_ERROR
      return showGenericError(response.error);
  }
}

Validation Error Details

When code is VALIDATION_ERROR, the details object maps field paths to error messages:
{
  "success": false,
  "error": "Request validation failed",
  "code": "VALIDATION_ERROR",
  "details": {
    "email": "value is not a valid email address",
    "settings.max_sessions": "value is not a valid integer"
  }
}
Field paths use dot notation for nested fields (e.g. settings.max_sessions). Use these keys to display inline errors next to form fields.