Skip to main content
After a blood draw is confirmed and the lab processes the samples, results become available on the appointment. Results are retrieved via GET /api/v1/appointments/{id}/results and include clinical biomarker results, genetic biomarker results, and downloadable lab report documents.

Results status

The top-level status field on the results response indicates overall processing progress: Individual results within the response have their own per-result status (see below), so you can display partial results while the overall status is still in_progress. To learn when results change, subscribe to webhooks. appointment.results.updated fires on every change of the results body, and appointment.results.completed fires when the top-level status moves into completed. Home kit orders send the same events with the home_kit_order. prefix. Each event carries the results URL to fetch. You can still poll this endpoint to reconcile. The submissions array breaks the top-level status down per laboratory requisition. A draw that spans several tubes or several laboratories has several entries, each with the lab_code (tube barcode) it was filed under and its own in_progress / completed / fault status. A fault entry was rejected or abandoned at the lab and will never report: the sample was often moved to another laboratory, which then files its own entry. The top-level status is completed when every entry has closed and at least one reported. Every received result and every document carries the same lab_code, so you can tell which tube a value came from.

Clinical results

Clinical results represent standard blood biomarker measurements — things like Vitamin D levels, cholesterol, thyroid hormones, and so on. Each clinical result includes:
  • id — the lab result row identifier. It stays the same when the lab corrects that row, so use it to upsert. null for a planned result.
  • biomarker_name and category — what was measured and its grouping (e.g., Vitamins, Hormones).
  • lab_code — the lab code (tube barcode) the result was filed under, one of the order’s lab codes. null for a planned result and for results the lab reported without a code.
  • value and value_operator — the effective measurement. The operator is eq (equals) for exact values, or lt/gt when the lab reports a value below or above the measurable range.
  • unit — the measurement unit (e.g., ng/mL, µIU/mL).
  • reference_range_lab — normal and optimal min/max bounds as reported by the lab for this result, converted onto the same scale as value. A bound the lab leaves open is null, so a one-sided interval such as < 50 arrives as normal_min: null, normal_max: 50. Each result carries the interval it was measured against, so a patient with results across several draws or labs gets the right one per result.
  • reference_range_lab_absent_reason — why reference_range_lab is null. null when the range is present, and on a planned result. See When there is no lab range.
  • reference_range_aniva — Aniva-curated reference ranges adjusted for age, sex, and pregnancy status, on the same scale as value. null when your key lacks the aniva_reference_ranges_view scope, or when Aniva has no range of its own for this biomarker.
  • tested_at — the date the test was performed.
  • comment — free-text annotation from the lab on this specific result (e.g., reference-range guidance, sample-quality warnings). null when the lab did not attach one.
  • status_reason — why a fault or undeliverable result is unusable. null for every other status. Distinct from comment, which is the lab’s own note and is often empty on faulted results.
  • lab_reported — the performing lab’s own report line, verbatim and unconverted. See below.

Normalized values versus the lab’s own line

value, unit and reference_range_lab are normalized: we convert every measurement onto the biomarker’s canonical unit and report it under our own biomarker name, so the same analyte is comparable across labs and over time. That is what you want for trending and thresholds, and it is not what a reviewing doctor sees on the lab’s PDF — alkaline phosphatase printed as 48 U/l (40 - 129) reaches you as 0.8 µkat/L. lab_reported carries that printed line alongside it: the lab’s analyte name and code, the value and unit as printed, the reference interval both as text (< 50, > 55, 40 - 129) and as the lab’s own unconverted bounds, plus its abnormal flag, result status and note. Use lab_reported when a human is reconciling your data against the lab’s report, and the canonical fields for everything computed. Only fields the lab actually sent are populated; nothing in lab_reported is inferred, defaulted or converted, and the whole object is null when we hold no raw record for a result.

When there is no lab range

