Errors
The Silent Witness API uses conventional HTTP response codes. Codes in the 2xx range indicate success, 4xx codes indicate client errors, and 5xx codes indicate server errors.
Error Response Format
Every error returns a consistent JSON envelope:
{
"success": false,
"error": "Human-readable error message",
"error_code": "specific_error_code",
"error_type": "invalid_request_error",
"error_param": "field_name",
"request_id": "req_abc123"
}
| Field | Type | Description |
|---|---|---|
success | boolean | Always false on error responses |
error | string | Human-readable message safe to show end users. Wording may change — do not pattern-match on it |
error_code | string | Stable machine-readable identifier. Switch on this field in your code |
error_type | string | Coarse category for generic handling (see below) |
error_param | string | Present when the error relates to a specific input field (e.g., "case_id", "occupants") |
request_id | string | Unique per-request ID (format: req_<hex>). Include when contacting support |
Error Types
| Type | Description |
|---|---|
invalid_request_error | 4xx — the request was invalid, caller can fix and retry |
authentication_error | 401 — bad or missing credentials |
permission_error | 403 — authenticated but denied |
rate_limit_error | 429 — too many requests |
api_error | 5xx — server fault, retry with backoff |
Client-supplied Request IDs
Send your own X-Request-ID header for tracing — the API will use it as-is and echo it back. If you don't send one, a req_<hex> value is generated automatically.
Error Code Catalog
File Upload
error_code | HTTP | Meaning |
|---|---|---|
case_id_missing | 400 | The case_id form field was not provided |
file_missing | 400 | No file form field in the multipart upload |
file_empty | 400 | The uploaded file is 0 bytes |
file_too_large | 400 | File exceeds the 50MB limit |
unsupported_file_type | 400 | Detected content type is not JPEG, PNG, GIF, WebP, or PDF. The message includes the detected MIME type |
vehicle_index_not_allowed | 400 | vehicle_role was sent with file_category=tcr_document. TCR is case-level — remove vehicle_role |
Cases
error_code | HTTP | Meaning |
|---|---|---|
case_not_found | 404 | Case does not exist or belongs to a different account |
case_access_denied | 403 | Authenticated but not authorized for this case |
Occupants
error_code | HTTP | Meaning |
|---|---|---|
occupant_field_required | 400 | An occupant is missing required fields. The error message lists which fields. error_param is "occupants" |
occupants_missing | 422 | Report creation for accident_injury case but no occupants exist. Add occupants before creating the report |
Reports
error_code | HTTP | Meaning |
|---|---|---|
unprocessable_entity | 422 | Missing crash data — no vehicles, photos, or EDR on the case |
tcr_extraction_failed | 422 | Traffic Collision Report PDF could not be parsed. Re-upload a cleaner scan |
tcr_extraction_timeout | 504 | TCR extraction exceeded its time budget. Retry after a short delay |
Organizations
error_code | HTTP | Meaning |
|---|---|---|
org_field_required | 400 | A required field is missing. error_param identifies which one |
org_invalid_body | 400 | The request body is not valid JSON |
org_not_found | 404 | Organization does not exist |
org_access_denied | 403 | Organization belongs to a different account |
Generic
error_code | HTTP | Meaning |
|---|---|---|
internal_error | 500 | Unexpected server failure. Include request_id when reporting to support |
When a handler has not been migrated to a specific code, error_code is derived from the HTTP status: bad_request, unauthorized, forbidden, not_found, conflict, unprocessable_entity, too_many_requests, gateway_timeout, internal_error.
Report Generation Errors
Report generation is asynchronous. Pre-flight validation errors (422) are returned immediately when calling POST /api/reports. Runtime errors appear when polling the report status:
| Scenario | Response | Resolution |
|---|---|---|
| No crash data | 422 (immediate) | Upload damage photos or EDR before creating a report |
No occupants (accident_injury) | 422 (immediate) | Add occupants with all required fields |
| TCR extraction failed | 422 (immediate) | Re-upload a cleaner scan of the police report |
| Delta-V calculation failed | status: "failed" (async) | Upload clearer damage photos and retry |
| Report timeout | status: "failed" (async) | Retry — usually transient |
To retry after an async failure, create a new report on the same case. You don't need to re-upload files.
Troubleshooting
Authentication errors with a valid API key
- Verify key starts with
sk-and is 64 characters - Check environment variable is set (no extra spaces)
- Test with a fresh key from the dashboard
File upload returns "unsupported file type"
The server detects file type from the file's bytes, not the Content-Type header. A .txt file renamed to photo.jpg is rejected because its bytes are plain text. The error message includes the detected MIME type.
Report creation returns 422
unprocessable_entity: Upload damage photos or EDR to the plaintiff vehicleoccupants_missing: Add at least one occupant with all 7 required fieldstcr_extraction_failed: The police report PDF is unreadable — re-upload
Intermittent 500 errors
- These are usually transient — retry after a few seconds with backoff
- Include the
request_idwhen contacting support