# How to book a room for a meeting

Source: https://developer.nylas.com/docs/cookbook/calendar/book-room-resources/

You scheduled the meeting, invited everyone, and forgot the room. Now two teams are standing in the same doorway. Booking a room or piece of equipment is a two-step job: list the resources your organization has, then attach the one you want to the event you create. Both Google Workspace and Microsoft 365 model rooms as calendars with their own email addresses, and the [Calendar API](/docs/reference/api/events/create-event/) reserves them the same way for both.

This recipe shows how to list the room resources available to a grant, then book one by adding its email to an event's `resources` array. The same flow reserves projectors, vehicles, or any bookable equipment your admin has registered.

## Why use Nylas instead of a provider room API directly?

Google and Microsoft both expose room data, but through completely different shapes. Going direct means maintaining code for 2 providers. The API collapses both providers into a single resources endpoint and one event field:

- **One schema for two providers.** Google returns resources from the Admin SDK Directory API; Microsoft returns them from the Graph `places` endpoint. Nylas normalizes both into one `room_resource` object.
- **One booking field.** You add the room's email to the event's `resources` array regardless of provider, instead of learning Graph attendee `type` values or Google `resourceEmail` semantics.
- **Admin scopes handled in auth.** Listing rooms needs directory-read scopes that the API requests during the grant's OAuth flow, so you don't manage two separate consent screens.

## Before you begin

You need a connected Google Workspace or Microsoft 365 grant, an API key from the [Nylas Dashboard](https://dashboard.nylas.com/), and a calendar you can write to. Listing rooms also requires directory-read access, which an admin grants once for the whole organization. Personal Gmail and Outlook.com accounts don't expose room resources, since rooms live in the workspace directory.


> **Info:** 
> **New to Nylas?** Start with the [quickstart guide](/docs/v3/getting-started/) to set up your app and connect a test account before continuing here.


### Directory scopes for listing rooms

Room resources live in the organization's directory, so the read scope is admin-controlled, not per-user. For Google, the grant needs `admin.directory.resource.calendar.readonly`. For Microsoft, it needs `Place.Read.All`. Both are admin-consent scopes: a Workspace or Microsoft 365 administrator approves them once, and every grant in that tenant can then list rooms. Booking a room only needs standard calendar write scopes, so a grant can reserve a room even when it can't list every room in the directory.

## List the room resources

Send a `GET` request to `/v3/grants/{grant_id}/resources` to return every room and equipment resource the directory exposes to the grant. The response is a `data` array of `room_resource` objects, each with an `email`, `name`, `capacity`, `building`, and floor fields. The `email` is the value you'll book against. The default page size is 50 resources, paged with the `next_cursor` token.

```bash
curl --compressed --request GET \
  --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/resources' \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer <NYLAS_API_KEY>' \
  --header 'Content-Type: application/json'

```

```js [listResources-Node.js SDK]
// The Node SDK has no dedicated resources method yet, so read room
// resources directly from the REST /resources endpoint.
const response = await fetch(
  `https://api.us.nylas.com/v3/grants/${process.env.NYLAS_GRANT_ID}/resources`,
  { headers: { Authorization: `Bearer ${process.env.NYLAS_API_KEY}` } },
)
const { data } = await response.json()
console.log(data)
```

```python [listResources-Python SDK]


grant_id = os.environ["NYLAS_GRANT_ID"]
api_key = os.environ["NYLAS_API_KEY"]

response = requests.get(
    f"https://api.us.nylas.com/v3/grants/{grant_id}/resources",
    headers={"Authorization": f"Bearer {api_key}"},
)
print(response.json()["data"])
```

A successful response carries a `request_id` and the resource list. Read the `email` and `capacity` of each room so you can match a 4-person room to a 4-person meeting. Here's a single-room response:

```json
{
  "request_id": "5fa64c92-e840-4357-86b9-2aa364d35b88",
  "data": [
    {
      "building": "West Building",
      "capacity": "8",
      "email": "training-room-1A@example.com",
      "floor_name": "7",
      "floor_section": "7",
      "floor_number": "7",
      "name": "Training Room 1A",
      "object": "room_resource"
    }
  ],
  "next_cursor": "OQ=="
}
```

## Book a room when you create the event

Create the event with a `POST` request to `/v3/grants/{grant_id}/events?calendar_id={calendar_id}` and include the room's email in the `resources` array. The `calendar_id` query parameter is required; use `primary` for the grant's main calendar. Each entry in `resources` takes 2 fields, a `name` and an `email`, where the `email` is the value from the resources list. The provider sends the booking request to the room's calendar, which accepts automatically within seconds if the room is free.

```bash
curl --request POST \
  --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/events?calendar_id=primary' \
  --header 'Authorization: Bearer <NYLAS_API_KEY>' \
  --header 'Content-Type: application/json' \
  --data '{
    "title": "Q3 Roadmap Review",
    "when": {
      "start_time": 1735660800,
      "end_time": 1735664400
    },
    "participants": [
      { "name": "Leyah Miller", "email": "leyah@example.com" }
    ],
    "resources": [
      { "name": "Training Room 1A", "email": "training-room-1A@example.com" }
    ]
  }'
