> ## 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.

# Appointments

> Understand how blood draw appointments work in the Aniva API

An appointment is a scheduled blood draw session for a patient profile. Depending on the `test_method`, the draw takes place either at a physical test location or through a home kit mailed to the patient.

## Fields

<ResponseField name="id" type="string" required>
  UUID auto-generated by Aniva. Use this to add panels, confirm the draw, and retrieve results.
</ResponseField>

<ResponseField name="profile_id" type="string" required>
  The UUID of the profile this appointment belongs to.
</ResponseField>

<ResponseField name="location_id" type="string">
  The UUID of the test location where the draw will take place. Null for home kit appointments.
</ResponseField>

<ResponseField name="scheduled_at" type="string">
  Scheduled blood draw datetime in ISO 8601 format. Always returned as a UTC instant with a `Z`
  suffix (e.g. `2026-04-15T08:30:00Z`). On create / update the value **must include a timezone
  designator** (either `Z` or an explicit offset like `+02:00`) — naive datetimes are rejected. Must
  be in the future.
</ResponseField>

<ResponseField name="status" type="string" required>
  Current lifecycle status. One of `pending`, `confirmed`, `blood_drawn`, or `cancelled`. See
  [Lifecycle](#lifecycle) below. Lab-result completion granularity (`partial` vs `done`) is derived
  from [Get Results](/api/appointments/get-results); finer-grained shipment progression (in transit,
  delivered, etc.) is derived by following each entry in the inline `shipments` array to [Get
  Shipment](/api/shipments/get-shipment).
</ResponseField>

<ResponseField name="test_method" type="string">
  How the blood draw is performed: - `practitioner` — drawn at a test location by a healthcare
  professional - `home` — home blood draw kit mailed to the patient
</ResponseField>

<ResponseField name="cancellation_reason" type="string">
  Free-text reason captured when the appointment was cancelled (e.g. no-show, late cancellation,
  'Failed. Repeat required'). `null` unless `status` is `cancelled`.
</ResponseField>

<ResponseField name="created_at" type="string" required>
  ISO 8601 timestamp of when the appointment was created.
</ResponseField>

<ResponseField name="updated_at" type="string">
  ISO 8601 timestamp of the most recent update. Null if the appointment has never been updated.
</ResponseField>

<ResponseField name="profile" type="object">
  The full profile object for the patient, embedded in every appointment response.

  <Expandable title="properties">
    <ResponseField name="id" type="string">
      UUID of the associated profile.
    </ResponseField>

    <ResponseField name="first_name" type="string">
      Patient's first name.
    </ResponseField>

    <ResponseField name="last_name" type="string">
      Patient's last name.
    </ResponseField>

    <ResponseField name="email" type="string">
      Patient's email address.
    </ResponseField>

    <ResponseField name="date_of_birth" type="string">
      Date of birth in `YYYY-MM-DD` format.
    </ResponseField>

    <ResponseField name="sex" type="integer">
      Biological sex per ISO/IEC 5218.
    </ResponseField>
  </Expandable>
</ResponseField>

## Lifecycle

Appointments expose four lifecycle states:

```
pending → confirmed → blood_drawn
   │          │             │
   └──────────┴─────────────┴──→ cancelled
```

| Status        | Meaning                                                                                                                                                                                                                    |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pending`     | The appointment is scheduled and awaiting confirmation. Can be [updated](/api/appointments/update-appointment) or [cancelled](/api/appointments/cancel-appointment).                                                       |
| `confirmed`   | Scheduling has been confirmed (e.g., by the practice), but blood has not yet been drawn. Can still be updated or cancelled.                                                                                                |
| `blood_drawn` | The kit barcode has been submitted via [Confirm Blood Draw](/api/appointments/confirm-blood-draw) and the lab order pipeline has been triggered. Panels are frozen and the appointment cannot be rescheduled or cancelled. |
| `cancelled`   | The appointment has been cancelled. Terminal — no further transitions.                                                                                                                                                     |

<Note>
  The appointment status surface is intentionally coarse — finer-grained progression is exposed
  via dedicated endpoints:

  * **Lab result completion** (partial vs done) →
    [Get Results](/api/appointments/get-results) — top-level `status` field is `in_progress` or
    `completed`.
  * **Shipment progression** (in transit, delivered, etc.) → follow each entry in the inline
    `shipments` array on the appointment to [Get Shipment](/api/shipments/get-shipment) — each
    shipment carries its own `status` and a `status_updates` event log.
</Note>

<Warning>
  Confirming an appointment (moving it to `blood_drawn`) is **irreversible**. Once you submit the
  kit barcode, the lab order pipeline is triggered and cannot be undone. Updates and cancellations
  are only allowed while the appointment is in `pending` or `confirmed` status.
</Warning>

## Listing appointments

Retrieve all appointments accessible to your API key:

```
GET /api/v1/appointments
```

Optionally pass `?profile_id={uuid}` to filter by a specific profile. Without this parameter, a default time window (past 2 months to future 1 month) is applied.

## Updating an appointment

Before the blood draw is confirmed, you can reschedule or change the test location:

```
PATCH /api/v1/appointments/{id}
```

Pass any combination of `scheduled_at` and `location_id` to update. Only include the fields you want to change.

<Note>
  Updates are only allowed before the blood draw — i.e. while the appointment is in `pending` or
  `confirmed` status. Once the appointment moves to `blood_drawn` or has been `cancelled`, it cannot
  be modified.
</Note>

## Cancelling an appointment

Cancel an appointment that is no longer needed:

```
DELETE /api/v1/appointments/{id}
```

This sets the appointment status to `cancelled`. The cancelled appointment is returned in the response.

<Note>
  Cancellation is only allowed before the blood draw — i.e. while the appointment is in `pending` or
  `confirmed` status. Appointments that have already moved to `blood_drawn` cannot be cancelled.
</Note>

## Adding panels

Before confirming the blood draw, add the blood test panels you want to run by calling:

```
POST /api/v1/appointments/{id}/panels
```

Pass an array of panel UUIDs in the `panel_ids` field. Panel IDs are provided by Aniva for your partner account.

<Note>
  Panels must be added before the blood draw — i.e. while the appointment is in `pending` or
  `confirmed` status. You cannot add or change panels once the appointment has moved to
  `blood_drawn`.
</Note>

## Removing panels

If you need to remove panels before the blood draw, call:

```
DELETE /api/v1/appointments/{id}/panels
```

Pass an array of panel UUIDs in the `panel_ids` field. All specified panels must currently be associated with the appointment.

<Note>
  Like adding panels, removal is only allowed before the blood draw — i.e. while the appointment is
  in `pending` or `confirmed` status. Panels cannot be removed once the appointment has moved to
  `blood_drawn`.
</Note>

## Individual biomarkers

You can attach individual biomarkers to an appointment as à la carte tests, outside any pre-built panel. This is useful when a practitioner wants a tailored selection rather than a pre-defined bundle. The attached biomarkers surface on the appointment response as a `individual_biomarkers` array (siblings of `panels`). Routing decisions are made downstream by the order-logistics pipeline based on each biomarker's per-lab mappings.

Add biomarkers (idempotent — already-attached biomarkers are kept):

```
PATCH /api/v1/appointments/{id}/biomarkers
```

Remove biomarkers (every ID must currently be attached, otherwise `404`):

```
DELETE /api/v1/appointments/{id}/biomarkers
```

Both endpoints take a `biomarker_ids` array and validate the resulting set against each lab's closure rules — an order that can't be cleanly placed (e.g. a CRP/HDL ratio without CRP and HDL) returns `400` with the biomarkers you'd need to add, and persists nothing. Use [List Biomarkers](/api/biomarkers/list-biomarkers) to discover biomarker IDs.

<Note>
  À-la-carte biomarkers are not pre-validated on add/remove — incoherent combinations are accepted
  at write time and may surface as ordering failures later in the lab pipeline.
</Note>

<Note>
  Like panel modifications, individual biomarkers can only be added or removed before the blood draw
  — i.e. while the appointment is in `pending` or `confirmed` status. Once the appointment moves to
  `blood_drawn` the custom snapshot is frozen.
</Note>

## Previewing container requirements

After adding panels or individual biomarkers, preview the physical specimen-collection containers that will be needed for the sample collection:

```
GET /api/v1/appointments/{id}/preview
```

The response's `containers` array includes the type, name, kind (`monovette`/`dbs_card`/`urine_cup`), cap color, volume, Sarstedt article number, and count for each required container. This helps practitioners prepare the correct materials before the blood draw. The response also carries a `pricing` object — the appointment's whole-cart pricing (billing groups plus a total, at both the retail card and your own card), populated when the appointment has a location and attached tests and your key has the `prices_view` scope, otherwise `null`. `routing` is intentionally never exposed and is always `null`.

<Note>
  The preview reflects the currently attached panels and biomarkers. If you add or remove panels
  after previewing, call the preview endpoint again to get updated container requirements.
</Note>

## Confirming the blood draw

After the blood is drawn, submit the kit barcode to trigger the lab order pipeline:

```
POST /api/v1/appointments/{id}/confirm
```

This moves the appointment status to `blood_drawn` and initiates lab processing. This action is irreversible.

## Shipment pickup

<Warning>
  `POST /api/v1/appointments/pickup` and the `shipment_picked_up` appointment status are
  **deprecated**. The endpoint is retained as a no-op that returns 200 with `Deprecation` and
  `Warning` response headers so existing integrations don't break — see [Confirm Shipment Pickup
  (deprecated)](/api/appointments/shipment-pickup).
</Warning>

To track post-blood-draw progression, follow each entry in the appointment's inline `shipments`
array to [Get Shipment](/api/shipments/get-shipment) — each shipment carries its own `status`
and a `status_updates` history.

## Linking shipments

An appointment can be linked to one or more shipments (and vice versa — the relation is
many-to-many). The current link set surfaces inline on both sides:

* on the appointment, as a `shipments` array of `{ id, added_at }` entries;
* on the shipment, as an `appointments` array of `{ id, added_at }` entries.

Both arrays are oldest-link first. Each `id` cross-references the canonical detail endpoint
([Get Shipment](/api/shipments/get-shipment) /
[Get Appointment](/api/appointments/get-appointment)) for the full payload.

Shipments are linked at creation time via the `appointment_id` field on
[Create Shipment](/api/shipments/create-shipment). After creation, manage links on the
appointment side with [Attach Shipments to
Appointment](/api/appointments/attach-shipments-to-appointment) and [Detach Shipments from
Appointment](/api/appointments/detach-shipments-from-appointment), or symmetrically from the
shipment side with [Attach Appointments to
Shipment](/api/shipments/attach-appointments-to-shipment) and [Detach Appointments from
Shipment](/api/shipments/detach-appointments-from-shipment). Each mutation returns the parent
resource (Appointment / Shipment) with the post-mutation link array populated.

The mutation rule is: links can be created or removed when EITHER

* the appointment is past blood-draw (`blood_drawn`), or
* the shipment was created less than 24 hours ago.

Outside that window the link is locked so historical records stay stable.

## Retrieving lab results

Once the lab has processed the blood samples, retrieve clinical and genetic results:

```
GET /api/v1/appointments/{id}/results
```

The response includes planned tests (status `planned`) alongside received results, so you can
track what is still pending. The top-level `status` field is `in_progress` until every submission
arrives, then `completed` — use this to derive whether the appointment's lab work is done. Lab
report PDFs are referenced in the `documents` array — use `GET /api/v1/documents/{id}` to
download them.

## Example appointment

```json theme={null}
{
  "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
  "profile_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "location_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
  "scheduled_at": "2026-04-15T08:30:00Z",
  "status": "confirmed",
  "test_method": "practitioner",
  "cancellation_reason": null,
  "created_at": "2026-04-01T10:00:00Z",
  "updated_at": null,
  "profile": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane.doe@example.com",
    "date_of_birth": "1990-06-15",
    "sex": 2
  },
  "panels": [
    {
      "panel_id": "f1c2d4a3-7b8e-4f2a-9c1d-2e3f4a5b6c7d",
      "added_at": "2026-04-01T10:05:00Z"
    }
  ],
  "individual_biomarkers": [
    {
      "biomarker_id": 42,
      "added_at": "2026-04-01T10:07:00Z"
    }
  ],
  "shipments": [
    {
      "id": "P2604154821",
      "added_at": "2026-04-15T07:00:00Z"
    }
  ]
}
```
