For the complete documentation index, see llms.txt. This page is also available as Markdown.

Webhook Payload

Webhook delivery format, complete payload example, and field specifications.

⚠️ 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

How Webhooks Work

When a lab report session reaches the sent status, Terra delivers an event to your configured webhook destination.

  1. Configure your webhook URL in the Terra Dashboard

  2. Upload a lab report via the API

  3. Terra processes the report and delivers the event via HTTP POST to your endpoint

  4. Your endpoint should return a 2xx status code to acknowledge receipt

Every webhook is an event envelope: event metadata (type, event_id, occurred_at, upload_id) wraps the report itself under data:

{
  "type": "lab_report.completed",
  "event_id": "evt_9f1c…",
  "occurred_at": "2026-03-28T14:23:45Z",
  "upload_id": "upl_4a2b…",
  "data": { "session_id": "", "panels": [  ], "results": [  ] }
}
  • type is the event: lab_report.completed (success) or lab_report.failed (failure). Branch on it in your handler.

  • event_id is unique per event — use it to deduplicate redeliveries.

  • upload_id ties every event from the same upload together (one upload can yield more than one report).

  • data is a focused event resource — the report's identity, metadata, panels, and standardized results. It is not the full session. For the full session (status history, file/byte counts, presigned files) call GET /v2/lab-reports/{session_id} and its sub-resources.

Result shape. Each result is grouped into four layers: source (what the report literally printed), biomarker (the normalized identity, key is null when unmatched), measurement (one typed value — check measurement.type and read the matching field), and interpretation (the coded abnormality signal). This is identical between the webhook payload and the GET response, so a result parses the same either way.

The webhook does not embed presigned file URLs or per-destination delivery state — those expire / change after delivery. Fetch them on demand from the sub-resources (/files, /deliveries) using the session_id from data.

Terra-Reference Header

Webhook requests include a Terra-Reference header containing the session ID. You can use this for logging and deduplication before parsing the body.

Complete Payload Example

Failure Webhook

If a report can't be processed — an unreadable file, an extraction or standardization error, or an internal error — Terra delivers a failure webhook to the same destination(s) instead of the success payload, so you're notified rather than left waiting.

Success and failure are distinguished by the envelope type: lab_report.completed for a completed report, lab_report.failed for a failure. Branch on type in your handler. A failure event is the same envelope; its data carries the session_id, reference_id, and an error object.

Error Object

Field
Type
Description

code

string

Machine-readable failure code (see below)

message

string

Human-readable description of the failure

retriable

boolean

true if re-uploading the same report may succeed; false if it won't

Error Codes

Code
Retriable
Meaning

file_unreadable

false

The uploaded file couldn't be read — upload a clearer scan/export.

extraction_failed

true

Biomarker extraction failed; a retry may succeed.

standardization_failed

true

Biomarker standardization failed; a retry may succeed.

internal

true

An internal error occurred; a retry may succeed.

Field Specifications

Event Envelope

Field
Type
Description

type

string

lab_report.completed or lab_report.failed

event_id

string

Unique per event — deduplicate redeliveries on it

occurred_at

string

When the event occurred (ISO-8601, UTC)

upload_id

string

Correlates every event produced from the same upload

data

object

The report resource — the Data Object below (completed) or an error (failed)

Data Object

The data of a lab_report.completed event. This is a focused event resource — the report's identity, metadata, and standardized resultsnot the full session. For the full session (status history, file/byte counts, presigned files) call GET /v2/lab-reports/{session_id} and its sub-resources. Input files, the thumbnail, and per-destination delivery state are not here — fetch them on demand from the /files, /artifacts, and /deliveries sub-resources.

Field
Type
Description

session_id

string

Snowflake int64 ID (as string) — always present

reference_id

string

Your external identifier (from upload query param)

report_date

string

Date from the lab report (YYYY-MM-DD)

report_time

string

Time from the lab report (HH:MM 24-hour)

report_locale

string

Locale of the report (e.g. "ja-JP", "pt-BR")

results_count

integer

Number of biomarker results — always present

panels

object[]

Report-level panels that results reference by panel_id (omitted if the report has no panel grouping)

results

object[]

Array of biomarker result objects — always present

report_notes

string

Free-text notes extracted from the report

client_id is intentionally omitted from the API response. The API authenticates by client identity, so echoing it back is redundant.

Panel Object

A panel is a report-level grouping (e.g. "CBC"). Results point at a panel by panel_id; join on it to group results under their panel.

Field
Type
Description

id

integer

Report-local panel id — matches biomarker.panel_id on results

name

string

Verbatim panel header as printed on the report

key

string?

Normalized dedup key for the panel (a stable text key, not a LOINC code)

Result Object

Each result is grouped into four layers plus its reference ranges. source is what the report literally printed, biomarker is the normalized identity, measurement is the single typed value, and interpretation is the coded abnormality signal.

