Creating Appointments
This guide walks you through building an appointment booking flow using the Jump EHR API. You'll learn how to list available appointment templates, search for patients, and create appointments with idempotency protection.
Overview
The booking flow consists of these steps:
- List appointment templates — show available service types
- Find or create a patient — search existing records or create new
- Create the appointment — book with idempotency protection
Required API Scopes
Your API key needs these scopes:
| Scope | Purpose |
|---|---|
read_appointment_templates | List available service types |
read_patients | Search existing patients |
write_patients | Create new patients |
write_appointments | Create appointments |
Step 1: Fetch Appointment Templates
Load the available appointment types to let users select a service.
const API_BASE = 'https://app.usejump.co.uk/functions/v1/api-v1';
async function getAppointmentTemplates() {
const response = await fetch(`${API_BASE}/appointment-templates?is_active=true`, {
headers: { 'Authorization': `Bearer ${API_KEY}` }
});
const { data } = await response.json();
return data;
}Step 2: Find or Create a Patient
Search for existing patients to avoid duplicates:
async function searchPatients(query) {
const response = await fetch(
`${API_BASE}/patients?search=${encodeURIComponent(query)}&limit=5`,
{ headers: { 'Authorization': `Bearer ${API_KEY}` } }
);
const { data } = await response.json();
return data;
}Create a new patient if no match is found:
async function createPatient(patientData) {
const response = await fetch(`${API_BASE}/patients`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
first_name: patientData.firstName,
last_name: patientData.lastName,
date_of_birth: patientData.dateOfBirth,
email: patientData.email,
phone: patientData.phone
})
});
const { data } = await response.json();
return data;
}Step 3: Create the Appointment
Book the appointment with idempotency protection to prevent double-bookings on network retries.
async function createAppointment(patientId, template, startTime) {
const endTime = new Date(
new Date(startTime).getTime() + template.duration * 60000
).toISOString();
const response = await fetch(`${API_BASE}/appointments`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID() // Prevents double-booking on retry
},
body: JSON.stringify({
patient_id: patientId,
appointment_type_id: template.id,
title: template.name,
start_time: startTime,
end_time: endTime,
status: 'confirmed'
})
});
if (!response.ok) {
const { error } = await response.json();
throw new Error(error.message);
}
const { data } = await response.json();
return data;
}The Idempotency-Key header ensures that if a network failure causes a retry, the same appointment won't be created twice. Use a unique UUID for each booking attempt.
Error Handling
| Error Code | HTTP Status | Recovery |
|---|---|---|
VALIDATION_ERROR | 422 | Check required fields (title, start_time, end_time) |
CONFLICT | 409 | Idempotency key reused with different body |
RATE_LIMITED | 429 | Exponential backoff and retry |
UNAUTHORISED | 401 | Check API key |
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status === 429) {
const delay = Math.pow(2, attempt) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
return response;
}
throw new Error('Max retries exceeded');
}Next Steps
- Learn about Patient Sync for managing patient records
- Set up Episodes to track post-appointment workflows
- Explore the API Reference for all endpoints