Error Reference

The CaseHug API uses standard HTTP status codes and returns a consistent error format for all failures. This page documents the error structure, status codes, and common errors.

Error Response Format

All errors return a JSON body with an error object containing a machine-readable code, a human-readable message, and optional details.

Error Response
{
  "error": {
    "code": "matter_not_found",
    "message": "No matter found with id mat_01HXYZ",
    "details": {
      "id": "mat_01HXYZ",
      "resource": "matter"
    }
  }
}

Error Object Fields

FieldTypeDescription
codestringMachine-readable error identifier (snake_case)
messagestringHuman-readable description of the error
detailsobjectAdditional context — structure depends on error type

Validation Error Format

For 400 validation errors, the details object includes a fields array describing exactly what failed.

Validation Error
{
  "error": {
    "code": "validation_error",
    "message": "Request body contains invalid fields",
    "details": {
      "fields": [
        {
          "field": "title",
          "message": "title is required"
        },
        {
          "field": "practice_area",
          "message": "practice_area must be one of: personal_injury, estate_planning, family_law, immigration, criminal, business, real_estate, other"
        }
      ]
    }
  }
}

HTTP Status Codes

StatusMeaningWhen It Occurs
200OKRequest succeeded
201CreatedResource was successfully created
204No ContentRequest succeeded, no body (e.g. DELETE)
400Bad RequestInvalid request body, missing required fields, or validation errors
401UnauthorizedMissing, invalid, or expired API key or token
403ForbiddenValid credentials but insufficient permissions or scope
404Not FoundResource does not exist or belongs to another firm
409ConflictDuplicate resource (e.g. email already exists)
422Unprocessable EntitySemantically invalid request (e.g. invalid state transition)
429Too Many RequestsRate limit exceeded — see Retry-After header
500Internal Server ErrorUnexpected server error — contact support
502Bad GatewayUpstream service unavailable (temporary)
503Service UnavailableAPI is down for maintenance

Common Error Codes

401
Error CodeHow to Fix
missing_api_keyAdd X-Api-Key header to your request
invalid_api_keyCheck for typos. Regenerate the key in Settings → API Keys if needed
expired_tokenRefresh your OAuth access token using the refresh_token grant
revoked_api_keyThe key was manually revoked. Create a new API key
403
Error CodeHow to Fix
insufficient_scopeRegenerate the API key with the required scope enabled
firm_suspendedContact support@calmintake.com to restore your account
forbiddenThe resource belongs to a different firm than the API key
404
Error CodeHow to Fix
matter_not_foundCheck the matter ID. Use GET /api/v1/matters to list valid IDs
client_not_foundCheck the client ID or create the client first
webhook_not_foundVerify the webhook ID via GET /api/v1/webhooks
upload_not_foundThe upload ID was not returned by POST /uploads/signed-url
409
Error CodeHow to Fix
duplicate_emailUse GET /api/v1/clients?search=email to find the existing client
user_already_existsThe email is already a member of this firm
429
Error CodeHow to Fix
rate_limit_exceededWait for the Retry-After duration before retrying. Implement exponential backoff
422
Error CodeHow to Fix
invalid_status_transitionCheck valid status transitions in the endpoint docs
upload_url_expiredRequest a new signed URL — they expire in 15 minutes
user_limit_reachedUpgrade your plan to add more users
send_failedVerify the email/phone is valid. Check your firm notification settings

Handling Errors in Code

JavaScript / TypeScript

typescript
async function apiRequest(url, options = {}) {
  const response = await fetch(`https://api.calmintake.com${url}`, {
    ...options,
    headers: {
      'X-Api-Key': process.env.CASEHUG_API_KEY,
      'Content-Type': 'application/json',
      ...options.headers,
    },
  });

  const body = await response.json();

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

    switch (response.status) {
      case 400:
        throw new ValidationError(error.message, error.details?.fields);
      case 401:
        throw new AuthError('Authentication failed: ' + error.code);
      case 403:
        throw new PermissionError(error.message);
      case 404:
        throw new NotFoundError(error.message);
      case 429:
        const retryAfter = response.headers.get('Retry-After');
        throw new RateLimitError(`Rate limited. Retry after ${retryAfter}s`);
      default:
        throw new ApiError(error.message, response.status);
    }
  }

  return body.data;
}

Python

python
import requests
import time

def api_request(method: str, path: str, **kwargs) -> dict:
    response = requests.request(
        method,
        f"https://api.calmintake.com{path}",
        headers={
            "X-Api-Key": os.environ["CASEHUG_API_KEY"],
            "Content-Type": "application/json",
        },
        **kwargs
    )

    if response.status_code == 429:
        retry_after = int(response.headers.get("Retry-After", 60))
        time.sleep(retry_after)
        return api_request(method, path, **kwargs)  # retry once

    if not response.ok:
        error = response.json().get("error", {})
        code = error.get("code", "unknown")
        message = error.get("message", "API request failed")
        raise CaseHugApiError(
            f"[{response.status_code}] {code}: {message}",
            status_code=response.status_code,
            code=code,
        )

    return response.json()["data"]

Server Errors (5xx)

If you receive a 500, 502, or 503 response, it is a server-side issue. We recommend:

  • • Implement retry with exponential backoff (start at 1s, max 3 attempts)
  • • Log the full response body — it may contain a trace ID to share with support
  • • Check status.calmintake.com for ongoing incidents
  • • Contact support@calmintake.com with the trace ID if the issue persists