source — verbatim provenance:

Field
Type
Description

name

string

Exact biomarker name as printed on the report

panel

string?

Verbatim panel header (omitted if the result isn't panelled)

value

string?

The value exactly as printed on the report

units

string?

Units as printed on the report

flag

string?

Raw flag text as printed (e.g. "H", "↑", "Négatif")

method

string?

Lab methodology (e.g. "ECLIA")

notes

string?

Additional lab comments

reference_text

string?

Human-readable reconstruction of the printed reference range

collection_date

string?

Specimen collection date for this result (YYYY-MM-DD)

collection_time

string?

Specimen collection time for this result (HH:MM 24-hour)

biomarker — normalized identity:

Field
Type
Description

key

string | null

Canonical biomarker slug; always present, null when unmatched

display_name

string?

Standardized English name

loinc_code

string?

LOINC code (omitted if not mapped)

panel_id

integer?

References the panels[].id this result belongs to

panel_key

string?

Normalized panel key (see Panel Object)

specimen

string?

"blood", "serum", "plasma", "urine", "saliva", "stool", or "other"

measurement — one typed value. Read type, then the matching field:

Field
Type
Description

type

string

"numeric", "bounded", "qualitative", "text", or "absent"

numeric

number?

Present when type is "numeric"

bounded

object?

Present when type is "bounded": { "operator": "lt"|"gt", "value": number }

qualitative

object?

Present when type is "qualitative": { "text": string }

text

string?

Present when type is "text"

absent_reason

string?

Present when type is "absent" (why the value is missing; may be null)

units

string?

Units of the numeric/bounded magnitude

ucum_code

string?

UCUM-compliant unit code

interpretation — the coded "is this abnormal" signal:

Field
Type
Description

flag

string | null

Coded abnormality (see Interpretation Flag enum); null when there is no signal

flag_raw

string?

The lab's verbatim flag text the code was derived from

source

string

Where the flag came from: "report" (normalized from the printed flag) or "none"

applied_range

object?

The reference range the flag was judged against: { "lower": number?, "upper": number? } — populated when exactly one range applies to the patient

Field
Type
Description

reference_ranges

object[]

Array of reference range objects

Reference Range Object

Field
Type
Description

lower

number?

Lower limit (omitted for one-sided ranges like "< 4.0")

upper

number?

Upper limit (omitted for one-sided ranges like "> 1.0")

type

string

Range-type label — see enum values below

context

object?

Demographic context for when this range applies

Context Object

Field
Type
Description

sex

string

"male", "female", or omitted (applies to all)

age_lower

integer?

Minimum age in years this range applies to

age_upper

integer?

Maximum age in years (omitted = no upper limit)

pregnancy_status

object?

Pregnancy status: "trimester_1", "trimester_2", or "trimester_3" (or omitted)

cycle_phase

object?

Menstrual cycle phase (or omitted)

gestational_week_lower

integer?

Lower gestational week bound (omitted if not applicable)

gestational_week_upper

integer?

Upper gestational week bound (omitted if not applicable)

reference_population

object?

Reference population (e.g. "nhanes_iii", "young_adult"; omitted if not set)

modifiers

string[]

Additional qualifiers (e.g. ["fasting", "supine"])

Enum Reference

Status

Value
Description

processing

Extracting biomarker data from the file

processed

Extraction complete

standardizing

Matching biomarkers and normalizing units

standardized

Standardization complete, results queryable

sending

Delivering payload to webhook

sent

Delivered to all configured destinations

partially_sent

Delivered to at least one destination, but failed others

failed

An error occurred

retry_scheduled

Retry has been scheduled

retrying

Retry in progress

cancelled

Processing was cancelled

deleted

Session has been deleted

Reference Range Type

Values for reference_ranges[].type — the label describes the range, not a verdict on the result.

Value
Description

normal

Within normal range

low

Below normal

high

Above normal

borderline_low

Borderline low

borderline_high

Borderline high

critical_low

Critically low

critical_high

Critically high

abnormal

Abnormal

therapeutic

Within therapeutic range

subtherapeutic

Below therapeutic range

toxic

Toxic level

Interpretation Flag

Values for interpretation.flag — the coded per-result abnormality signal (null when there is no signal).

Value
Description

normal

Not flagged

low

Below range

high

Above range

borderline_low

Borderline low

borderline_high

Borderline high

critical_low

Critically low

critical_high

Critically high

abnormal

Flagged, direction unspecified

Specimen Type

Value
Description

blood

Blood

serum

Serum

plasma

Plasma

urine

Urine

saliva

Saliva

stool

Stool

other

Other

Result Type

Value
Description

numeric

Numeric measurement

qualitative

Categorical outcome

text

Free-text observation

Biological Sex

Value
Description

male

Male

female

Female

Report Type

Value
Description

lab

Lab report

Last updated

Was this helpful?