Skip to main content

Get Report

Retrieves a report by its ID.

Endpoint

GET /api/reports/:id

Path Parameters

ParameterTypeDescription
idstringThe 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

StatusDescription
pendingReport creation accepted, processing not started
processingReport is being generated
completedReport is ready for download
failedReport generation failed
cancelledReport was cancelled

Progress Object

The progress field provides information about the current state:

FieldDescription
current_stepThe step currently being processed
steps_completedArray of completed step names
messageHuman-readable status message

Output Object

When status is completed, the output object contains download URLs and an optional structured report:

FieldDescription
pdf_urlSigned URL to download PDF version (expires in 1 hour)
docx_urlSigned URL to download DOCX version (expires in 1 hour)
reportStructured 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.

FieldTypeDescription
schema_versionstringWire contract version. Currently "1.0". Consumers should branch on this to handle future changes.
sectionsarrayOrdered list of report sections.

Section Object

FieldTypeDescription
idstringStable identifier (e.g., sec_header, sec_biomechanics).
typestringSection 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).
titlestringHuman-readable section heading.
markdownstringSection content as self-contained markdown.
dataobject(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):

TypeTitleWhen emitteddata carries
biomechanics_alleged_injuries_and_healthAlleged Injuries & Health Considerationsalways
biomechanics_injury_mechanisms_introInjury Mechanisms & Tolerancesalways
biomechanics_injury_mechanism_<region>e.g., Cervical Spine, TBI, Kneeone per alleged injury
biomechanics_occupant_motionOccupant Motionalways
biomechanics_occupant_motion_analysisOccupant Motion Analysisalwaysmotion_tables
biomechanics_forces_introForces Exerted On <Name>if any forces resolved
biomechanics_forces_tbiTraumatic Brain Injuryif TBI alleged
biomechanics_forces_<region>e.g., Cervical Spine, Lumbar Spineone per non-TBI region with resolved forcesforces_tables (filtered to this region)
biomechanics_loading_considerationsLoading Considerationsalwayscharts (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:

  1. Poll every 5 seconds
  2. Set a maximum timeout of 10-15 minutes
  3. Stop polling when status is completed, failed, or cancelled
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');
}