> ## Documentation Index
> Fetch the complete documentation index at: https://docs.yorlet.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Book a viewing

> Learn how to look up bookable slots and book viewings with the Yorlet API.

export const APIBadge = () => {
  return <div style={{
    display: 'inline-block',
    backgroundColor: 'rgb(var(--primary-light))',
    color: '#ffffff',
    padding: '1px 4px',
    borderRadius: '.375rem',
    borderColor: 'rgb(var(--gray-200))',
    borderWidth: '1px',
    position: 'relative',
    top: '-2px',
    marginLeft: '4px',
    fontSize: '11px',
    fontWeight: 700,
    letterSpacing: '0.05em',
    lineHeight: '11px'
  }}>
      API
    </div>;
};

This guide shows you how to book a property viewing using the <a href="/api/leads/leads-viewings/create" target="_blank">Viewings API<APIBadge /></a>. Viewings are booked against an [enquiry](/development/integrations/leads/create-an-enquiry), and Yorlet handles the confirmation, the calendar invite, the reminder, and the follow-up for you.

## Prerequisites

Before booking a viewing, ensure you have:

* The Leads product enabled on your account.
* A [secret key](/development/api-keys).
* An enquiry ID.
* Viewing availability configured on the account, if you want to offer bookable slots.

<Warning>
  Your secret key can perform any action on your account, so only ever use it from your own server. Never embed it in a booking page, a mobile app, or anything else a lead can see.

  If you want leads to pick their own slot, send them a [booking link](#let-the-lead-book-their-own-slot) rather than calling the slots endpoint from the browser. The hosted page authenticates itself with a key scoped to that one enquiry, so your secret key never leaves your server. If a secret key is ever exposed, [roll it](/development/api-keys) immediately.
</Warning>

## Permissions

| Permission             | Grants                                              |
| ---------------------- | --------------------------------------------------- |
| `leads.viewings.read`  | Retrieve and list viewings, and list bookable slots |
| `leads.viewings.write` | Book, reschedule, and transition viewings           |

## Look up bookable slots

Slots are derived from the account's weekly viewing availability, minus anything already on the calendar. They respect the configured viewing duration, buffer, minimum notice, and booking horizon, so anything returned is safe to offer a lead.

```bash List bookable slots theme={"theme":"dracula"}
curl "https://api.yorlet.com/v1/leads/viewings/slots?enquiry=le_pXm2Qk8sZ1vBnRtY&from=1712716800&to=1713321600" \
  -H "Authorization: Bearer {{API_KEY}}"
```

```json Response theme={"theme":"dracula"}
{
  "object": "list",
  "data": [
    {
      "object": "leads.viewing_slot",
      "start_time": 1712743200,
      "end_time": 1712745000,
      "duration": 30
    }
  ],
  "count": 1,
  "has_more": false
}
```

| Parameter | Type      | Required | Description                                                              |
| --------- | --------- | -------- | ------------------------------------------------------------------------ |
| `enquiry` | string    | Yes      | The enquiry to compute slots for                                         |
| `from`    | timestamp | No       | Start of the range. Defaults to now                                      |
| `to`      | timestamp | No       | End of the range. Defaults to 14 days after `from`, and must be after it |

<Note>
  An empty `data` array usually means the account has no viewing availability configured, or the whole range falls inside the minimum notice period.
</Note>

## Book a viewing

Only `enquiry` and `start_time` are required. The unit and host default to whatever is on the enquiry, and the duration defaults to the account's viewing duration.

```bash Book a viewing theme={"theme":"dracula"}
curl https://api.yorlet.com/v1/leads/viewings \
  -H "Authorization: Bearer {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
        "enquiry": "le_pXm2Qk8sZ1vBnRtY",
        "start_time": 1712743200,
        "duration": 30,
        "unit": "unit_lrhplff1u4ZhIPSU",
        "assignee": "user_9fRtZm4nQpLxVsKd",
        "notes": "Lead is running late, buzz flat 4"
      }'
```

```json Response theme={"theme":"dracula"}
{
  "id": "lv_3nQpZr7tKmXwBsLd",
  "object": "leads.viewing",
  "created": 1712716800,
  "enquiry": "le_pXm2Qk8sZ1vBnRtY",
  "unit": "unit_lrhplff1u4ZhIPSU",
  "assignee": "user_9fRtZm4nQpLxVsKd",
  "start_time": 1712743200,
  "duration": 30,
  "status": "scheduled",
  "source": "manual",
  "notes": "Lead is running late, buzz flat 4",
  "canceled_at": null,
  "canceled_reason": null,
  "completed_at": null,
  "metadata": {}
}
```

### Viewing parameters

| Parameter    | Type      | Required | Description                                                          |
| ------------ | --------- | -------- | -------------------------------------------------------------------- |
| `enquiry`    | string    | Yes      | The ID of the enquiry to book the viewing for                        |
| `start_time` | timestamp | Yes      | When the viewing starts                                              |
| `duration`   | integer   | No       | Length in minutes (5–480). Defaults to the account viewing duration  |
| `assignee`   | string    | No       | The ID of the user hosting. Defaults to the enquiry's assignee       |
| `unit`       | string    | No       | The ID of the unit being viewed. Defaults to the unit on the enquiry |
| `notes`      | string    | No       | Internal notes recorded against the viewing                          |
| `metadata`   | object    | No       | Key-value pairs for storing additional information                   |

Booking a viewing also advances the enquiry to `viewing` when it is at an earlier stage, so you do not need to transition it yourself.

<Note>
  Requests made with a secret key are only checked for collisions, not against your published availability, so your own team can book outside their usual windows. Bookings made by a lead through their booking link must fall exactly on a published slot.
</Note>

## Let the lead book their own slot

Rather than choosing a time on the lead's behalf, create a booking link on the enquiry and let them pick. Set `notification.type` to `email` to send it to them, or `none` to return the URL and deliver it yourself.

```bash Create a booking link theme={"theme":"dracula"}
curl https://api.yorlet.com/v1/leads/enquiries/le_pXm2Qk8sZ1vBnRtY/booking_link \
  -H "Authorization: Bearer {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{ "notification": { "type": "email" } }'
```

The response is a `leads.viewing_booking_link` object with the `enquiry` it belongs to and the `url` of the hosted booking page. The link is created on first use and reused for the lifetime of the enquiry, and the hosted page handles its own authentication, so you never expose an API key to the lead.

Leads booking through the link can pick a slot, reschedule, and cancel, but they cannot record an outcome. Viewings they book have a `source` of `self_serve`; viewings your team books have `manual`.

<Tip>
  Turn on automatic booking links in the account's viewing settings and Yorlet emails the lead their link as soon as you transition the enquiry to `qualified` — no extra API call needed.
</Tip>

## Reschedule a viewing

Pass a new `start_time` to the <a href="/api/leads/leads-viewings/update" target="_blank">update endpoint<APIBadge /></a>. Yorlet re-checks the slot, sends the lead an updated confirmation, and reschedules the reminder and follow-up.

```bash Reschedule a viewing theme={"theme":"dracula"}
curl https://api.yorlet.com/v1/leads/viewings/lv_3nQpZr7tKmXwBsLd \
  -H "Authorization: Bearer {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{ "start_time": 1712829600 }'
```

You can also update `duration`, `assignee`, `unit`, and `notes`. Pass `null` to `assignee` or `unit` to clear them.

## Record the outcome

Use the <a href="/api/leads/leads-viewings/status" target="_blank">status endpoint<APIBadge /></a> once the viewing has happened, or to call it off.

```bash Mark a viewing as completed theme={"theme":"dracula"}
curl https://api.yorlet.com/v1/leads/viewings/lv_3nQpZr7tKmXwBsLd/status \
  -H "Authorization: Bearer {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{ "status": "completed" }'
```

| Status      | Meaning                                                                         |
| ----------- | ------------------------------------------------------------------------------- |
| `scheduled` | Booked and upcoming. Setting this again clears any recorded outcome             |
| `completed` | The lead attended. Records `completed_at`                                       |
| `canceled`  | Called off. Records `canceled_at` and the optional `reason`, and frees the slot |
| `no_show`   | The lead did not attend, and the slot is freed                                  |

Marking a viewing as `completed` follows up with the lead and reminds the host to move them on to an application.

## What Yorlet sends the lead

Booking a viewing through the API triggers the same lifecycle as booking one in the Dashboard:

| When                  | What happens                                                                        |
| --------------------- | ----------------------------------------------------------------------------------- |
| Booked or rescheduled | The lead is emailed a confirmation with a calendar invite, and the host is notified |
| 24 hours before       | The lead is reminded, unless the viewing has moved or already has an outcome        |
| After the end time    | The lead is followed up with, and the host is nudged to record the outcome          |
| Marked as completed   | The lead is followed up with, and the host is nudged towards an application         |

## List viewings

```bash List upcoming viewings for an enquiry theme={"theme":"dracula"}
curl "https://api.yorlet.com/v1/leads/viewings?enquiry=le_pXm2Qk8sZ1vBnRtY&sort=ascending&expand[]=assignee&expand[]=unit" \
  -H "Authorization: Bearer {{API_KEY}}"
```

| Parameter  | Type            | Description                               |
| ---------- | --------------- | ----------------------------------------- |
| `enquiry`  | string          | Filter to viewings for a single enquiry   |
| `status`   | string or array | Filter by one or more statuses            |
| `source`   | string          | `self_serve` or `manual`                  |
| `unit`     | string          | Filter by the unit being viewed           |
| `assignee` | string          | Filter by host                            |
| `sort`     | string          | `ascending` or `descending` by start time |

`enquiry`, `assignee`, and `unit` can each be expanded with `expand[]`.
