Appearance
API Error Codes
EaseLab APIs use standard HTTP status codes with JSON error bodies. Lab routes (/lab/*) wrap errors in the ApiResponse envelope; auth and some admin routes return simpler objects.
HTTP status codes
| Code | Meaning | When used |
|---|---|---|
| 200 | OK | Successful request |
| 201 | Created | Resource created (rare; most use 200) |
| 204 | No Content | Successful delete/logout |
| 400 | Bad Request | Validation failure, business rule violation |
| 401 | Unauthorized | Missing/invalid token, bad credentials |
| 403 | Forbidden | Insufficient permissions, tenant locked |
| 404 | Not Found | Resource does not exist |
| 422 | Unprocessable Entity | Analyzer ingest parse failure |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Unexpected server error |
Response shapes
Lab API error (envelope)
json
{
"requestId": "trace-id",
"timeStampUtc": "2026-06-26T10:00:00Z",
"success": false,
"exception": false,
"message": "Invoice not found.",
"data": null
}Auth error (simple)
json
{ "message": "Invalid email or password." }Tenant read-only lock
HTTP 403 with extended body:
json
{
"message": "Your lab is in read-only mode. Subscribe to restore full access.",
"code": "TENANT_READONLY",
"readonlyMode": true,
"tenant": {
"name": "My Lab",
"slug": "my-lab",
"paymentStatus": "Unpaid",
"isLocked": true
}
}Common error messages
| Message | Cause | Resolution |
|---|---|---|
Invalid email or password. | Bad login credentials | Check email/password |
Verify your email before signing in. | Unverified account | Complete email verification |
Refresh token is missing. | No cookie on refresh | Re-login |
Invalid or expired refresh token. | Expired/revoked session | Re-login |
You do not have permission to perform this action. | Missing RBAC permission | Contact admin |
Resource not found. | Invalid ID | Verify resource exists |
Invalid invoice number or access code. | Public lookup failed | Check patient details |
Invalid or missing analyzer API key. | Bad X-Analyzer-Key | Reconfigure device |
Impersonation code expired or invalid. | Code > 2 min or reused | Start new impersonation |
Your free trial has ended... | Trial expired | Subscribe via billing |
Your subscription period has ended... | Subscription lapsed | Renew plan |
Validation errors
FluentValidation failures on auth routes return 400:
json
{ "message": "Password must be at least 8 characters." }Field-level validation may include errors object in future versions.
Analyzer ingest errors
HTTP 422:
json
{
"message": "Could not match barcode SAMPLE-999.",
"logId": "guid-for-support"
}Use logId when contacting support — it links to the ingestion log.
Client handling recommendations
| Status | Client action |
|---|---|
| 401 | Attempt refresh; if fails, redirect to login |
403 + TENANT_READONLY | Show billing prompt; allow reads only |
| 403 (permission) | Show access denied UI |
| 404 | Show not-found state |
| 429 | Retry with exponential backoff |
| 500 | Show generic error; include requestId in support tickets |
Debugging
Include the requestId from any error response when reporting issues. It maps to server trace logs.