Creating Appointments Programmatically

To create appointments programmatically, you can use an M2M. Once you have created an M2M, give it the following access policy:

{
  "rule": [
    {
      "action": [
        "Zambda:InvokeFunction"
      ],
      "effect": "Allow",
      "resource": [
        "Zambda:Function:create-appointment",
        "Zambda:Function:get-appointments",
        "Zambda:Function:search-patients"
      ]
    }
  ]
}

Now let's go through the steps to create an appointment.

Search patients

First we will search patients. You can search by name, date of birth, phone, and email.

curl -X POST https://project-api.zapehr.com/v1/zambda/search-patients/execute \
  -H 'Authorization: Bearer your_access_token' \
  -H "x-oystehr-project-id: your_project_id" \
  -H "Content-Type: application/json" \
  --data '{"name":"example"}'

This will return a list of patients, for example:

{
  "patients": [
    {
      "id":"b5a6496b-0bcc-49f0-8f77-95cbec5d1b5e",
      "name":"Example, Example",
      "firstName":"Example",
      "lastName":"Example",
      "dateOfBirth":"1990-01-01",
      "gender":"male"
    },
    {
      "id":"d8032f9e-da77-4cc1-9a9f-9d2a3a94ec37",
      "name":"Example, Example",
      "firstName":"Example",
      "lastName":"Example",
      "dateOfBirth":"1990-01-01",
      "gender":"male"
    },
    {
      "id":"9afe85fd-4e73-426b-86ac-d872fc528f71",
      "name":"Example, Example",
      "firstName":"Example",
      "lastName":"Example",
      "dateOfBirth":"1990-01-01",
      "gender":"male",
      "email":"[email protected]"
    }
  ],
  "total":3,
  "offset":0
}

Get a list of locations

Next we will call the list-bookables zambda to get a list of locations.

curl -X POST https://project-api.zapehr.com/v1/zambda/list-bookables/execute-public \
  -H "x-oystehr-project-id: your_project_id" \
  -H "Content-Type: application/json" \
  --data '{"serviceMode":"in-person"}'

This will return a list, for example:

{
  "items": [
    {
      "label": "example",
      "slug": "example",
      "resourceType": "Location",
      "resourceId": "2691f646-e16a-42e6-953a-47f7b254a77c",
      "secondaryLabel": [],
      "category": "example"
    }
  ]
}

Get a schedule

Next we will call the get-schedule zambda to get the schedule.

curl -X POST https://project-api.zapehr.com/v1/zambda/get-schedule/execute-public \
  -H "x-oystehr-project-id: your_project_id" \
  -H "Content-Type: application/json" \
  --data '{"slug":"example","scheduleType":"location","serviceCategoryCode":"urgent-care"}'

This will return a list of available appointments, for example:

{
  "message": "Successfully retrieved all available slot times",
  "available": [
    {
      "slot": {
        "resourceType": "Slot",
        "id": "48e40c33-4bd3-4e5d-92fa-7b8c3c6ebef6|2026-08-14T11:30:00.000-05:00",
        "start": "2026-08-14T11:30:00.000-05:00",
        "serviceCategory": [
          {
            "coding": [
              {
                "system": "https://fhir.ottehr.com/slot-service-category",
                "code": "in-person-service-mode"
              },
              {
                "system": "https://fhir.ottehr.com/CodeSystem/service-mode-service-category",
                "code": "in-person-service-mode"
              }
            ]
          },
          {
            "coding": [
              {
                "system": "https://fhir.ottehr.com/CodeSystem/service-category",
                "code": "urgent-care"
              }
            ]
          }
        ],
        "end": "2026-08-14T16:45:00.000+00:00",
        "schedule": {
          "reference": "Schedule/48e40c33-4bd3-4e5d-92fa-7b8c3c6ebef6"
        },
        "status": "free"
      },
      "owner": {
        "resourceType": "Location",
        "id": "2691f646-e16a-42e6-953a-47f7b254a77c",
        "name": "example"
      },
      "timezone": "America/New_York"
    }
  ],
  "location": {
    "id": "2691f646-e16a-42e6-953a-47f7b254a77c",
    "slug": "example",
    "name": "example",
    "telecom": [
      {
        "system": "phone",
        "use": "work",
        "value": "1234567890"
      }
    ],
    "timezone": "America/New_York",
    "otherOffices": [],
    "scheduleOwnerType": "location",
    "scheduleExtension": {
      "schedule": {
        "monday": {
          "open": 8,
          "close": 20,
          "openingBuffer": 0,
          "closingBuffer": 0,
          "workingDay": true,
          "hours": [
            {
              "hour": 0,
              "capacity": 4
            },
            {
              "hour": 1,
              "capacity": 4
            },
            {
              "hour": 2,
              "capacity": 4
            }
          ]
        }
      },
      "scheduleOverrides": {}
    }
  },
  "displayTomorrowSlotsAtHour": 9,
  "waitingMinutes": 0,
  "timezone": "America/New_York"
}

