Appointments
Add Individual Biomarkers
Add individual biomarkers to an appointment
PATCH
/
api
/
v1
/
appointments
/
{id}
/
biomarkers
curl --request PATCH \
--url https://anivahealth.com/api/v1/appointments/c9f3e2a1-7d6b-4c5e-b3a2-1f0e9d8c7b6a/biomarkers \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"biomarker_ids": [45, 43, 42]
}'
{
"success": true
}
{
"error": "LDL / HDL Ratio cannot be ordered cleanly with this set — add LDL Cholesterol / HDL Cholesterol (see validation.failures for every option).",
"message": "LDL / HDL Ratio cannot be ordered cleanly with this set — add LDL Cholesterol / HDL Cholesterol (see validation.failures for every option).",
"validation": {
"failures": [
{
"biomarker_ids": [45],
"fix_options": [
{
"add_biomarkers": [
{ "id": 43, "name": "LDL Cholesterol" },
{ "id": 42, "name": "HDL Cholesterol" }
]
}
]
}
]
}
}
{
"error": "biomarker_not_priced",
"message": "Adrenaline, 24h urine carries no price in the catalog you can order from — GET /api/v1/biomarkers/1465 reports price_cents: null for it, and a marker with no price cannot be ordered.",
"biomarker_id": 1465
}
{
"error": "biomarker_requires_position",
"message": "Mean Platelet Volume (MPV) is not sold on its own — it comes only inside a position. Add every biomarker of one of the positions listed here; the cheapest is \"Blood Count (extended)\".",
"biomarker_id": 77,
"positions": [
{
"id": "3f1a8c22-64bd-4e57-8a90-1c2d3e4f5a6b",
"name": "Blood Count (extended)",
"price_cents": 460,
"retail_cents": 529
}
]
}
{
"error": "Some individual biomarkers need more than 150 µl of one lab's microtainer. Remove some of them."
}
{
"error": "Individual biomarkers cannot be modified after blood draw has been confirmed"
}
{
"error": "Biomarkers not found: 99999"
}
Add one or more individual biomarkers to an appointment as à-la-carte tests, outside any
pre-built panel. Routing decisions are made downstream by the order-logistics pipeline based
on each biomarker’s per-lab mappings.
The resulting set is validated synchronously against each lab’s closure rules. Some biomarkers
can only be obtained by ordering them together with others — an LDL/HDL ratio, for example, is
delivered by an order that also returns LDL and HDL, so requesting the ratio on its own isn’t a
coherent order. (Those same components are what the ratio is priced as — see the
Reading a
Every closure-validation and catalog
pricing
breakdown on List Biomarkers.) When the set can’t be cleanly
ordered, the call returns 400 with a validation payload and nothing is persisted, so you
can trial-and-error your way to a valid set:
PATCH … { "biomarker_ids": [45] }(LDL/HDL ratio alone) →400;validation.failures[].fix_options[].add_biomarkerslists LDL and HDL.PATCH … { "biomarker_ids": [45, 43, 42] }(ratio + LDL + HDL) →200.
400 with error: "biomarker_requires_position" and the positions that sell it. See
Pricing.
List Biomarkers quotes some biomarkers as price_cents: null.
These biomarkers are uncurated: Aniva has not priced them. If you add one, the call returns
400 with error: "biomarker_not_priced". No other biomarker fixes this failure, because null
means that you have no price to buy the marker at. Kit markers are the exception. Attach the panel
that is the kit, and its members come with the panel. The call also accepts a member that you add to
an appointment that already carries that panel.
At a pharmacy location (capillary draw), the draw is packed into microtainers. Panels go first, in
booking order. Individual biomarkers then fill the free space of the panel microtainers. When they
need more space, they open new microtainers. We spread them evenly. Each microtainer holds at most
150 µl of individual biomarkers. A biomarker that needs more than 150 µl in a microtainer of its own never
fits, and neither does a biomarker without a curated volume. If the resulting set breaks this rule,
the call returns 400 with an error message and nothing is persisted.
This call is idempotent: biomarkers already on the appointment are kept.
Requires the test_sessions_schedule and schedule_appointments_with_individual_biomarker_selection scopes.
Biomarkers cannot be added after you call Confirm Blood
Draw. Attempting to modify a confirmed appointment returns
409 Conflict.Request
Path parameters
string
required
Unique identifier (UUID) of the appointment.
Body parameters
number[]
required
Array of biomarker IDs to attach. Must contain at least one item. Use List
Biomarkers to discover IDs.
Response
On success, the API returns200 OK confirming the biomarkers have been attached.
boolean
true when the biomarkers have been successfully added to the appointment.Reading a 400
Every closure-validation and catalog 400 carries a sentence in message. A body-validation or
microtainer 400 carries error alone. Branch on error. On a
closure-validation failure, error is a sentence. On a catalog failure, error is a machine code:
biomarker_not_priced or biomarker_requires_position.
Closure validation — the set can’t be cleanly ordered at any lab:
validation.failures[]— present only on closure-validation failures (absent on plain body-validation errors). One entry per biomarker group no lab can serve, carrying thebiomarker_idsthat failed andfix_options[], smallest option first. Each option’sadd_biomarkers({ id, name }) is a set you add ALL of — keep your originals, add those, and retry. An emptyfix_optionsmeans adding nothing will help; remove the biomarker instead.
price_cents: null, or omits it when it
is a ratio with an unpriced component. error is biomarker_not_priced, and biomarker_id names
the marker. No other biomarker fixes this failure, so remove the biomarker. Two conditions make a
biomarker orderable: a lab Aniva can route to must deliver it, and the Aniva catalog must price
it.
Gated biomarker — a biomarker sold only inside a position, with no covering position in the
set. error is biomarker_requires_position, biomarker_id names the marker, and positions[]
lists every position that sells it, cheapest first. Add every biomarker of one of them. See
Pricing for the model, and pricing.positions on Get
Biomarker for a position’s full member set.
Error responses
| Status | Description |
|---|---|
400 | biomarker_ids empty / non-integer, the set fails closure validation (validation.failures), it adds a biomarker Aniva has not priced (error: "biomarker_not_priced"), it holds a gated biomarker no position in the set covers (error: "biomarker_requires_position"), or, at a pharmacy, the biomarkers do not fit in their microtainers. |
403 | Forbidden — missing scope or appointment outside access context. |
404 | Appointment or one or more biomarkers not found. |
409 | Conflict — biomarkers cannot be modified after the blood draw has been confirmed. |
500 | Internal server error. |
curl --request PATCH \
--url https://anivahealth.com/api/v1/appointments/c9f3e2a1-7d6b-4c5e-b3a2-1f0e9d8c7b6a/biomarkers \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"biomarker_ids": [45, 43, 42]
}'
{
"success": true
}
{
"error": "LDL / HDL Ratio cannot be ordered cleanly with this set — add LDL Cholesterol / HDL Cholesterol (see validation.failures for every option).",
"message": "LDL / HDL Ratio cannot be ordered cleanly with this set — add LDL Cholesterol / HDL Cholesterol (see validation.failures for every option).",
"validation": {
"failures": [
{
"biomarker_ids": [45],
"fix_options": [
{
"add_biomarkers": [
{ "id": 43, "name": "LDL Cholesterol" },
{ "id": 42, "name": "HDL Cholesterol" }
]
}
]
}
]
}
}
{
"error": "biomarker_not_priced",
"message": "Adrenaline, 24h urine carries no price in the catalog you can order from — GET /api/v1/biomarkers/1465 reports price_cents: null for it, and a marker with no price cannot be ordered.",
"biomarker_id": 1465
}
{
"error": "biomarker_requires_position",
"message": "Mean Platelet Volume (MPV) is not sold on its own — it comes only inside a position. Add every biomarker of one of the positions listed here; the cheapest is \"Blood Count (extended)\".",
"biomarker_id": 77,
"positions": [
{
"id": "3f1a8c22-64bd-4e57-8a90-1c2d3e4f5a6b",
"name": "Blood Count (extended)",
"price_cents": 460,
"retail_cents": 529
}
]
}
{
"error": "Some individual biomarkers need more than 150 µl of one lab's microtainer. Remove some of them."
}
{
"error": "Individual biomarkers cannot be modified after blood draw has been confirmed"
}
{
"error": "Biomarkers not found: 99999"
}
⌘I
curl --request PATCH \
--url https://anivahealth.com/api/v1/appointments/c9f3e2a1-7d6b-4c5e-b3a2-1f0e9d8c7b6a/biomarkers \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"biomarker_ids": [45, 43, 42]
}'
{
"success": true
}
{
"error": "LDL / HDL Ratio cannot be ordered cleanly with this set — add LDL Cholesterol / HDL Cholesterol (see validation.failures for every option).",
"message": "LDL / HDL Ratio cannot be ordered cleanly with this set — add LDL Cholesterol / HDL Cholesterol (see validation.failures for every option).",
"validation": {
"failures": [
{
"biomarker_ids": [45],
"fix_options": [
{
"add_biomarkers": [
{ "id": 43, "name": "LDL Cholesterol" },
{ "id": 42, "name": "HDL Cholesterol" }
]
}
]
}
]
}
}
{
"error": "biomarker_not_priced",
"message": "Adrenaline, 24h urine carries no price in the catalog you can order from — GET /api/v1/biomarkers/1465 reports price_cents: null for it, and a marker with no price cannot be ordered.",
"biomarker_id": 1465
}
{
"error": "biomarker_requires_position",
"message": "Mean Platelet Volume (MPV) is not sold on its own — it comes only inside a position. Add every biomarker of one of the positions listed here; the cheapest is \"Blood Count (extended)\".",
"biomarker_id": 77,
"positions": [
{
"id": "3f1a8c22-64bd-4e57-8a90-1c2d3e4f5a6b",
"name": "Blood Count (extended)",
"price_cents": 460,
"retail_cents": 529
}
]
}
{
"error": "Some individual biomarkers need more than 150 µl of one lab's microtainer. Remove some of them."
}
{
"error": "Individual biomarkers cannot be modified after blood draw has been confirmed"
}
{
"error": "Biomarkers not found: 99999"
}