Get Report
Retrieves a report by its ID.
Endpoint
GET /api/reports/:id
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string | The report ID (e.g., rpt_xyz789) or case ID |
Request Example
curl https://api.silentwitness.ai/api/reports/rpt_xyz789 \
-H "X-API-Key: sk-your-api-key"
Response
Success - Processing (200 OK)
{
"success": true,
"data": {
"id": "rpt_xyz789",
"case_id": "case_abc123",
"type": "technical_report",
"status": "processing",
"progress": {
"current_step": "biomechanics_analysis",
"steps_completed": ["delta_v_calculation"],
"message": "Running biomechanics analysis..."
},
"output": null,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:35:00Z"
}
}
Success - Completed (200 OK)
{
"success": true,
"data": {
"id": "rpt_xyz789",
"case_id": "case_abc123",
"type": "technical_report",
"status": "completed",
"progress": {
"current_step": "report_generation",
"steps_completed": ["delta_v_calculation", "biomechanics_analysis", "report_generation"],
"message": "Report generation complete"
},
"output": {
"pdf_url": "https://storage.silentwitness.ai/reports/rpt_xyz789.pdf",
"docx_url": "https://storage.silentwitness.ai/reports/rpt_xyz789.docx",
"report": {
"schema_version": "1.0",
"sections": [
{
"id": "sec_header",
"type": "header",
"title": "Report Header",
"markdown": "April 15, 2026\n\nJane Smith, Esq.\nLaw Offices of Jane Smith\n\n**Case:** Doe v. Miller"
},
{
"id": "sec_assignment",
"type": "assignment",
"title": "Assignment",
"markdown": "Silent Witness was utilized to review..."
},
{
"id": "sec_opinions",
"type": "opinions",
"title": "Opinions",
"markdown": "**Accident Reconstruction**\n\n1. The Honda Civic experienced..."
},
{
"id": "sec_biomechanics",
"type": "biomechanics",
"title": "Biomechanical Analysis",
"markdown": "### Alleged Injuries & Health Considerations\n\n...",
"data": {
"charts": [
{
"chartType": "CERVICAL_COMPRESSION",
"title": "Cervical Compression — Crash vs ADL",
"labels": ["Walking", "Stair climbing", "Sneeze", "Subject crash"],
"datasets": [{ "label": "Force (lbf)", "data": [22, 48, 75, 215] }],
"ylabel": "Cervical Spine Loading (lbf)"
}
],
"motion_tables": [],
"forces_tables": []
}
}
]
}
},
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:45:00Z"
}
}
Success - Not Found (200 OK)
Returns null data if the report doesn't exist (Stripe-style behavior):
{
"success": true,
"data": null
}
Status Values
| Status | Description |
|---|---|
pending | Report creation accepted, processing not started |
processing | Report is being generated |
completed | Report is ready for download |
failed | Report generation failed |
cancelled | Report was cancelled |
Progress Object
The progress field provides information about the current state:
| Field | Description |
|---|---|
current_step | The step currently being processed |
steps_completed | Array of completed step names |
message | Human-readable status message |
Output Object
When status is completed, the output object contains download URLs and an optional structured report:
| Field | Description |
|---|---|
pdf_url | Signed URL to download PDF version (expires in 1 hour) |
docx_url | Signed URL to download DOCX version (expires in 1 hour) |
report | Structured per-section JSON view of the report (see below). May be null for older reports. |
Structured Report Object
The report object provides the report content as discrete, ordered sections. Each section carries its own markdown narrative. The biomechanics section additionally includes structured chart and table data for programmatic rendering.
| Field | Type | Description |
|---|---|---|
schema_version | string | Wire contract version. Currently "1.0". Consumers should branch on this to handle future changes. |
sections | array | Ordered list of report sections. |
Section Object
| Field | Type | Description |
|---|---|---|
id | string | Stable identifier (e.g., sec_header, sec_biomechanics). |
type | string | Section type. For non-biomechanics: header, assignment, opinions, ar_intro, ar_similar_cases, ar_acceleration, ar_liability, references. Biomechanics content is split across multiple sections (see below). |
title | string | Human-readable section heading. |
markdown | string | Section content as self-contained markdown. |
data | object | (Optional) Structured data for the section. See the biomechanics subsection breakdown below for which sections carry data. |
Sections absent from the report (e.g., biomechanics is omitted for accident-only cases) are not included in the array — check by type, do not assume a fixed index.
Biomechanics Section Breakdown
The biomechanical analysis is emitted as multiple ordered sections rather than one monolithic blob. Each body region gets its own section so consumers can render hierarchical views or cherry-pick a single region. Types (in wire order):
| Type | Title | When emitted | data carries |
|---|---|---|---|
biomechanics_alleged_injuries_and_health | Alleged Injuries & Health Considerations | always | — |
biomechanics_injury_mechanisms_intro | Injury Mechanisms & Tolerances | always | — |
biomechanics_injury_mechanism_<region> | e.g., Cervical Spine, TBI, Knee | one per alleged injury | — |
biomechanics_occupant_motion | Occupant Motion | always | — |
biomechanics_occupant_motion_analysis | Occupant Motion Analysis | always | motion_tables |
biomechanics_forces_intro | Forces Exerted On <Name> | if any forces resolved | — |
biomechanics_forces_tbi | Traumatic Brain Injury | if TBI alleged | — |
biomechanics_forces_<region> | e.g., Cervical Spine, Lumbar Spine | one per non-TBI region with resolved forces | forces_tables (filtered to this region) |
biomechanics_loading_considerations | Loading Considerations | always | charts (ADL comparison) |
<region> is the normalized region key: cervical_spine, thoracic_spine, lumbar_spine, shoulder, hip, knee, foot_ankle, tbi. Consumers that want to reconstruct the hierarchy can group by the biomechanics_ type prefix and then by the intro/region pattern (e.g., all types starting with biomechanics_injury_mechanism_ are children of biomechanics_injury_mechanisms_intro).
Polling Strategy
For report generation, we recommend:
- Poll every 5 seconds
- Set a maximum timeout of 10-15 minutes
- Stop polling when
statusiscompleted,failed, orcancelled
async function waitForReport(reportId) {
const maxAttempts = 180; // 15 minutes at 5-second intervals
for (let i = 0; i < maxAttempts; i++) {
const response = await fetch(`/api/reports/${reportId}`);
const { data } = await response.json();
if (!data) {
throw new Error('Report not found');
}
if (data.status === 'completed') {
return data;
}
if (data.status === 'failed' || data.status === 'cancelled') {
throw new Error(`Report ${data.status}: ${data.progress?.message}`);
}
await new Promise(resolve => setTimeout(resolve, 5000));
}
throw new Error('Report generation timeout');
}