Error Handling

Understand error response shapes, HTTP status codes, when to retry, and how to parse failures in your integration.

Always check HTTP status first

Error bodies are JSON with Content-Type: application/json. The exact fields vary by endpoint — use the status code to decide whether to refresh a token, fix input, or retry later.

Error response schema

There is no single global error envelope. Most error responses include one primary text field (message or error). Validation failures may add an errors object or array with per-field detail.

FieldTypeDescription
messagestringPrimary human-readable error text (auth, login, orders, rate limits).
errorstringAlternate single-field error message (assets, team users, legal hold).
errorsobject | arrayValidation details — field map or Joi details array
successbooleanfalse on auth middleware failures and some business-rule errors.

Success responses are endpoint-specific (lists include pagination metadata; mutations may return message plus created IDs). This guide covers failure responses only.

HTTP status code catalogue

StatusTitleWhen you see itRetry
200OKRequest succeeded.
201CreatedResource created (e.g. legal hold ticket).
400Bad RequestMalformed payload shape or business rule blocked the action.Fix the request; do not retry unchanged.
401UnauthorizedMissing, invalid, revoked, or expired token; invalid login credentials.Refresh token or re-login, then retry once.
404Not FoundUnknown route, order, asset, employee, or recovery ID.Fix URL or ID; do not retry unchanged.
422Validation ErrorRequired fields missing or field values invalid.Fix payload; do not retry unchanged.
429Too Many RequestsRate limit exceeded (60 req/min per IP).Backoff and retry (see Rate Limits).
500Internal Server ErrorUnexpected server failure.Exponential backoff; contact support if persistent.

Example payloads

401 — Missing Bearer token

Returned by the auth middleware when Authorization is absent.

{
  "success": false,
  "message": "Access denied: No token provided."
}

401 — Invalid or expired JWT

{
  "success": false,
  "message": "Access denied: token Not Valid."
}

401 — Revoked token

After calling DELETE /api/v1/auth/token, the access token is revoked and will be rejected for all subsequent requests until the user logs in again.

{
  "success": false,
  "message": "Access denied: token has been revoked."
}

401 / 422 — Login failures

Missing credentials return 422; wrong credentials return 401.

{
  "message": "client_id & client_secret are required"
}
{
  "message": "Invalid credentials"
}

404 — Unknown route or resource

{
  "message": "The requested API endpoint does not exist"
}
{
  "error": "Asset number not found in your company."
}

422 — Validation (Joi details array)

Some endpoints return a Joi details array under errors.

{
  "errors": [
    {
      "message": "\"assign_to_email\" must be a valid email",
      "path": ["assign_to_email"],
      "type": "string.email"
    }
  ]
}

422 — Validation (field map)

Bulk import and similar endpoints may return a map of field names to messages.

{
  "errors": {
    "email": "Email is required",
    "firstname": "First name may only contain letters and spaces"
  }
}

400 — Business rule

{
  "success": false,
  "message": "Payload must be a non-empty array."
}

429 — Rate limit

See also RateLimit-* response headers.

{
  "message": "Too many requests. Please try again later."
}

500 — Server error

{
  "message": "An unexpected error occurred"
}

Retry guidance

StatusAction
401Call refresh-token once. If still 401, re-login with client_id / client_secret. Do not retry in a tight loop.
422 / 400 / 404Do not retry with the same payload. Fix validation errors or verify IDs and URLs.
429Read RateLimit-Reset or use exponential backoff (1s → 2s → 4s, cap ~30s). See the Rate Limits guide.
500Retry with exponential backoff (e.g. 3 attempts). If errors persist, contact Unduit support with timestamp and endpoint.
  • Treat POST mutations as non-idempotent unless the endpoint docs state otherwise — avoid blind retries that may duplicate resources.
  • Log the HTTP status, response body, and request ID (if you add one client-side) for support tickets.
  • Surface message or error to users; avoid exposing raw serverError values in production UIs.

Example: parsing errors in JavaScript

async function parseApiError(response) {
  let body = {};
  try {
    body = await response.json();
  } catch {
    return { status: response.status, message: response.statusText };
  }

  const message =
    body.message ??
    body.error ??
    (typeof body.errors === 'object' && !Array.isArray(body.errors)
      ? Object.values(body.errors).join('; ')
      : Array.isArray(body.errors)
        ? body.errors.map((e) => e.message ?? e.msg ?? String(e)).join('; ')
        : 'Request failed');

  return { status: response.status, message, raw: body };
}

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

  if (response.ok) {
    return response.json();
  }

  const err = await parseApiError(response);

  if (response.status === 401) {
    // refresh token or re-login, then retry once
  } else if (response.status === 429) {
    // backoff using RateLimit-Reset header
  } else if (response.status >= 500) {
    // exponential backoff retry
  }

  throw err;
}

Next steps