Lab Report API Documentation
Overview
The Blood Biomarkers API provides an advanced medical report parsing service that converts unstructured lab reports (PDFs, images) into structured, actionable biomarker data. Using state-of-the-art AI models, the API extracts, standardizes, and delivers comprehensive biomarker information with high accuracy.
Key Features
Multi-format Support: Accepts PDF, PNG, and JPEG files
Batch Processing: Upload multiple files in a single request
Real-time Status Tracking: Monitor processing progress through status updates
Standardized Output: Biomarkers mapped to standard enums with UCUM-compliant units
Flexible Delivery: Asynchronous webhook delivery or synchronous response
Reference Tracking: Associate uploads with your internal user/patient IDs
Quick Start
Basic Upload Request
curl --location 'https://api.tryterra.co/v2/lab-reports' \
--header 'dev-id: YOUR_DEV_ID' \
--header 'x-api-key: YOUR_API_KEY' \
--form 'files=@"/path/to/bloodtest.pdf"' \
--form 'files=@"/path/to/labresult.jpg"'
With Reference ID
curl --location 'https://api.tryterra.co/v2/lab-reports?reference_id=patient_123' \
--header 'dev-id: YOUR_DEV_ID' \
--header 'x-api-key: YOUR_API_KEY' \
--form 'files=@"/path/to/report.pdf"'
API Reference
Upload Endpoint
POST
https://api.tryterra.co/v2/lab-reports
Alternative: POST /v2/reports/upload
(legacy)
Headers
dev-id
string
✅
Your Terra developer ID
x-api-key
string
✅
Your Terra API key
Content-Type
string
✅
Must be multipart/form-data
Query Parameters
reference_id
string
null
Your internal identifier for the patient/user. This ID will be included in the webhook payload for easy reconciliation
Request Body
files
file[]
✅
One or more files to upload. Supported formats: PDF, PNG, JPEG. Maximum 10 files per request
Response Codes
202 Accepted
Upload successful, processing started asynchronously
200 OK
Upload and processing successful (sync mode only)
400 Bad Request
Invalid request (missing files, wrong format)
500 Internal Server Error
Processing error
Success Response (202/200)
{
"upload_id": "550e8400-e29b-41d4-a716-446655440000",
"files": [
{"file_name": "blood_test_2024.pdf"},
{"file_name": "lab_results.jpg"}
]
}
Error Response (400/500)
{
"type": "about:blank",
"title": "Invalid File Format",
"status": 400,
"detail": "File format must be PDF, PNG, or JPEG.",
"instance": "/lab-reports"
}
Processing Pipeline
Status Progression
The system tracks detailed status updates throughout processing:
RECEIVED → QUEUED → DEQUEUED → PROCESSING → PROCESSED →
STANDARDIZING → STANDARDIZED → QUEUED (delivery) → SENT → COMPLETED
received
Files uploaded and stored
No
queued
Task queued for processing
No
dequeued
Processing started
No
processing
Extracting biomarker data from files
No
processed
Extraction complete
No
standardizing
Mapping to standard biomarker enums and units
No
standardized
Standardization complete
No
sent
Results delivered to webhook
Yes ✅
failed
Error occurred during processing
Yes ❌
Status updates above are only used internally in https://dashboard.tryterra.co/dashboard/lab-reports at the moment.
Webhook Payload
Structure
The parsed results are delivered to your configured webhook endpoint:
{
"upload_id": "550e8400-e29b-41d4-a716-446655440000",
"reference_id": "patient_123", // If provided in upload
"data": [{
"date": "2024-03-15",
"time": "09:30",
"results": [
{
"original_name": "Haemoglobin",
"display_name": "Hemoglobin",
"biomarker": "hemoglobin_blood",
"type": "blood",
"value": 14.2,
"value_bound": null,
"display_units": "g/dL",
"unit_code": "g/dL",
"units": {
"id": 123,
"name": "grams_per_deciliter",
"numerator": "g",
"denominator": "dL",
"exponent": null
},
"reference_ranges": [
{
"reference_lower_bound": 13.5,
"reference_upper_bound": 17.5,
"classification": "normal",
"context": {
"sex": "male",
"age_lower_bound": 18,
"age_upper_bound": null,
"modifiers": []
}
}
],
"notes": null
}
]
}]
}
Field Specifications
Top Level Fields
upload_id
string
Unique identifier for this upload
reference_id
string?
Your reference ID if provided
data
array
Array of report data (usually one item)
LabData Object
date
string?
ISO date (YYYY-MM-DD) when specimen was collected
time
string?
Time in HH:MM format (24-hour)
results
Result[]
Array of biomarker measurements
Result Object
original_name
string?
Exact name from the report
display_name
string?
Standardized English name
biomarker
string?
Standardized biomarker enum (see list below)
type
string
Specimen type: "blood" or "urine"
value
number?
Numeric measurement value
value_bound
object?
Boundaries for non-exact values
display_units
string?
Units as shown on report
unit_code
string?
UCUM-compliant unit code
reference_ranges
ReferenceRange[]
Normal/abnormal ranges
notes
string?
Additional lab comments
Value Bound Object
Used when exact values aren't available (e.g., ">5" or "<0.1"):
greater_than
number?
Lower bound when result is ">X"
less_than
number?
Upper bound when result is "<Y"
Units Object
Structured breakdown of measurement units:
id
number?
Internal unit identifier
name
string?
Internal unit name
numerator
string?
Top unit (e.g., "mg", "mmol")
denominator
string?
Bottom unit (e.g., "L", "dL")
exponent
number?
Power of 10 multiplier (e.g., 9 for 10^9/L)
Reference Range Object
reference_lower_bound
number?
Lower normal limit
reference_upper_bound
number?
Upper normal limit
classification
string
"normal", "abnormal", or "borderline"
context
Context
Demographic context for this range
Context Object
sex
string?
"male", "female", or "other"
age_lower_bound
number?
Minimum age in years
age_upper_bound
number?
Maximum age in years
modifiers
Modifier[]
Additional qualifiers
Modifier Object
dimension
string?
Qualifier type (e.g., "menstrual_phase")
value
any?
Associated value
Handling Edge Cases
Missing Data
The API gracefully handles missing or unclear data:
Missing dates/times: Fields set to
null
Unclear values:
value
field set tonull
, may usevalue_bound
Missing units: All unit fields
null
,display_units
= "Unknown"One-sided ranges: One bound is
null
(e.g.,reference_upper_bound: null
means ">X")
UTF-8 Encoding
⚠️ Important: Always use UTF-8 encoding when parsing responses. The data may contain:
Scientific symbols (µ, ×, ±)
Superscript/subscript notation
International characters in names
Error Handling
Implement retry logic for failed webhooks:
// Example webhook handler
app.post('/webhook/lab-reports', async (req, res) => {
try {
const data = req.body;
// Verify upload_id exists
if (!data.upload_id) {
return res.status(400).send('Missing upload_id');
}
// Process biomarker data
for (const report of data.data) {
await processBiomarkers(report.results);
}
res.status(200).send('OK');
} catch (error) {
console.error('Webhook processing error:', error);
res.status(500).send('Processing failed');
// Terra will retry on 5xx responses
}
});
Biomarker Enums
The system standardizes biomarkers to consistent enum values. Common examples:
Hemoglobin
hemoglobin_blood
g/dL
Glucose
glucose_blood
mg/dL or mmol/L
Cholesterol Total
cholesterol_total
mg/dL
Vitamin D
vitamin_d_25_oh
ng/mL
TSH
thyroid_stimulating_hormone
mIU/L
White Blood Cell Count
white_blood_cell_count
10^9/L
For the complete list of biomarker enums, refer to the supplementary files:
biomarkers.json - Full biomarker enum list
Best Practices
1. Implement Idempotency
Track upload_id
to prevent duplicate processing:
def handle_webhook(data):
upload_id = data['upload_id']
if already_processed(upload_id):
return # Skip duplicate
process_results(data)
mark_as_processed(upload_id)
2. Handle Partial Data
Not all biomarkers/units may be successfully extracted:
for result in data['results']:
if result.get('biomarker') == 'unknown':
# Log for manual review
log_unknown_biomarker(result)
else:
process_biomarker(result)
3. Validate Reference Ranges
Consider patient context when interpreting results:
def is_abnormal(result, patient_age, patient_sex):
for range_obj in result['reference_ranges']:
context = range_obj['context']
# Check if range applies to patient
if (context['sex'] == patient_sex and
context['age_lower_bound'] <= patient_age <= context['age_upper_bound']):
# Check if value is within range
lower = range_obj['reference_lower_bound']
upper = range_obj['reference_upper_bound']
if lower and result['value'] < lower:
return True
if upper and result['value'] > upper:
return True
return False
Rate Limits & Quotas
File Size: Maximum 10MB per file
Files per Request: Maximum 10 files
Processing Time: Typically 1-10 minutes per report, based on size
Support
For technical support or questions:
Email: [email protected]
Documentation: docs.tryterra.co
Changelog
v2 (Current)
Enhanced biomarker standardization
UCUM-compliant unit codes (in Beta)
Improved reference range context
Status tracking system
Multi-file batch processing
v1 (Deprecated)
Basic PDF parsing
Limited biomarker recognition
Last updated
Was this helpful?