Create a slot

Next we will the create-slot zambda to create a slot.

curl -X POST https://project-api.zapehr.com/v1/zambda/create-slot/execute-public \
  -H "x-oystehr-project-id: your_project_id" \
  -H "Content-Type: application/json" \
  --data '{"scheduleId":"e4dbd91f-084b-4ce7-a809-4ef2dc231307","startISO":"2026-08-14T13:30:00.000-05:00","lengthInMinutes":15,"serviceModality":"in-person","walkin":false,"postTelemedLabOnly":false,"serviceCategoryCode":"urgent-care"}'

This will return a slot, for example:

{
  "resourceType": "Slot",
  "status": "busy",
  "start": "2026-08-14T13:30:00.000-05:00",
  "end": "2026-08-14T13:45:00.000-05:00",
  "serviceCategory": [
    {
      "coding": [
        {
          "system": "https://fhir.ottehr.com/slot-service-category",
          "code": "in-person-service-mode"
        },
        {
          "system": "https://fhir.ottehr.com/CodeSystem/service-mode-service-category",
          "code": "in-person-service-mode"
        }
      ]
    },
    {
      "coding": [
        {
          "display": "Urgent Care",
          "code": "urgent-care",
          "system": "https://fhir.ottehr.com/CodeSystem/service-category"
        }
      ]
    }
  ],
  "schedule": {
    "reference": "Schedule/48e40c33-4bd3-4e5d-92fa-7b8c3c6ebef6"
  },
  "id": "f056a186-d3fd-4139-afa8-f620c23c0aaf"
}

Create an appointment

Next we will call the create-appointment zambda to create an appointment. If it is a new patient, do not include the patient ID.

curl -X POST https://project-api.zapehr.com/v1/zambda/create-appointment/execute \
  -H "x-oystehr-project-id: your_project_id" \
  -H 'Authorization: Bearer your_access_token' \
  -H "Content-Type: application/json" \
  --data '{"patient":{"id":"15bf1b59-6944-485b-97d8-0ea2404df614","newPatient":false,"dateOfBirth":"1990-01-01","phoneNumber":"1234567890","firstName":"Example","lastName":"Example","sex":"male","reasonForVisit":"Allergies or Allergic reaction"},"slotId":"f056a186-d3fd-4139-afa8-f620c23c0aaf"}'

This will return details about the appointment, for example:

