Troubleshooting

Error Reference

Every error code, what it means, and how to handle it. All errors follow a consistent JSON format with a requestId for tracing.

Error Format

All API errors return a consistent ApiError shape. The requestId is unique per request and can be used for debugging with the admin audit log.

JSON
{  "statusCode": 404,  "error": "CASE_NOT_FOUND",  "message": "Case cas_abc123 not found",  "requestId": "req_xyz789"}

Error Codes

CodeStatusRetryDescriptionAction
UNAUTHORIZED401NoInvalid or expired JWT tokenRefresh the JWT using onTokenRefresh callback
FORBIDDEN403NoInsufficient permissions for this actionCheck user roles match required permissions
CASE_NOT_FOUND404NoSupport case does not existVerify the case ID is correct
VALIDATION_ERROR400NoRequest body failed schema validationCheck the request body against the API reference
RATE_LIMITED429YesToo many requestsBack off exponentially and retry
INTERNAL_ERROR500YesUnexpected server errorRetry with backoff. If persistent, contact support

Rate Limits

EndpointLimitScope
POST /api/cases10 per minutePer user
POST /api/cases/:id/messages30 per minutePer user
All other endpoints60 per minutePer user

Retry Pattern

For 429 and 500 errors, use exponential backoff. The widget handles this automatically, but here's the pattern for custom integrations:

JavaScript
async function callWithRetry(fn, maxRetries = 3) {  for (let attempt = 1; attempt <= maxRetries; attempt++) {    try {      return await fn();    } catch (err) {      if (err.statusCode === 429) {        // Exponential backoff: 1s, 2s, 4s        const delay = Math.pow(2, attempt - 1) * 1000;        await new Promise(r => setTimeout(r, delay));        continue;      }      throw err; // Non-retryable error    }  }  throw new Error('Max retries exceeded');}