# Scheduling & booking

> Public booking pages, the embed script, booking events and integrations through automations.

Source: https://tickos.dev/docs/scheduling

Scheduling lets a workspace publish calendars (meeting types) that customers book from a public page or an embed on your own site. Each booking sends confirmation and reminder emails, creates or updates the contact, and can open a ticket in an inbox. Calendars, availability and questions are configured in the dashboard (Scheduling).

## Availability by plan

| Plan | Calendars | Google Calendar sync |
|---|---|---|
| Free | Not included | No |
| Starter | 1 | No |
| Pro | 10 | Yes |
| Enterprise | Unlimited | Yes |

Google Calendar sync checks the host's busy times when offering slots, creates an event for each booking and can generate a Google Meet link.

## Public URLs

Booking pages live on the help center host (https://{slug}.tickos.dev, or your custom domain on Pro and Enterprise). {calendar} is the calendar slug.

| URL | Content |
|---|---|
| /book | List of active calendars. Only shown when "Book a meeting" is enabled in Help Center settings. |
| /book/{calendar} | Booking page for one calendar. Works for any active calendar, even if /book is disabled. |
| /book/manage/{token} | Link sent in the emails so the invitee can cancel or reschedule without logging in. |
| /book/manage/{token}/ics | Downloads the booking as an .ics calendar file. |
| /embed/{calendar} | Booking widget without the help center layout, meant for iframes. |
| /embed.js | Embed script (inline, popup and booking event). |

## Embedding on your site

Load embed.js from your help center host and point it to the /embed/{calendar} URL. The iframe resizes itself to fit its content, and /embed/* pages can be framed from any origin.

### Inline

```html
<div data-tickos-booking="https://acme.tickos.dev/embed/intro-call"></div>
<script src="https://acme.tickos.dev/embed.js" async></script>
```

### Popup

Any clickable element with data-tickos-booking-popup opens the booking widget in a modal. Close it with the X button, Escape or a click outside.

```html
<button data-tickos-booking-popup="https://acme.tickos.dev/embed/intro-call">
  Book a meeting
</button>
<script src="https://acme.tickos.dev/embed.js" async></script>
```

Or open it from JavaScript:

```javascript
window.TickOS.openBooking('https://acme.tickos.dev/embed/intro-call')
window.TickOS.closeBooking()
// Re-scan the page after inserting data-tickos-booking elements dynamically
window.TickOS.mount()
```

### Theme

Add data-theme="light" or data-theme="dark" to the element to force a theme. Otherwise the widget follows the help center theme settings. When linking to /embed/{calendar} directly, use ?theme=light or ?theme=dark.

### Booking event

When a booking is completed inside an embedded widget, embed.js dispatches a tickos:booked CustomEvent on the host page window. Use it for analytics or to continue your own flow:

```javascript
window.addEventListener('tickos:booked', (e) => {
  // e.detail = {
  //   type: 'tickos:booked',
  //   calendar: 'intro-call',
  //   booking: { start_at: '2026-10-20T15:00:00.000Z', end_at: '2026-10-20T15:30:00.000Z' }
  // }
  analytics.track('meeting_booked', e.detail)
})
```

> **Tip:** The same embed.js also powers inbox form embeds (data-tickos-form attributes). A single script tag is enough for both.

## What happens on a booking

- The invitee is created or matched as a contact (source: scheduling).
- Confirmation and host notification emails are sent from the calendar's verified sending domain (or a TickOS no-reply address if none is set); reminders are scheduled. These emails count toward the plan email quota and respect suppressed recipients.
- If the calendar has a ticket inbox configured, a ticket is created there. It fires the usual ticket.created inbox webhooks; cancellations and reschedules are added to the same ticket as internal notes.
- Two confirmed bookings can never overlap on the same calendar: if a slot is taken while the invitee is booking, the widget asks them to pick another time.
- Automations with a booking trigger are started (see below).

## Integrating bookings with your systems

There is no API-key endpoint for calendars or bookings. To push bookings into your CRM or backend, create an automation (Pro and Enterprise) with one of these triggers and add a Webhook step:

| trigger_type | Webhook trigger_event | Fired when |
|---|---|---|
| booking_scheduled | booking.scheduled | A new booking is confirmed. |
| booking_rescheduled | booking.rescheduled | The invitee moves a booking to a new time. |
| booking_cancelled | booking.cancelled | A booking is cancelled. |

The Webhook step payload includes the contact plus the resolved booking and calendar:

```json
{
  "event": "automation.step.webhook",
  "trigger_event": "booking.scheduled",
  "timestamp": "2026-10-13T09:12:00.000Z",
  "automation": { "id": "…", "name": "New demo booked", "trigger_type": "booking_scheduled" },
  "enrollment": { "id": "…", "enrolled_at": "…" },
  "customer": { "id": "cst-…", "email": "jane@example.com", "name": "Jane Doe", … },
  "trigger": {
    "type": "booking_scheduled",
    "event": "booking.scheduled",
    "booking": {
      "id": "bkg_…", "status": "confirmed",
      "invitee_name": "Jane Doe", "invitee_email": "jane@example.com",
      "invitee_timezone": "Europe/Madrid", "answers": { … },
      "start_at": "…", "end_at": "…", "ticket_id": "tck-…",
      "manage_url": "https://acme.tickos.dev/book/manage/…",
      "meeting_url": "https://meet.google.com/…"
    },
    "calendar": { "id": "cal_…", "name": "Intro call", "slug": "intro-call", "duration_minutes": 30, … }
  }
}
```

Booking data is also available as {{booking_*}} variables (for example {{booking_start_date}}, {{booking_meeting_url}}, {{booking_manage_url}}) in automation emails. Sequences started by a booking exit automatically when it is cancelled, and the exit condition on_booking removes a contact from other automations once they book. See the Automations page (/docs/automations) for the step and trigger reference.