```

```js [bookRoom-Node.js SDK]
const event = await nylas.events.create({
  identifier: process.env.NYLAS_GRANT_ID,
  queryParams: { calendarId: "primary" },
  requestBody: {
    title: "Q3 Roadmap Review",
    when: { startTime: 1735660800, endTime: 1735664400 },
    participants: [{ name: "Leyah Miller", email: "leyah@example.com" }],
    resources: [
      { name: "Training Room 1A", email: "training-room-1A@example.com" },
    ],
  },
})
console.log(event)
```

```python [bookRoom-Python SDK]
from nylas import Client

nylas = Client(api_key=os.environ["NYLAS_API_KEY"])

event = nylas.events.create(
    identifier=os.environ["NYLAS_GRANT_ID"],
    query_params={"calendar_id": "primary"},
    request_body={
        "title": "Q3 Roadmap Review",
        "when": {"start_time": 1735660800, "end_time": 1735664400},
        "participants": [{"name": "Leyah Miller", "email": "leyah@example.com"}],
        "resources": [
            {"name": "Training Room 1A", "email": "training-room-1A@example.com"}
        ],
    },
)
print(event)
```

The room shows up in the event's `resources` and `participants` once the provider processes the booking. If the room is already taken, the provider declines the invite and the event keeps the slot without the room, so check the response after a few seconds.

## Things to know about room resources

A room is a mailbox with a calendar, so booking one means sending it an invite, and a few quirks fall out of that. Knowing them up front saves a round of debugging when a room fails to confirm.

- **Acceptance depends on the room's policy.** Google and Microsoft let admins configure rooms to accept automatically, decline conflicts, or require manual approval by a delegate. A room set to manual approval won't confirm instantly, so don't treat a `200` create response as a guaranteed reservation.
- **Double-booking is provider-enforced.** If 2 events request the same room for overlapping times, the room's calendar declines the second one. The API surfaces the decline in the event's resource status, not as a create error.
- **`email` is the stable identifier.** Resource `name` values like "Training Room 1A" can be renamed by admins, but the `email` stays constant. Store the email, not the display name, when you cache rooms.
- **Equipment uses the same model.** Projectors, cars, and other bookable equipment are registered as resource calendars too, so they appear in the same resources list and book through the same `resources` array.
- **Personal accounts return an empty list.** Consumer Gmail and Outlook.com grants have no directory, so the resources endpoint returns an empty `data` array. Room booking is a workspace feature. See [Google Workspace resource calendars](https://support.google.com/a/answer/1686462) for how admins create rooms.

## Paginate through large room directories

Large organizations register hundreds of rooms across buildings, so the resources endpoint returns 50 rooms at a time by default. Each response includes a `next_cursor` token when more rooms exist. Pass it back as the `page_token` query parameter to fetch the next page, and stop when `next_cursor` is absent. This mirrors how the [Events API paginates](/docs/cookbook/calendar/google-calendar-api-pagination/) lists.

```bash
curl --request GET \
  --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/resources?limit=50&page_token=<NEXT_CURSOR>' \
  --header 'Authorization: Bearer <NYLAS_API_KEY>'
```

```python [paginateResources-Python SDK]
rooms, cursor = [], None
while True:
    params = {"limit": 50}
    if cursor:
        params["page_token"] = cursor
    res = requests.get(
        f"https://api.us.nylas.com/v3/grants/{grant_id}/resources",
        headers={"Authorization": f"Bearer {api_key}"},
        params=params,
    ).json()
    rooms.extend(res["data"])
    cursor = res.get("next_cursor")
    if not cursor:
        break
```

## What's next

- [Check Free/Busy status](/docs/cookbook/calendar/check-free-busy/) to confirm attendees are free before you reserve a room
- [Create Microsoft calendar events](/docs/cookbook/calendar/events/create-events-microsoft/) for Teams conferencing and write scopes
- [Create Google calendar events](/docs/cookbook/calendar/events/create-events-google/) for automatic Google Meet links and invites
- [Manage calendars](/docs/cookbook/calendar/manage-calendars/) to find the `calendar_id` you book the event on
- [Room resources API reference](/docs/reference/api/room-resources/list-room-resources/) for every field the resources endpoint returns