# Appointments from Booking Orbit

> How Service, Services and People fields and the Booking Orbit blocks are delivered, and how to take bookings on your own pages with the Booking Orbit SDK.

Source: https://docs.bifrostorbit.com/cms/developers/booking-blocks/

A space connected to a Booking Orbit location under **Configuration › Orbit connections** has three more field types and four blocks. The entry stores only what was picked, `{ "id", "name" }`. Every answer from the delivery API, the SDK and GraphQL fills in the rest from Booking Orbit's public booking API, kept for at most a minute, and fresh in preview.

Connecting also adds the space's verified domains and preview addresses to the location's allowed websites, so your pages can book in the visitor's browser without the booking widget. The widget is for websites not built with CMS Orbit.

## A service

```json
"service": {
  "id": "6c0000000000000000000001",
  "slug": "boot-fitting",
  "name": "Boot fitting",
  "summary": "Foot scan, try-on and heat moulding, so new boots fit from the first walk.",
  "category": "Fitting",
  "image": { "url": "https://images.unsplash.com/photo-1575987116913-e96e7d490b8a?…", "alt": "Boot fitting" },
  "durationMinutes": 45,
  "mode": "in-person",
  "priceKind": "fixed",
  "price": 45000,
  "currency": "NOK",
  "payment": "none",
  "deposit": null,
  "staffIds": ["6c00…a1", "6c00…a2"],
  "nextAvailable": "2026-11-02T09:00:00.000Z",
  "bookingUrl": "https://app.bifrostorbit.com/booking/b/northwilds-oslo/boot-fitting",
  "available": true
}
```

- **Prices** are in minor units, such as øre: `45000` is 450 kr. `priceKind` is `fixed`, `from`, `free` or `hidden`. `bookingPrice(service, locale)` from the SDK formats it.
- **`payment`** is `none`, `deposit` (with `deposit` in minor units) or `full`. A booking that needs payment comes back with a payment address to send the customer to.
- **`nextAvailable`** is the first free time in the coming two weeks, in UTC, or `null`.
- **`bookingUrl`** is the service on Booking Orbit's own booking page, for visitors without JavaScript.
- **`available: false`** means the service is hidden or removed, or Booking Orbit could not be reached and had never answered. The picked name is kept; leave it out or show it without a button.

A person arrives as `{ id, name, title, bio, photo, available }`.

## The blocks

Every block also arrives with `booking`: `{ baseUrl, location, name, kind, timeZone, currency, locales, address, phone, email, openingHours, policies }`. `baseUrl` and `location` are what the Booking Orbit SDK needs.

| Block | Key | Fields it arrives with |
| --- | --- | --- |
| Services | `bookingServiceList` | `eyebrow`, `heading`, `intro`, `services`, `bookLink`, `limit`, and `items`: the picked services, or every service when none are picked |
| Book an appointment | `bookingAppointment` | `heading`, `intro`, `service`, `confirmation`, and `items` (every service) and `team` (every person) |
| Next available times | `bookingNextTimes` | `heading`, `service`, `bookLink`, `limit`, and `times`: `{ startsAt, staffIds }[]` for the service |
| Meet the team | `bookingTeam` | `eyebrow`, `heading`, `intro`, `staff`, and `items`: the picked people, or everyone who takes bookings |

`bookLink` is the page the editor chose to book on. Open it with the service, and the time, in the address, such as `/services?service=boot-fitting&start=2026-11-02T09:00:00.000Z`, so the booking block starts there.

## Book on the page

The **Book an appointment** block gives you what to start with. Take the booking itself with the Booking Orbit SDK, in the browser:

```tsx
'use client'
import { createBookingClient } from '@bifrost-orbit-booking/sdk'
import type { CmsBookingLocation } from '@bifrost-orbit-cms/sdk'

export async function bookFirstTime(booking: CmsBookingLocation, service: string, customer: { name: string; email: string }) {
  const client = createBookingClient({ baseUrl: booking.baseUrl, location: booking.location, locale: 'en' })
  const slots = await client.slots({ service, timeZone: booking.timeZone, days: 7 })
  const time = slots.days.flatMap((day) => day.slots)[0]
  if (!time) return null
  const hold = await client.hold({ service, startsAt: time.startsAt })
  return client.book({ service, startsAt: time.startsAt, holdToken: hold.token, ...customer })
}
```

The answer has the appointment, whose `manageUrl` ends in its manage token for moving or cancelling, and `payment`, with a `url` to send the customer to when the service needs paying online. See [Booking Orbit SDK](https://docs.bifrostorbit.com/booking/developers/booking-sdk/) for every call.

## Refreshing pages

Booking Orbit does not send `content.published` when a service or a free time changes. Free times change all the time, so cache pages with Booking Orbit blocks for a minute or two at most, or read times in the browser with the SDK.
