API Reference
Complete reference for all Lab Reports API endpoints.
⚠️ Pre-Release
This page is currently under active development and is provided in a pre-release state.
Some content may evolve as we continue to iterate on the implementation based on your feedback, but the core concepts and functionality are expected to remain consistent.
The page will be marked as general release in the near future
Base URL: https://access.tryterra.co/api
All endpoints require the following headers:
dev-id
string
Yes
Your Terra developer ID
x-api-key
string
Yes
Your Terra API key
Upload a Lab Report
POST /v2/lab-reports
Upload a single lab report file for processing. The file is sent as multipart form data. Accepted formats: PDF, PNG, JPEG, GIF, WebP.
Headers
dev-id
Your developer ID
x-api-key
Your API key
Content-Type
multipart/form-data
Query Parameters
reference_id
string
No
Your external identifier for this report/patient
Request Body (multipart)
file
file
Yes
File to upload (PDF, PNG, JPEG, GIF, WebP; max 20 MB)
The form field name is file (singular). Currently only a single file per request is supported. Multi-file support is planned.
Example
Response — 202 Accepted
The upload returns an upload_id, not a session_id. A single upload can produce more than one report (e.g. a multi-report PDF), so the session id(s) aren't known at upload time. Learn the session_id(s) from the webhook events, or list them with GET /v2/lab-reports?upload_id=upl_4a2b8c1d. Every resulting session and webhook event carries the same upload_id. session_id still exists — it's the per-report identifier you use for GET /v2/lab-reports/{session_id} — it's just no longer the handle returned at upload.
The list is eventually consistent: immediately after upload the sessions may not all exist yet, so a GET ?upload_id=… right after the 202 can return an empty or partial list. Webhook events are the reliable signal that a report finished.
Error Responses
400
Missing file or unsupported format
401
Invalid or missing dev-id / x-api-key
413
File exceeds 20 MB
429
Rate limit exceeded
500
Internal server error
Error responses use RFC 7807 Problem JSON format:
List Sessions
GET /v2/lab-reports
List lab report sessions for your account, with optional filters.
Query Parameters
reference_id
string
No
Filter by your external reference ID
upload_id
string
No
Filter by the upload handle — returns every report from that upload
report_date_from
string
No
Lab report date lower bound (ISO-8601 YYYY-MM-DD)
report_date_to
string
No
Lab report date upper bound (ISO-8601 YYYY-MM-DD)
uploaded_at_from
string
No
Upload date lower bound (ISO-8601 YYYY-MM-DD)
uploaded_at_to
string
No
Upload date upper bound (ISO-8601 YYYY-MM-DD)
All date filters are inclusive and can be combined. For example, you can query "reports from January uploaded this week" by setting both report_date_from/report_date_to and uploaded_at_from/uploaded_at_to.
Results are capped at 500 per request. Use date range filters to narrow results for accounts with many sessions.
Example
Response — 200 OK
Retrieve a Session
GET /v2/lab-reports/{session_id}
Retrieve the report: metadata, results, reference ranges, and status history. This response is immutable and cacheable — it does not include presigned file URLs (which expire) or per-destination delivery state (which changes). Fetch those from the sub-resources below.
Path Parameters
session_id
string
Yes
The session's snowflake ID
Example
Response — 200 OK
See the Quick Start page for a complete example response. The session object is returned directly at the top level. Key fields include:
session_id— Snowflake ID (string)upload_id— The upload this report came from (string)reference_id— Your external reference (omitted if not set)current_status— Current status as a clean lowercase string (e.g."sent","processing")report_type— Report type (e.g."lab")report_date— Date from the lab report (YYYY-MM-DD string, omitted if not extracted)report_time— Time from the lab report (HH:MM 24-hour, omitted if not extracted)collection_date— Specimen collection date (YYYY-MM-DD string, omitted if not extracted)collection_time— Specimen collection time (HH:MM 24-hour, omitted if not extracted)uploaded_at— Session creation timestamp (ISO-8601)updated_at— Last update timestamp (ISO-8601)report_locale— Locale of the report (e.g."ja-JP","pt-BR")lab_name— Name of the laboratorypatient_sex— Clean lowercase string (e.g."male","female", omitted if unspecified)patient_age_at_collection— Patient age in years (integer, omitted if unknown)report_notes— Free-text notes extracted from the reportstatus_history[]— Array of{ status, timestamp, note }entriesresults_count— Number of biomarker resultsfile_count— Number of uploaded filesinput_bytes— Size of uploaded file(s) in bytesoutput_bytes— Size of output data in bytespanels[]— Report-level panels that results reference bypanel_id(omitted if the report has no panel grouping)results[]— Array of biomarker results, each grouped intosource/biomarker/measurement/interpretationlayers (see Core Concepts for field specs)
client_id is intentionally omitted from the API response. The API authenticates by client identity, so echoing it back is redundant.
Error Responses
401
Invalid or missing authentication
404
Session not found
500
Internal server error
List Deliveries
GET /v2/lab-reports/{session_id}/deliveries
The per-destination delivery state for a report. A report is fanned out to every destination that has opted in to lab reports (see Delivery Destinations), and each is tracked independently — so a failure to one never hides that the report reached the others. This is also why a session can be partially_sent. Delivery state is mutable (pending → delivered/failed/retrying), so it lives here rather than on the cacheable session.
Response — 200 OK
destination_id
string
The destination's ID
destination_type
string
The destination's type (e.g. webhook, s3)
status
string
pending, delivered, or failed
attempt_count
integer
Retry count — 0 on the first attempt, incremented per retry
last_error
string?
Most recent delivery error (omitted when delivered)
List Files
GET /v2/lab-reports/{session_id}/files
The uploaded input files and the report thumbnail, with freshly minted presigned download URLs. URLs are issued on demand because they expire — they are not embedded in the cacheable session or the webhook.
Response — 200 OK
expires_at applies to every URL in the response. Fetch the endpoint again to mint fresh URLs.
List Artifacts
GET /v2/lab-reports/{session_id}/artifacts
Intermediate processing artifacts, with freshly minted presigned URLs. Privileged keys only — standard keys receive a 404. Same response shape as /files (an artifacts[] array plus expires_at).
Reprocess a Session
POST /v2/lab-reports/{session_id}/reprocess
Re-run extraction and standardization on an existing session. Useful if a session failed or if you want to pick up improvements in the processing pipeline.
Path Parameters
session_id
string
Yes
The session's snowflake ID
Example
Response — 202 Accepted
Includes a Location header pointing to the session. The session re-enters the processing -> ... -> sent flow. A new webhook delivery will occur on completion.
Error Responses
401
Invalid or missing authentication
404
Session not found
500
Internal server error
Delete a Session
DELETE /v2/lab-reports/{session_id}
Soft-delete a session. The session is marked as deleted and a background process cleans up associated storage later.
Path Parameters
session_id
string
Yes
The session's snowflake ID
Example
Response — 204 No Content
No response body.
Error Responses
401
Invalid or missing authentication
404
Session not found
500
Internal server error
Last updated
Was this helpful?