> For the complete documentation index, see [llms.txt](https://docs.tryterra.co/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.tryterra.co/lab-reports/best-practices.md).

# Best Practices

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

## Idempotency via Event ID

Every webhook event carries a unique `event_id`. Use it to deduplicate webhook deliveries and prevent processing the same event twice.

```python
def handle_webhook(event):
    event_id = event["event_id"]

    if already_processed(event_id):
        return 200  # Acknowledge but skip

    process_results(event["data"]["results"])
    mark_processed(event_id)
    return 200
```

{% hint style="info" %}
`event_id` is stable across redeliveries of the same event, so it's the right dedup key. If you reprocess a session (`POST /v2/lab-reports/{session_id}/reprocess`), that emits a **new** event with a new `event_id` — intentional reprocessing is a distinct event, not a duplicate.
{% endhint %}

## Handling Unmatched Biomarkers

When `biomarker.key` is `null`, the system could not confidently match the extracted name to a known biomarker. This is normal for uncommon or lab-specific tests.

```python
for result in session["results"]:
    biomarker_key = result["biomarker"]["key"]
    source_name = result["source"]["name"]

    if biomarker_key is not None:
        # Matched — use the canonical key for aggregation
        store_by_biomarker(biomarker_key, result)
    else:
        # Unmatched — fall back to the name printed on the report
        store_by_name(source_name, result)
        log_unmatched(source_name)
```

{% hint style="warning" %}
Do not discard results where `biomarker.key` is `null`. They may contain clinically relevant data. Display them using `source.name` and flag them for manual review if needed.
{% endhint %}

## Reference Range Interpretation

Reference ranges include demographic context. Match the range to your patient's demographics before interpreting:

```python
def find_applicable_range(result, patient_sex, patient_age):
    """Find the reference range that best matches the patient."""
    for ref_range in result["reference_ranges"]:
        ctx = ref_range.get("context", {})

        # Check sex (absent context means applies to all)
        if "sex" in ctx and ctx["sex"] != patient_sex:
            continue

        # Check age bounds
        if "age_lower" in ctx and patient_age < ctx["age_lower"]:
            continue
        if "age_upper" in ctx and patient_age > ctx["age_upper"]:
            continue

        return ref_range

    return None  # No applicable range found


def interpret_result(result, patient_sex, patient_age):
    """Determine if a numeric result is within range."""
    ref = find_applicable_range(result, patient_sex, patient_age)

    if ref is None:
        return "no_range"

    measurement = result["measurement"]
    if measurement["type"] == "numeric":
        value = measurement["numeric"]
    elif measurement["type"] == "bounded":
        # Conservative: treat "<X"/">X" as the bound X
        value = measurement["bounded"]["value"]
    else:
        return "no_value"  # qualitative / text / absent

    if "lower" in ref and value < ref["lower"]:
        return "low"
    if "upper" in ref and value > ref["upper"]:
        return "high"

    return "normal"
```

{% hint style="info" %}
Terra also surfaces the lab's own verdict directly in `interpretation.flag` (a coded value like `"high"` / `"low"` / `null`) with the raw printed flag in `interpretation.flag_raw`. Use that when you want the report's flag rather than computing your own from the ranges above.
{% endhint %}

{% hint style="info" %}
Some results have multiple reference ranges for different demographics (e.g., separate ranges for males and females, or for pregnant vs. non-pregnant patients). Always filter by context before interpreting.
{% endhint %}

## Webhook Error Handling

Your webhook endpoint should:

1. **Return 2xx quickly** — Do heavy processing asynchronously after acknowledging
2. **Be idempotent** — The same payload may be delivered more than once
3. **Handle retries** — If your endpoint returns a non-2xx status, Terra will retry delivery

```javascript
app.post('/webhook/lab-reports', async (req, res) => {
  const session = req.body;

  // Acknowledge immediately
  res.status(200).send('OK');

  // Process asynchronously
  try {
    await processLabReport(session);
  } catch (error) {
    console.error(`Failed to process session ${session.session_id}:`, error);
    // The data is still available via GET /v2/lab-reports/{session_id}
  }
});
```

{% hint style="warning" %}
If your webhook is consistently failing, use the REST API (`GET /v2/lab-reports`) to retrieve sessions you may have missed. Sessions remain available regardless of webhook delivery status.
{% endhint %}

## Choosing Delivery Destinations

Webhooks aren't the only delivery option — lab reports can also be delivered to cloud storage, message queues, and databases. Delivery is opt-in per destination: a destination receives reports only if its `destination_event_types` allow-list includes `"lab_report"`. Each destination's outcome is tracked independently, so one failing never fails the others (the session ends `partially_sent` in that case). Inspect per-destination state via the `/deliveries` sub-resource. See [Delivery Destinations](/lab-reports/core-concepts.md#delivery-destinations) for the full mechanism.

## UTF-8 Encoding

Lab report data may contain:

* Scientific symbols: `µ`, `×`, `±`
* Superscript notation: `10^9`, `10^12`
* International characters in biomarker names and notes
* Special units: `µmol/L`, `µg/dL`

Always parse response bodies as UTF-8. Most HTTP clients do this by default, but verify your JSON parser handles multibyte characters correctly.

## Rate Limits

The Lab Reports API enforces rate limits to ensure fair usage:

| Constraint        | Limit              |
| ----------------- | ------------------ |
| File size         | 20 MB per file     |
| Files per request | 1 (single file)    |
| Processing time   | Typically 30s–3min |

{% hint style="info" %}
If you need to upload many reports in bulk, space your requests and use `reference_id` to track which reports belong to which patient.
{% endhint %}

## Storing Session IDs

Session IDs are snowflake int64 values serialized as JSON strings (e.g., `"297405620317847552"`). Store them as strings, not integers:

```python
# Correct
session_id = response["session_id"]  # "297405620317847552"
db.store(session_id=session_id)

# Incorrect — may lose precision in JavaScript
session_id = int(response["session_id"])  # Risk of precision loss
```

## Polling vs. Webhooks

| Approach | Pros                             | Cons                                  |
| -------- | -------------------------------- | ------------------------------------- |
| Webhook  | Real-time, no wasted requests    | Requires public endpoint, retry logic |
| Polling  | Simple, no infrastructure needed | Wastes requests, adds latency         |

**Recommendation:** Use webhooks for production. Use polling only during development or as a fallback when your webhook endpoint is unavailable.

If you must poll, check no more frequently than every 5 seconds:

```bash
# Poll until status is terminal
while true; do
  STATUS=$(curl -s "https://access.tryterra.co/api/v2/lab-reports/$SESSION_ID" \
    -H "dev-id: $DEV_ID" \
    -H "x-api-key: $API_KEY" | jq -r '.current_status')

  echo "Status: $STATUS"

  if [ "$STATUS" = "sent" ] || [ "$STATUS" = "failed" ]; then
    break
  fi

  sleep 5
done
```
