What This Example Does
- Looks up a customer by email
- Creates the customer only if not found
- Creates an appointment for that customer
- Retrieves the appointment to confirm it
Estimated time: 10-15 minutes
Use These Variables
```bash
SUBDOMAIN="acmeclinic"
API_KEY="YOUR_API_KEY_HERE"
BASE_URL="https://$SUBDOMAIN.juvonno.com"
CUSTOMER_EMAIL="alex.taylor@example.com"
# Set during execution as you parse API responses:
CUSTOMER_ID=""
APPOINTMENT_ID=""
BRANCH_CODE=""
BRANCH_ID=""
```
Step 1: Search for Customer
**Endpoint:** `POST /customers/search`
Search by email and try to reuse an existing customer.
```bash
curl --request POST "$BASE_URL/api/customers/search" \
--header "Accept: application/json" \
--header "Content-Type: application/json" \
--header "X-API-Key: $API_KEY" \
--data '{
"email": "'"$CUSTOMER_EMAIL"'"
}'
```
Example response:
```json
{
"total": 1,
"page": 1,
"count": 1,
"list": [
{
"id": 12345,
"first_name": "Alex",
"last_name": "Taylor",
"email": "alex.taylor@example.com"
}
]
}
```
Required vs optional:
- `email` is optional in the schema, but used here as the common identifier for lookup.
- `first_name`, `last_name`, and `phone` are also available search fields.
Capture and reuse:
- If `list` has at least one customer, set `CUSTOMER_ID = list[0].id` and skip Step 2.
- Example: `CUSTOMER_ID=12345`
- If `list` is empty, continue to Step 2.
- Multiple results may be returned; using `list[0]` is a simplification for this example.
Gotcha: search responses are wrapped (`total`, `page`, `count`, `list`) instead of returning a bare array.
Step 2: Create Customer (if needed)
**Endpoint:** `POST /customers`
Only create a customer if Step 1 returns no results.
```bash
curl --request POST "$BASE_URL/api/customers" \
--header "Accept: application/json" \
--header "Content-Type: application/json" \
--header "X-API-Key: $API_KEY" \
--data '{
"first_name": "Alex",
"last_name": "Taylor",
"date_of_birth": "1990-07-15",
"gender": "female",
"email": "'"$CUSTOMER_EMAIL"'"
}'
```
Example response:
```json
{
"id": 67890,
"first_name": "Alex",
"last_name": "Taylor",
"email": "alex.taylor@example.com"
}
```
Required vs optional:
- Required: `first_name`, `last_name`, `date_of_birth`, `gender`
- Optional (used here): `email`
Capture and reuse:
- Set `CUSTOMER_ID = id` from the create response.
- Example: `CUSTOMER_ID=67890`
Step 3: Create Appointment
**Endpoint A (to get clinic identifier):** `GET /branches`
If you do not already have a clinic identifier, fetch branches first and capture one `code` (or `id`).
```bash
curl --request GET "$BASE_URL/api/branches" \
--header "Accept: application/json" \
--header "X-API-Key: $API_KEY"
```
Example response:
```json
[
{
"id": 10,
"code": "MAIN",
"name": "Main Clinic"
}
]
```
Capture and reuse:
- Set `BRANCH_CODE = "MAIN"` (or use `BRANCH_ID = 10`).
- Example: `BRANCH_CODE="MAIN"`
- Example: `BRANCH_ID=10`
- Prefer `BRANCH_CODE` when available; use `BRANCH_ID` only if required.
**Endpoint B (create):** `POST /appointments`
You must provide a valid staff identifier (practitioner) to create an appointment.
```bash
STAFF_NUM="RMT001"
BRANCH_CODE="MAIN"
curl --request POST "$BASE_URL/api/appointments" \
--header "Accept: application/json" \
--header "Content-Type: application/json" \
--header "X-API-Key: $API_KEY" \
--data '{
"attendants": ["'"$STAFF_NUM"'"],
"customer_id": '"$CUSTOMER_ID"',
"date": "2026-04-20",
"time": "09:00",
"duration": 60,
"branch_code": "'"$BRANCH_CODE"'"
}'
```
Example response:
```json
{
"id": 555001,
"status": "open",
"customer": {
"id": 67890,
"first_name": "Alex",
"last_name": "Taylor"
},
"branch": {
"id": 10,
"code": "MAIN",
"name": "Main Clinic"
},
"date": {
"start": "2026-04-20 09:00:00",
"end": "2026-04-20 10:00:00"
}
}
```
Required vs optional:
- Required in `AppointmentPost`: customer reference, branch reference, `date`, `time`, `duration`, attendants.
- Optional in this example: other fields like `reason`, `memo`, `schedule_type_id`.
Capture and reuse:
- Set `APPOINTMENT_ID = id` from the create response.
- Example: `APPOINTMENT_ID=555001`
Gotcha: ID vs code vs number can vary by endpoint (`branch_id` vs `branch_code`, `customer_id` vs `customer_num`).
Common failure: invalid `STAFF_NUM` or `branch_code` may cause validation errors.
Step 4: Retrieve or Confirm Appointment
**Endpoint:** `GET /appointments/{appointmentId}`
```bash
curl --request GET "$BASE_URL/api/appointments/$APPOINTMENT_ID" \
--header "Accept: application/json" \
--header "X-API-Key: $API_KEY"
```
Example response:
```json
{
"id": 555001,
"status": "open",
"customer": {
"id": 67890,
"first_name": "Alex",
"last_name": "Taylor"
},
"branch": {
"id": 10,
"code": "MAIN",
"name": "Main Clinic"
}
}
```
Required vs optional:
- Required path parameter: `appointmentId`
- Optional query parameter: `items=1` to include products/services on the appointment
Gotcha: parameter naming is not fully consistent across the API in general (for example, some endpoints use `customerId` and others use `customer_id`).
What You Should Have Now
- `CUSTOMER_ID` captured from search or create
- clinic identifier captured (`BRANCH_CODE` and/or `BRANCH_ID`)
- `APPOINTMENT_ID` captured from create
- a confirmed appointment response from `GET /appointments/{appointmentId}`
You have now completed a full integration loop: customer -> appointment -> confirmation