Creating Appointments

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:

  1. List appointment templates — show available service types
  2. Find or create a patient — search existing records or create new
  3. Create the appointment — book with idempotency protection

Required API Scopes

Your API key needs these scopes:

ScopePurpose
read_appointment_templatesList available service types
read_patientsSearch existing patients
write_patientsCreate new patients
write_appointmentsCreate 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 CodeHTTP StatusRecovery
VALIDATION_ERROR422Check required fields (title, start_time, end_time)
CONFLICT409Idempotency key reused with different body
RATE_LIMITED429Exponential backoff and retry
UNAUTHORISED401Check 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