Appointments
Create Appointment
Schedule a blood draw appointment for a profile at a test location
POST
/
api
/
v1
/
appointments
curl --request POST \
--url https://anivahealth.com/api/v1/appointments \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"profile_id": "a3f1c2d4-8b7e-4f2a-9c1d-2e3f4a5b6c7d",
"location_id": "b7e2d1f5-3c4a-4e8b-a2f1-9d0c1e2f3a4b",
"scheduled_at": "2026-05-15T10:30:00Z"
}'
{
"id": "c9f3e2a1-7d6b-4c5e-b3a2-1f0e9d8c7b6a",
"profile_id": "a3f1c2d4-8b7e-4f2a-9c1d-2e3f4a5b6c7d",
"location_id": "b7e2d1f5-3c4a-4e8b-a2f1-9d0c1e2f3a4b",
"scheduled_at": "2026-05-15T10:30:00Z",
"status": "confirmed",
"cancellation_reason": null,
"results_ready_emails": [],
"created_at": "2026-04-01T09:45:00Z",
"updated_at": null,
"profile": {
"id": "a3f1c2d4-8b7e-4f2a-9c1d-2e3f4a5b6c7d",
"handle": null,
"first_name": "Maria",
"last_name": "Schmidt",
"email": "maria.schmidt@example.com",
"phone": "+4930123456789",
"sex": 2,
"date_of_birth": "1985-03-22",
"height": 168,
"weight": 65,
"language": "de",
"transactional_emails_enabled": true,
"created_at": "2026-04-01T09:14:32Z",
"updated_at": null
},
"panels": [],
"individual_biomarkers": [],
"shipments": [],
"lab_codes": []
}
{
"error": "Appointments must be booked at least 12 hours in advance. Please pick a later time."
}
{
"error": "Location not found or inactive"
}
{
"error": "This profile already has an appointment at this location and time. Reschedule or cancel the existing appointment instead of creating a second one.",
"appointment_id": "03e916d2-7fd6-4414-8276-8532a105edc3"
}
Schedule a blood draw appointment for a patient profile at a specific test location. Once created, the appointment enters
Send an optional
Handle it by treating the request as already applied: fetch the appointment with
Get Appointment, move it with
Update Appointment, or drop it with
Cancel Appointment. Do not retry the create — it will keep
returning
confirmed status by default (some locations may instead default to pending — this is a per-location setting). Use Add Panels to attach blood test panels before confirming the blood draw.
A profile can hold at most one appointment per location and time. Creating an appointment for
a slot the profile already occupies returns
409 with the existing appointment_id instead of a
second appointment — so a retried or accidentally repeated request can never duplicate a booking.
See Duplicate appointments.Idempotency-Key header to make this create safe to retry — a repeat of the same key returns the original response instead of creating a second one. See Idempotency.
Requires the test_sessions_schedule scope.
Request
Body parameters
string
required
UUID of the patient profile to create the appointment for.
string
required
UUID of the test location where the blood draw will take place. Use List
Locations to find available location IDs.
string
required
Date and time of the appointment in ISO 8601 format (e.g.,
2026-05-15T10:30:00Z or
2026-05-15T12:30:00+02:00). Must include a timezone designator — either Z or an explicit
offset like +02:00; naive datetimes are rejected. Must be in the future.Must be an exact slot start on the location’s grid, and the slot must still have capacity — read
Get Availability first. Off-grid times return 400; a slot that
has filled up returns 409. At Aniva-operated locations it must also be at least 12 hours out — see
Minimum notice.string[]
Up to 5 addresses that each get one email once every lab result is final. The email links to the
result page in the Aniva admin panel. Your key needs
results_view access to the patient, else
403. See Results-ready emails.Response
On success, the API returns201 Created with the new appointment.
string
required
Unique appointment identifier (UUID).
string
required
UUID of the profile this appointment belongs to.
string
UUID of the test location where the draw takes place.
string
Scheduled appointment datetime as a UTC instant (ISO 8601 with
Z suffix, e.g.
2026-05-15T10:30:00Z). May be null.string
required
Current appointment status. One of
pending, confirmed, blood_drawn, or cancelled.
Lab-result completion granularity is derived from Get Results;
finer-grained shipment progression from each entry in the inline shipments array (see below)
followed to Get Shipment.string
Free-text reason captured when the appointment was cancelled (e.g. no-show, late cancellation,
‘Failed. Repeat required’).
null unless status is cancelled.string[]
required
Addresses that each get one email once every lab result is final, lowercased. Empty when off. See
Results-ready emails.
object
required
The patient profile associated with this appointment.
Show profile
Show profile
string
Unique profile identifier (UUID).
string
Profile handle or username. May be
null.string
First name. May be
null.string
Last name. May be
null.string
Email address. May be
null.string
Phone number in E.164 format. May be
null.integer
Biological sex per ISO/IEC 5218. May be
null.string
Date of birth in
YYYY-MM-DD format. May be null.number
Height in centimeters. May be
null.number
Weight in kilograms. May be
null.string
Preferred language (
en, de, or fi).boolean
Whether this profile receives transactional (programmatic) emails. May be
null.AppointmentPanel[]
required
Panels currently attached to this appointment. Empty for newly-created appointments — attach via
Add Panels. Look up panel details (name, biomarkers) via
List Panels.
Show Panel attachment fields
Show Panel attachment fields
string
Panel UUID — cross-reference against List Panels.
string
ISO 8601 timestamp the panel was first attached to this appointment.
AppointmentIndividualBiomarker[]
required
À-la-carte biomarkers attached to this appointment outside any panel. Empty for newly-created
appointments — attach via Add Individual Biomarkers.
Cross-reference each
biomarker_id against List Biomarkers
for display data.Show Individual biomarker fields
Show Individual biomarker fields
integer
Biomarker identifier — cross-reference against
List Biomarkers.
string
ISO 8601 timestamp the biomarker was first attached to this appointment.
AppointmentShipmentLink[]
required
Shipments linked to this appointment, oldest-link first. Empty for newly-created appointments —
link via Create Shipment (with
appointment_id) or
Attach Appointments to Shipment.Show Shipment link fields
Show Shipment link fields
string
Shipment reference identifier — cross-reference against
Get Shipment.
string
ISO 8601 timestamp the shipment was first linked to this appointment.
string[]
required
Lab codes (tube barcodes) recorded for this appointment, in scan order. Empty until the blood draw
is confirmed. A venous draw carries one code printed on every tube; a pharmacy draw carries one
code per microtainer. Results and documents on Get Results reference
these codes via
lab_code.Minimum notice
Appointments at Aniva-operated locations must be booked at least 12 hours ahead; booking inside that window returns400. Get Availability already withholds
those slots, so anything you read from that grid is bookable.
Locations you operate yourself have no minimum notice — same-day and walk-in booking is unchanged.
Duplicate appointments
A profile can hold at most one non-cancelled appointment per location and instant — one person cannot be drawn twice at one place at one moment, however many chairs the location has. Creating an appointment for a slot the profile already occupies returns409 with an
appointment_id naming the appointment that already exists:
{
"error": "This profile already has an appointment at this location and time. Reschedule or cancel the existing appointment instead of creating a second one.",
"appointment_id": "03e916d2-7fd6-4414-8276-8532a105edc3"
}
409.
This makes repeated creates safe: a request you resend after a timeout, or a double-submitted form,
cannot produce two appointments for the same patient at the same time.
appointment_id is only present on the duplicate 409. The capacity 409 — the slot is full
because other patients hold it — returns error alone, and the right recovery there is to pick
a different slot from Get Availability.Error responses
| Status | Description |
|---|---|
400 | Validation error — e.g., scheduled_at is in the past, is less than 12 hours out at an Aniva-operated location, or is not an exact slot start on the location’s grid. |
403 | Forbidden — your API key does not have access to this operation, or results_ready_emails is set without results_view access to the patient. |
404 | Profile or location not found, or the location is inactive. |
409 | This profile already has an appointment at this location and time (carries appointment_id), the slot is at capacity, the location is closed on that date, or the Idempotency-Key was reused with a different body, or its original request has not completed. |
500 | Internal server error. |
curl --request POST \
--url https://anivahealth.com/api/v1/appointments \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"profile_id": "a3f1c2d4-8b7e-4f2a-9c1d-2e3f4a5b6c7d",
"location_id": "b7e2d1f5-3c4a-4e8b-a2f1-9d0c1e2f3a4b",
"scheduled_at": "2026-05-15T10:30:00Z"
}'
{
"id": "c9f3e2a1-7d6b-4c5e-b3a2-1f0e9d8c7b6a",
"profile_id": "a3f1c2d4-8b7e-4f2a-9c1d-2e3f4a5b6c7d",
"location_id": "b7e2d1f5-3c4a-4e8b-a2f1-9d0c1e2f3a4b",
"scheduled_at": "2026-05-15T10:30:00Z",
"status": "confirmed",
"cancellation_reason": null,
"results_ready_emails": [],
"created_at": "2026-04-01T09:45:00Z",
"updated_at": null,
"profile": {
"id": "a3f1c2d4-8b7e-4f2a-9c1d-2e3f4a5b6c7d",
"handle": null,
"first_name": "Maria",
"last_name": "Schmidt",
"email": "maria.schmidt@example.com",
"phone": "+4930123456789",
"sex": 2,
"date_of_birth": "1985-03-22",
"height": 168,
"weight": 65,
"language": "de",
"transactional_emails_enabled": true,
"created_at": "2026-04-01T09:14:32Z",
"updated_at": null
},
"panels": [],
"individual_biomarkers": [],
"shipments": [],
"lab_codes": []
}
{
"error": "Appointments must be booked at least 12 hours in advance. Please pick a later time."
}
{
"error": "Location not found or inactive"
}
{
"error": "This profile already has an appointment at this location and time. Reschedule or cancel the existing appointment instead of creating a second one.",
"appointment_id": "03e916d2-7fd6-4414-8276-8532a105edc3"
}
⌘I
curl --request POST \
--url https://anivahealth.com/api/v1/appointments \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"profile_id": "a3f1c2d4-8b7e-4f2a-9c1d-2e3f4a5b6c7d",
"location_id": "b7e2d1f5-3c4a-4e8b-a2f1-9d0c1e2f3a4b",
"scheduled_at": "2026-05-15T10:30:00Z"
}'
{
"id": "c9f3e2a1-7d6b-4c5e-b3a2-1f0e9d8c7b6a",
"profile_id": "a3f1c2d4-8b7e-4f2a-9c1d-2e3f4a5b6c7d",
"location_id": "b7e2d1f5-3c4a-4e8b-a2f1-9d0c1e2f3a4b",
"scheduled_at": "2026-05-15T10:30:00Z",
"status": "confirmed",
"cancellation_reason": null,
"results_ready_emails": [],
"created_at": "2026-04-01T09:45:00Z",
"updated_at": null,
"profile": {
"id": "a3f1c2d4-8b7e-4f2a-9c1d-2e3f4a5b6c7d",
"handle": null,
"first_name": "Maria",
"last_name": "Schmidt",
"email": "maria.schmidt@example.com",
"phone": "+4930123456789",
"sex": 2,
"date_of_birth": "1985-03-22",
"height": 168,
"weight": 65,
"language": "de",
"transactional_emails_enabled": true,
"created_at": "2026-04-01T09:14:32Z",
"updated_at": null
},
"panels": [],
"individual_biomarkers": [],
"shipments": [],
"lab_codes": []
}
{
"error": "Appointments must be booked at least 12 hours in advance. Please pick a later time."
}
{
"error": "Location not found or inactive"
}
{
"error": "This profile already has an appointment at this location and time. Reschedule or cancel the existing appointment instead of creating a second one.",
"appointment_id": "03e916d2-7fd6-4414-8276-8532a105edc3"
}