> ## Documentation Index
> Fetch the complete documentation index at: https://docs.anivahealth.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Add Individual Biomarkers

> Add individual biomarkers to an appointment

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 `pricing`
breakdown on [List Biomarkers](/api/biomarkers/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:

1. `PATCH … { "biomarker_ids": [45] }` (LDL/HDL ratio alone) → `400`;
   `validation.failures[].candidates[].missing_biomarkers` lists LDL and HDL.
2. `PATCH … { "biomarker_ids": [45, 43, 42] }` (ratio + LDL + HDL) → `200`.

This call is idempotent: biomarkers already on the appointment are kept.

<Warning>
  Biomarkers cannot be added after you call [Confirm Blood
  Draw](/api/appointments/confirm-blood-draw). Attempting to modify a confirmed appointment returns
  `409 Conflict`.
</Warning>

## Request

### Path parameters

<ParamField path="id" type="string" required>
  Unique identifier (UUID) of the appointment.
</ParamField>

### Body parameters

<ParamField body="biomarker_ids" type="number[]" required>
  Array of biomarker IDs to attach. Must contain at least one item. Use [List
  Biomarkers](/api/biomarkers/list-biomarkers) to discover IDs.
</ParamField>

## Response

On success, the API returns `200 OK` confirming the biomarkers have been attached.

<ResponseField name="success" type="boolean">
  `true` when the biomarkers have been successfully added to the appointment.
</ResponseField>

### Closure-validation failure (`400`)

When the resulting set can't be cleanly ordered, the body is a `BiomarkerValidationError` (see the
`400` schema in the API playground above):

* `error` — a human-readable summary of why the set can't be ordered.
* `validation.failures[]` — present only on closure-validation failures (absent on plain
  body-validation errors). One entry per `lab` that rejected the set, each with `candidates[]`. A
  candidate lists the `order_ids` it would place and the `missing_biomarkers` (`{ id, name }`) you'd
  need to add — keep your originals, add those, and retry.

## Error responses

| Status | Description                                                                                                 |
| ------ | ----------------------------------------------------------------------------------------------------------- |
| `400`  | `biomarker_ids` empty / non-integer, or the resulting set fails closure validation (`validation.failures`). |
| `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.                                                                                      |

<RequestExample>
  ```bash cURL theme={null}
  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]
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "success": true
  }
  ```

  ```json 400 (closure validation) theme={null}
  {
    "error": "ZK biomarker validation failed: the requested biomarker set does not match any orderable ZK panel combination.\nTo proceed, choose one of the options below and add the missing biomarkers:\n  Option 1 — order panels [662, 663, 665], also add biomarkers: LDL Cholesterol (#43), HDL Cholesterol (#42)",
    "validation": {
      "failures": [
        {
          "lab": "zotzklimas",
          "candidates": [
            {
              "order_ids": [662, 663, 665],
              "missing_biomarkers": [
                { "id": 43, "name": "LDL Cholesterol" },
                { "id": 42, "name": "HDL Cholesterol" }
              ]
            }
          ]
        }
      ]
    }
  }
  ```

  ```json 409 theme={null}
  {
    "error": "Individual biomarkers cannot be modified after blood draw has been confirmed"
  }
  ```

  ```json 404 theme={null}
  {
    "error": "Biomarkers not found: 99999"
  }
  ```
</ResponseExample>