{
  "message": "Successfully created an appointment and encounter",
  "appointmentId": "7fe3d73a-f1a2-4f8c-98e2-6a15c6688060",
  "fhirPatientId": "76900ad8-de80-4d64-af8e-aae524913296",
  "questionnaireResponseId": "d0105049-046e-47b9-9b1b-961c67fec396",
  "encounterId": "9af10a1b-10d3-4ed2-b7a2-c52f20575da3",
  "resources": {
    "appointment": {
      "resourceType": "Appointment",
      "meta": {
        "tag": [
          {
            "code": "OTTEHR-IP"
          },
          {
            "system": "created-by",
            "display": "Staff [email protected]"
          },
          {
            "system": "appointment-preprocessing-status",
            "code": "APPOINTMENT_READY_FOR_PREPROCESSING"
          }
        ]
      },
      "participant": [
        {
          "actor": {
            "reference": "Patient/76900ad8-de80-4d64-af8e-aae524913296"
          },
          "status": "accepted"
        },
        {
          "actor": {
            "reference": "Location/2691f646-e16a-42e6-953a-47f7b254a77c"
          },
          "status": "accepted"
        }
      ],
      "start": "2026-08-14T17:30:00.000Z",
      "end": "2026-08-14T17:45:00.000Z",
      "slot": [
        {
          "reference": "Slot/e3d6d982-607a-4e49-83a5-654c4750e0c4"
        }
      ],
      "appointmentType": {
        "text": "walkin"
      },
      "serviceCategory": [
        {
          "coding": [
            {
              "system": "https://fhir.ottehr.com/slot-service-category",
              "code": "in-person-service-mode"
            },
            {
              "system": "https://fhir.ottehr.com/CodeSystem/service-mode-service-category",
              "code": "in-person-service-mode"
            }
          ]
        },
        {
          "coding": [
            {
              "display": "Urgent Care",
              "code": "urgent-care",
              "system": "https://fhir.ottehr.com/CodeSystem/service-category"
            }
          ]
        }
      ],
      "description": "Allergies or Allergic reaction",
      "status": "arrived",
      "created": "2026-08-14T17:00:00.000Z",
      "extension": [],
      "id": "7fe3d73a-f1a2-4f8c-98e2-6a15c6688060"
    },
    "encounter": {
      "resourceType": "Encounter",
      "status": "arrived",
      "statusHistory": [
        {
          "status": "arrived",
          "period": {
            "start": "2026-08-14T17:00:00.000Z"
          }
        }
      ],
      "class": {
        "system": "http://hl7.org/fhir/R4/v3/ActEncounterCode/vs.html",
        "code": "ACUTE",
        "display": "inpatient acute"
      },
      "subject": {
        "reference": "Patient/76900ad8-de80-4d64-af8e-aae524913296"
      },
      "appointment": [
        {
          "reference": "Appointment/7fe3d73a-f1a2-4f8c-98e2-6a15c6688060"
        }
      ],
      "location": [
        {
          "location": {
            "reference": "Location/2691f646-e16a-42e6-953a-47f7b254a77c"
          }
        }
      ],
      "id": "9af10a1b-10d3-4ed2-b7a2-c52f20575da3",
    },
    "questionnaire": {
      "resourceType": "QuestionnaireResponse",
      "questionnaire": "https://ottehr.com/FHIR/Questionnaire/intake-paperwork-inperson|1.2.7",
      "status": "in-progress",
      "meta": {
        "tag": [
          {
            "system": "https://fhir.ottehr.com/CodeSystem/questionnaire-response-type",
            "code": "intake-paperwork"
          }
        ],
        "versionId": "51e17d40-9edd-493e-a963-4797ce1e4258",
        "lastUpdated": "2026-08-14T17:10:54.129Z"
      },
      "subject": {
        "reference": "Patient/76900ad8-de80-4d64-af8e-aae524913296"
      },
      "encounter": {
        "reference": "Encounter/9af10a1b-10d3-4ed2-b7a2-c52f20575da3"
      },
      "item": [
        {
          "linkId": "contact-information-page",
          "item": [
            {
              "linkId": "photo-id-front"
            },
            {
              "linkId": "photo-id-back"
            },
            {
              "linkId": "patient-street-address"
            },
            {
              "linkId": "patient-street-address-2"
            },
            {
              "linkId": "patient-city"
            },
            {
              "linkId": "patient-state"
            },
            {
              "linkId": "patient-zip"
            },
            {
              "linkId": "patient-email"
            },
            {
              "linkId": "patient-number",
              "answer": [
                {
                  "valueString": "(123) 456-7890"
                }
              ]
            },
            {
              "linkId": "patient-preferred-communication-method"
            },
            {
              "linkId": "mobile-opt-in"
            },
            {
              "linkId": "is-new-qrs-patient",
              "answer": [
                {
                  "valueBoolean": false
                }
              ]
            },
            {
              "linkId": "patient-first-name",
              "answer": [
                {
                  "valueString": "Example"
                }
              ]
            },
            {
              "linkId": "patient-last-name",
              "answer": [
                {
                  "valueString": "Example"
                }
              ]
            },
            {
              "linkId": "patient-birthdate",
              "answer": [
                {
                  "valueString": "1990-01-01"
                }
              ]
            },
            {
              "linkId": "patient-birth-sex",
              "answer": [
                {
                  "valueString": "Male"
                }
              ]
            },
            {
              "linkId": "appointment-service-category",
              "answer": [
                {
                  "valueString": "urgent-care"
                }
              ]
            },
            {
              "linkId": "reason-for-visit",
              "answer": [
                {
                  "valueString": "Allergies or Allergic reaction"
                }
              ]
            }
          ]
        }
      ],
      "id": "d0105049-046e-47b9-9b1b-961c67fec396"
    },
    "patient": {
      "id": "76900ad8-de80-4d64-af8e-aae524913296",
      "name": [
        {
          "use": "official",
          "given": [
            "Example"
          ],
          "family": "Example"
        }
      ],
      "active": true,
      "gender": "male",
      "birthDate": "1990-01-01",
      "resourceType": "Patient",
    }
  }
}

