# Build your own booking page

Instead of sending the recipient to Cronofy's booking page, your application
can offer the times itself, for example in your own booking page, a chat, or
an AI agent's conversation. Cronofy still books the event exactly as its own
booking page would, with the same calendar writes, notifications, and
webhooks.

You'll need a Scheduling Request from
[Create a request with the API](https://docs.cronofy.com/developers/getting-started/first-api-request/index.md),
or from the [Embed](https://docs.cronofy.com/developers/getting-started/first-embed-request/index.md).
Create a new one if you've already booked it.

## Find the operations
A Scheduling Request lists the actions your application can take in its
`integration_operations`. If you created the request with the API, they're in
the response. Otherwise,
[query the request](https://docs.cronofy.com/api-reference/managed-scheduling/scheduling-requests/query/index.md)
by its `scheduling_request_id`:

```http
POST /v1/scheduling_requests/query HTTP/1.1
Host: {data_center_url}
Authorization: Bearer {ACCESS_TOKEN}
Content-Type: application/json; charset=utf-8

{
  "scheduling_request_ids": ["{SCHEDULING_REQUEST_ID}"]
}
```

```json
{
  "integration_operations": {
    "simple_available_slots_url": "{SIMPLE_AVAILABLE_SLOTS_URL}"
  }
}
```

Use these URLs exactly as Cronofy gives them to you. Don't build them
yourself or rely on their format, as it can change.

`simple_available_slots_url` is only there when a time is all the request
needs. A request that asks the recipient for more, such as a phone number,
needs Cronofy's booking page.

## List the times
Call `simple_available_slots_url` with the same access token:

```http
POST {SIMPLE_AVAILABLE_SLOTS_URL} HTTP/1.1
Authorization: Bearer {ACCESS_TOKEN}
```

```json
{
  "integration_operation_result": {
    "type": "available_slots",
    "result": "success",
    "integration_operations": {
      "simple_select_slot_url": "{SIMPLE_SELECT_SLOT_URL}"
    },
    "available_slots": [
      { "start": "2026-11-13T09:00:00Z", "end": "2026-11-13T09:30:00Z" },
      { "start": "2026-11-13T09:30:00Z", "end": "2026-11-13T10:00:00Z" },
      { "start": "2026-11-13T14:00:00Z", "end": "2026-11-13T14:30:00Z" }
    ],
    "suggested_slots": [
      { "start": "2026-11-13T09:00:00Z", "end": "2026-11-13T09:30:00Z" },
      { "start": "2026-11-13T14:00:00Z", "end": "2026-11-13T14:30:00Z" }
    ]
  },
  "scheduling_request": { ... }
}
```

Show the recipient the `available_slots`, or the `suggested_slots` if you
want to offer a short, spread-out list. Times are in UTC, so convert them to
the recipient's time zone for display.

## Book the chosen time
Post the `start` of the time the recipient picks to `simple_select_slot_url`:

```http
POST {SIMPLE_SELECT_SLOT_URL} HTTP/1.1
Authorization: Bearer {ACCESS_TOKEN}
Content-Type: application/json; charset=utf-8

{
  "start": "2026-11-13T09:30:00Z"
}
```

```json
{
  "integration_operation_result": {
    "type": "select_slot",
    "result": "accepted",
    "integration_operations": {},
    "selected_slot": {
      "start": "2026-11-13T09:30:00Z",
      "end": "2026-11-13T10:00:00Z"
    }
  },
  "scheduling_request": { ... }
}
```

An `accepted` result means Cronofy has taken the booking. The event is
written to the calendars moments later, and you receive the invitation.

Your [webhook URL](https://docs.cronofy.com/developers/getting-started/set-up-a-sandbox/index.md)
receives the same `scheduling_request_time_chosen` webhook as when a
recipient books on Cronofy's booking page, so the rest of your application
handles both the same way.

## Handle a time that’s gone
If someone else books the time first, the result is `not_possible` with a
`result_detail` of `slot_unavailable`. The response includes a fresh list of
times and a new `simple_select_slot_url`, so ask the recipient to pick again.

If the request can no longer be booked this way, for example because it's
been cancelled, the `result_detail` says why. Where a person can still act,
send them to the request's `recipient_operations.view_url`.

[Integration Operations](https://docs.cronofy.com/api-reference/managed-scheduling/scheduling-requests/integration-operations/index.md)
covers every result, and
[Available Slots](https://docs.cronofy.com/api-reference/managed-scheduling/scheduling-requests/available-slots/index.md)
and [Select Slot](https://docs.cronofy.com/api-reference/managed-scheduling/scheduling-requests/select-slot/index.md)
have the full detail of each call.

## In your product
### In an applicant tracking system
Offer the times in your candidate portal or careers site, or through an AI agent that is already talking to the candidate.

### In talent management
Offer the times in your employee self-service portal, or through an AI agent in the tools your customers' employees already use.

### In a CRM
Offer the times on a booking page in your product, or through an AI agent in the conversation with the prospect.

### In healthtech
Offer the times in your patient portal or app, so patients book without leaving it.

## Next
[Use Selection Rules and Templates](https://docs.cronofy.com/developers/getting-started/rules-and-templates/index.md)
so Cronofy chooses the host, and requests come from predefined meeting types.



---
[Read in HTML](https://docs.cronofy.com/developers/getting-started/headless-selection/)