Calendly-style booking primitives: define event types, expose available time slots, and accept bookings with automatic conflict detection.

Schedule API

The SimplySchedule API provides Calendly-style booking primitives: define event types with weekly availability, expose free time slots to visitors, and accept bookings via a public endpoint with automatic conflict detection.

Concepts

  • Event type — a bookable offering (e.g. "30 min discovery call") with a duration, optional buffers, and weekly availability windows
  • Availability window — a recurring weekly slot (day of week + start/end time) when bookings can be created against an event type
  • Booking — a single reservation against an event type with a confirmed/cancelled/completed/no_show status

Authentication

Reading public availability and creating bookings do not require an API key. Listing bookings, creating or modifying event types, and updating bookings require a project-scoped API key in the x-api-key header.

Create an Event Type

POST /api/v1/schedule/event-types

Creates a bookable event type and (optionally) its weekly availability windows in a single call.

JavaScript
const res = await fetch('https://www.simplystack.dev/api/v1/schedule/event-types', {
  method: 'POST',
  headers: {
    'x-api-key': 'YOUR_PROJECT_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    slug: 'discovery-call',
    name: '30 min Discovery Call',
    description: 'Quick intro chat to see if we are a fit.',
    duration_min: 30,
    buffer_before_min: 5,
    buffer_after_min: 5,
    timezone: 'UTC',
    location_type: 'video',
    availability: [
      { day_of_week: 1, start_time: '09:00', end_time: '17:00' },
      { day_of_week: 2, start_time: '09:00', end_time: '17:00' },
      { day_of_week: 3, start_time: '09:00', end_time: '17:00' },
      { day_of_week: 4, start_time: '09:00', end_time: '17:00' },
      { day_of_week: 5, start_time: '09:00', end_time: '13:00' },
    ],
  }),
});
const { data } = await res.json();
console.log(data.id);

Request Body

  • slug (string, required) — lowercase / hyphenated, unique per project, max 80 chars
  • name (string, required) — display name
  • duration_min (integer, required) — 1 to 1440
  • buffer_before_min, buffer_after_min (integer, default 0) — minutes blocked around each booking
  • timezone (string, default "UTC") — IANA tz name stored with the event type. NOTE: the current availability and booking endpoints interpret all times in UTC; timezone-aware slot computation is planned for a future release.
  • location_type (string, default "video") — video | phone | in_person | custom
  • location_value (string, optional) — link, address, or note
  • active (boolean, default true) — inactive event types reject new bookings
  • availability (array, optional) — weekly windows: { day_of_week (0=Sun..6=Sat), start_time, end_time }

List, Read, Update, Delete Event Types

  • GET /api/v1/schedule/event-types — list this project's event types with their availability
  • GET /api/v1/schedule/event-types/:id — single event type
  • PUT /api/v1/schedule/event-types/:id — update fields; if you pass an `availability` array, all existing windows are replaced
  • DELETE /api/v1/schedule/event-types/:id — delete event type and all related bookings (cascade)

Get Available Slots (Public)

GET /api/v1/schedule/availability/:eventTypeId

Returns free booking slots over a date range. No API key required for active event types.

JavaScript
const res = await fetch(
  `https://www.simplystack.dev/api/v1/schedule/availability/${eventTypeId}` +
  `?date=2026-05-01&days=14`
);
const { data } = await res.json();
// data.days = [{ date: '2026-05-04', slots: ['2026-05-04T09:00:00Z', ...] }]

Query Parameters

  • date (YYYY-MM-DD, default today) — start of the range
  • days (number, default 14, max 60) — how many days to check

Each slot start is returned in UTC ISO-8601 format. The slot duration equals the event type's duration_min, and slots that conflict with existing confirmed bookings (including buffers) are excluded automatically.

Create a Booking (Public)

POST /api/v1/schedule/bookings

Books a slot. The server validates that the requested start time falls inside an availability window and does not collide with another confirmed booking (with buffers applied).

JavaScript
const res = await fetch('https://www.simplystack.dev/api/v1/schedule/bookings', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    event_type_id: 'EVENT_TYPE_ID',
    scheduled_at: '2026-05-04T14:00:00Z',
    attendee_name: 'Jane Doe',
    attendee_email: 'jane@example.com',
    attendee_notes: 'Looking forward to chatting!',
  }),
});
const { data } = await res.json();
console.log(data.status); // "confirmed"

Request Body

  • event_type_id (uuid, required)
  • scheduled_at (ISO-8601, required) — must be in the future
  • attendee_name (string, required) — max 200 chars
  • attendee_email (string, required) — must be a valid email
  • attendee_notes (string, optional)
  • metadata (object, optional)

Errors

  • 400 — invalid input or attempting to book in the past
  • 403 — event type is inactive
  • 404 — event type not found
  • 409 — slot is outside availability or already booked

Manage Bookings

  • GET /api/v1/schedule/bookings — list bookings for the project; filter with event_type_id, status, from, to, limit
  • PATCH /api/v1/schedule/bookings/:id — update status (confirmed | cancelled | completed | no_show), notes, or cancellation reason
  • DELETE /api/v1/schedule/bookings/:id — hard delete a booking

Example: Embedding a Booking Page

A minimal Calendly-style flow: fetch slots, render a date/time picker, then post the booking.

JavaScript
// 1) Load slots for the next two weeks
const eventTypeId = 'EVENT_TYPE_ID';
const slotsRes = await fetch(
  `https://www.simplystack.dev/api/v1/schedule/availability/${eventTypeId}?days=14`
);
const { data: avail } = await slotsRes.json();

// 2) When the user picks a slot, submit the booking
async function book(slotIso, name, email) {
  const res = await fetch(
    'https://www.simplystack.dev/api/v1/schedule/bookings',
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        event_type_id: eventTypeId,
        scheduled_at: slotIso,
        attendee_name: name,
        attendee_email: email,
      }),
    }
  );
  if (res.status === 409) throw new Error('Slot just got taken');
  return (await res.json()).data;
}
Last updated: 4/28/2026