Get a list of appointments

Next we will call the get-appointments zambda to get a list appointments.

curl -X POST https://project-api.zapehr.com/v1/zambda/get-appointments/execute \
  -H "x-oystehr-project-id: your_project_id" \
  -H 'Authorization: Bearer your_access_token' \
  -H "Content-Type: application/json" \
  --data '{"searchDateFrom":"2026-08-14","searchDateTo":"2026-08-14","timezone":"America/New_York","locationIds":["2691f646-e16a-42e6-953a-47f7b254a77c"],"providerIds":[],"serviceCategories":[],"visitType":["in-person-walk-in","in-person-pre-booked","in-person-post-telemed","virtual-walk-in","virtual-pre-booked"],"supervisorApprovalEnabled":false}'

This will return a list of appointments, for example:

{
  "message": "Successfully retrieved all appointments",
  "preBooked": [
    {
      "id": "7fe3d73a-f1a2-4f8c-98e2-6a15c6688060",
      "encounter": {
        "resourceType": "Encounter",
        "status": "planned",
        "class": {
          "system": "http://hl7.org/fhir/R4/v3/ActEncounterCode/vs.html",
          "code": "ACUTE",
          "display": "inpatient acute"
        },
        "id": "9af10a1b-10d3-4ed2-b7a2-c52f20575da3",
        "appointment": [
          {
            "reference": "Appointment/7fe3d73a-f1a2-4f8c-98e2-6a15c6688060"
          }
        ],
        "statusHistory": [
          {
            "status": "planned",
            "period": {
              "start": "2026-08-14T15:45:13.169Z"
            }
          }
        ],
        "location": [
          {
            "location": {
              "reference": "Location/2691f646-e16a-42e6-953a-47f7b254a77c"
            }
          }
        ]
      },
      "encounterId": "9af10a1b-10d3-4ed2-b7a2-c52f20575da3",
      "start": "2026-08-14T13:30:00.000-05:00",
      "patient": {
        "id": "76900ad8-de80-4d64-af8e-aae524913296",
        "firstName": "Example",
        "lastName": "Example",
        "sex": "male",
        "dateOfBirth": "1990-01-01"
      },
      "reasonForVisit": "Cough and/or congestion",
      "appointmentType": "pre-booked",
      "appointmentAttendanceType": "in-person",
      "appointmentStatus": "booked",
      "status": "pending",
      "provider": "",
      "paperwork": {
        "demographics": false,
        "photoID": false,
        "insuranceCard": false,
        "consent": false,
        "ovrpInterest": false
      },
      "participants": {},
      "next": false,
      "visitStatusHistory": [
        {
          "status": "pending",
          "period": {
            "start": "2026-08-14T15:45:13.169Z"
          }
        }
      ],
      "serviceCategory": "Urgent Care",
      "location": {
        "resourceType": "Location",
        "id": "ba60ab0e-28cd-489d-82fa-f43a293398a0",
        "name": "Castroville",
        "telecom": [
          {
            "system": "phone",
            "use": "work",
            "value": "1234567890"
          }
        ]
      }
    }
  ],
  "inOffice": [],
  "completed": [],
  "cancelled": []
}