When reference_range_lab is null, reference_range_lab_absent_reason tells you why: Some labs print a target or a risk tier in place of a reference range, for example Ziel: <200. >240 hoch or <5.7 neg >=6.5 pos. These do not go into reference_range_lab, so its bounds are null and the reason is lab_text_only. Show the printed text verbatim. A one-sided range such as < 20 is a range. It arrives in reference_range_lab with one bound null, and the reason is null. Keep the open side open when you show it.

Clinical result statuses

When displaying results to patients, use the value field directly — it already reflects the corrected value when one exists.
Never present a fault result as a finding — its value is populated but confirmed incorrect.

Genetic results

Genetic results represent gene-level findings from DNA analysis. Each result corresponds to a gene (e.g., APOE, MTHFR) and includes:
  • id — the lab result row identifier. It stays the same when the lab corrects that row, so use it to upsert. null for a planned result.
  • gene — the gene symbol.
  • lab_code — the lab code (tube barcode) the result was filed under. null for a planned result and for results the lab reported without a code.
  • genotype — the detected genotype (e.g., E3/E4).
  • clinical_significance — an ACMG-based classification of the finding.
  • risk_description — a human-readable explanation of what the genotype means.
  • comment — free-text annotation from the lab on this specific result. null when the lab did not attach one.
  • status_reason — why a fault or undeliverable result is unusable. null for every other status.
  • variants — the specific genetic variants detected, each with SNV-level detail.

Clinical significance values

Genetic result statuses

Variant structure

Each genetic result contains a variants array. Each variant includes:
  • name — common name (e.g., APOE E4).
  • clinical_significance — variant-level significance (may differ from the gene-level significance).
  • conditions — associated diseases/conditions with descriptions.
  • snvs — the underlying single nucleotide variants, each with rs_id, allele, reference_allele, and optional HGVS notation.

Documents

The documents array in the results response contains references to downloadable lab report files. Each document has:
  • id — a file UUID. Pass this to GET /api/v1/documents/{id} to download the file.
  • file_name — the file name, the same one the download sends (e.g., 20260405 - Quade Befund - 7642001234.pdf).
  • original_file_name — the file name exactly as the lab or the uploader delivered it (e.g., 7642001234_B0012_FA_E.pdf).
  • origin — who delivered the file: lab, member_upload or staff_upload.
  • content_type — the media type of the file (e.g., application/pdf). Most reports are PDFs. Uploaded scans can be images or CSV files.
  • etag — the version of the file content; it changes whenever the bytes change. Store it and download again only when it changes. It is the standard HTTP entity tag, as used by S3 or Google Cloud Storage.
  • size_bytes — the file size in bytes.
  • lab_code — the lab code (tube barcode) the report belongs to; null when the lab filed none.
Access to documents is validated against the profile that owns the document.

Emailing results to the customer

You can tell the customer by email that their results are ready. Call Email Results for an appointment, or Email Results for a home kit order. Each call sends one email. Calls are not deduplicated.
  • The email goes to the email of the profile. A placeholder @aniva.email address is refused.
  • The email shows the name and the logo of the profile’s primary profile group.
  • The email is in German when the profile language is de, and in English for all other languages.
  • Set with_reports to true to attach the lab reports.
  • Add note to show a personal note in the email, at most 1000 characters.
If the profile has turned off transactional emails, the call returns 409 with the reason unsubscribed. To turn them on again, set transactional_emails_enabled to true with Update Profile.

Results-ready emails

You can get an email when the results of a booking are ready. Set results_ready_emails to up to 5 addresses on Create Appointment or Create Home Kit Order. Change the list later with Update Appointment or Update Home Kit Order; [] turns the emails off.
  • Each address gets one email, once every lab result of the booking is final.
  • An address added after the results are final gets the email right away.
  • The email links to the result page in the Aniva admin panel. The customer does not get it.
  • Your key needs results_view access to the patient. Without it, the request returns 403.

Example results response