Book Appointments

Drop a self-service slot picker on any page. Visitors see your live availability, pick a time, fill in name + email (plus any custom fields), and walk away with a confirmation email and an .ics calendar invite. You see every booking in your dashboard.

How It Works

  1. Visitor opens your page; the embed renders the booking widget inside Shadow DOM.
  2. The widget fetches free slots for the next N days from /api/embed/availability, scoped to your account + the message's settings.
  3. Visitor picks a date, then a time slot. The UI greys out slots that are already taken.
  4. Visitor fills in name, email, and any custom fields you configured, then submits.
  5. Server validates the slot is still free, persists the booking, sends the confirmation email, and fires any configured webhook.

Owner Configuration

Open Dashboard -> Messages -> New -> Book Appointment, or edit an existing booking message. The settings panel covers:

SettingPurpose
weeklyHoursPer-weekday open / close (e.g., Mon-Fri 9-17, Sat 10-13, Sun closed).
slotLength15, 30, 45, 60, 90, or 120 minutes.
bufferGap inserted between back-to-back meetings (default 0).
leadTimeMinimum hours before now that visitors can book (default 1).
bookingHorizonHow many days ahead visitors can book (default 60).
timezoneOwner timezone. Slots stored in UTC; visitor sees their local time via Intl.DateTimeFormat.
blockedDatesOne-off blackouts (holidays, conferences) without changing recurring hours.
customFieldsExtra inputs beyond name + email (e.g., "What would you like to discuss?").
appointmentWebhookUrlPOSTed JSON for every successful booking (Slack, Zapier, your CRM).

Race-Resolved Booking

Two visitors can hit Submit on the same slot at almost the same time. The server validates the slot is still free at the moment of write; the loser gets a 409 Conflict response and the calendar UI refreshes to hide the now-taken slot. The winner is the oldest write that lands.

Endpoint: POST /api/embed/appointments/submit. Body includes messageId, slotStartISO, name, email, plus any custom field values keyed by their configured name.

Confirmation Email + .ics + Provider Deep Links

On a successful booking the visitor receives an email with:

  • A universal .ics file attachment (works on every calendar app on every OS).
  • An Add to Google Calendar deep link.
  • An Add to Outlook / Office 365 deep link.
  • The booking summary (date, time, owner timezone, custom field values).

Email is sent via Resend from info@floatmessage.com. The visitor sees the date in their own timezone; the owner sees it in the timezone configured on the message.

Webhook Payload

Set appointmentWebhookUrl on the booking message and FloatMessage POSTs JSON to that URL on every successful booking:

{
  "type": "appointment_booked",
  "messageId": "msg_abc123",
  "bookingId": "apt_xyz789",
  "name": "Jane Doe",
  "email": "jane@example.com",
  "slotStartUTC": "2026-05-14T15:00:00.000Z",
  "slotEndUTC":   "2026-05-14T15:30:00.000Z",
  "ownerTimezone": "America/Los_Angeles",
  "visitorTimezone": "Europe/Paris",
  "pageUrl": "https://example.com/pricing",
  "country": "FR",
  "customFields": {
    "topic": "Pricing question"
  },
  "submittedAt": "2026-05-13T10:42:09.501Z"
}

Plug into Slack via the Incoming Webhook integration, or into Zapier / Make / n8n / your own backend.

Owner Dashboard

Open Dashboard -> Appointments for a month-grid calendar with daily booking counts. Click any day for the detailed list - visitor name, email, time, custom fields. From there you can:

  • Copy the visitor email or open a mailto: draft.
  • Cancel a booking (frees the slot for someone else, sends a cancellation email if configured).
  • Filter by message (if you run several different booking offers).

Targeting + Smart Triggers

Booking messages support every targeting rule the rest of FloatMessage supports - URL pattern, country, device class, schedule windows, exit intent, dismiss behaviors. A common setup is to show the booking widget only on the pricing page for desktop visitors during business hours, with a session-only dismiss so it reappears on the next visit.

See Scheduling & Triggers and Geo Targeting for the full rule list.

Related