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

# Take bookings

> Discover a location, search availability, hold capacity, and create or change a booking.

Bookings use your app’s **location grants**, not its POS connection. A booking integration can coexist with another app receiving orders at the same location. The surface rolls out per location; ask Maple to enable your test location first.

Set `MAPLE_BASE` to your environment’s base URL, including `/v1`, and `MAPLE_KEY` to a matching credential. See [Environments](https://docs.maple.inc/developer-api/concepts/environments.md). You need `bookings:read` for discovery and reads, and `bookings:write` for holds and guest-side writes.

## 1. Read the booking profile

```bash
curl "$MAPLE_BASE/locations/$LOCATION_ID/booking_profile" \
  -H "Authorization: Bearer $MAPLE_KEY"
```

Check `enabled`, `provider`, and `capabilities`. Native locations have a `hold_ttl_seconds` of 300 by default. Provider-managed locations report `null` because the provider owns expiry; always use the hold’s `expires_at`.

## 2. Search availability

`start` and `end` are venue-local wall-clock times, not UTC instants. Choose a future window no longer than 31 days:

```bash
curl --get "$MAPLE_BASE/locations/$LOCATION_ID/booking_availability" \
  -H "Authorization: Bearer $MAPLE_KEY" \
  --data-urlencode 'party_size=2' \
  --data-urlencode "start=${BOOKING_DATE}T18:00" \
  --data-urlencode "end=${BOOKING_DATE}T21:00"
```

Each `data` entry contains an opaque `slot_token`, `starts_at` in UTC, `starts_at_local`, and any quoted cancellation policy. Tokens expire after 15 minutes. Save the selected token unchanged; never reconstruct it from the displayed time.

A `422` is a business outcome, not an empty result: inspect `code` and `details` for party-size bounds, booking-window restrictions, or a large-party inquiry contact.

### Experiences

Some venues sell experiences: a prix-fixe menu, a tasting, a ticketed seating. Where the location offers them (`booking_profile.capabilities.experiences`), availability lists their slots alongside standard ones, in start order. An experience slot names its `experience`: `id`, `name`, `description`, and `prepayment_required`. Standard slots have `experience: null`.

To show one experience’s times, pass its id:

```bash
curl --get "$MAPLE_BASE/locations/$LOCATION_ID/booking_availability" \
  -H "Authorization: Bearer $MAPLE_KEY" \
  --data-urlencode 'party_size=2' \
  --data-urlencode "start=${BOOKING_DATE}T18:00" \
  --data-urlencode "end=${BOOKING_DATE}T21:00" \
  --data-urlencode 'experience_id=1771'
```

Book an experience slot like any other: its `slot_token` is bound to the experience, so the hold and the booking are for it, and the booking’s `experience` names it. OpenTable can move an experience booking to another time of the same experience, but not to a different experience or a standard table: that change answers `422 capability_unsupported`, so cancel and book the new slot instead. Maple can’t take payment yet, so holding a slot whose experience has `prepayment_required: true` answers `422 payment_instrument_required`. Show those experiences, but offer a standard slot to book. Maple-native locations don’t offer experiences yet: `capabilities.experiences` is `false` and `experience_id` answers `422 capability_unsupported`.

## 3. Hold the slot

```http
POST /v1/booking_holds
Content-Type: application/json


{ "slot_token": "<selected slot_token>" }
```

Save `id` as the hold ID. The hold claims capacity until `expires_at`; create the booking before then. If the guest abandons the flow, release a native hold with `DELETE /v1/booking_holds/{holdId}`. A consumed hold answers `409 hold_consumed` instead of reporting that its capacity was released.

Each app may have **ten live holds per location**, enforced atomically across concurrent requests. Native and provider holds count, including provider acquisitions still awaiting an outcome. A different app cannot release or consume your hold, even if both apps are granted the location; those requests return `404`.

If a provider hold response is lost, its quota reservation has a conservative 24-hour quarantine after the last possible provider attempt. Once that deadline passes, it no longer consumes quota. The original operation stays recorded and cannot restart after its replay window; this does not clear an uncertain booking create/change/cancel operation.

All partner hold IDs use `bkh_…`. Repeating the same `slot_token` replays your live hold rather than acquiring another lock. OpenTable release remains a no-op: the provider lock and its quota usage remain until expiry or consumption. Check `booking_profile.capabilities.hold_release` before promising an early release.

## 4. Create the booking

```http
POST /v1/bookings
Content-Type: application/json


{
  "hold_id": "<hold id>",
  "guest": {
    "first_name": "Alex",
    "last_name": "Quinn",
    "phone": "+12125551234",
    "email": "alex@example.com"
  },
  "notes": "Window table if possible",
  "idempotency_key": "<unique key for this write>"
}
```

The response is the full `booking` resource. At native locations, its initial status is `confirmed` or `requested`, according to the venue’s acceptance policy. Save its `id` and `version`. Guest phone is required; the current service accepts dialable North American numbers.

The persisted hold supplies the authoritative quote for both native and provider-managed bookings. You can omit `slot_token`; it remains accepted as an optional field for existing integrations.

> [!WARNING]
>
> A timeout or `409 operation_in_progress` does not mean the provider rejected the booking. Retry with the **same key and body**, never a new key. Maple records the pending command before provider I/O and reconciles uncertain results using the same provider request identity. If `details.reason` is `replay_window_expired`, stop automatic retries and contact Maple with `details.operation_id`. See [Replay safety](https://docs.maple.inc/developer-api/concepts/idempotency.md).

## 5. Change or cancel

For a native change, search again with `changing_booking_id=<booking id>`. This excludes the booking’s current capacity claim. Send the chosen token to `POST /v1/bookings/{id}/change`, with a new `idempotency_key` and optionally `expected_version`.

Provider-managed locations reject `changing_booking_id` with `422 capability_unsupported`. Their direct-change path takes a token from an ordinary availability search instead. OpenTable supports time/party changes and `notes`, which replace the reservation’s special request. If OpenTable does not confirm the note, the change returns `422 capability_unsupported` rather than reporting it saved; a time or party-size change sent with it may still have applied, so read the booking back. OpenTable does not take structured `intake_answers`; they return `422 capability_unsupported` rather than being silently dropped.

To change only the guest’s note, send `notes` without `slot_token`. The booking keeps its time, party size and table, so there’s no quote to take, and a reschedule cutoff or a later blackout of the day doesn’t block it. `""` clears the note. A guest-side note change is refused once the reservation has started. At OpenTable, the note is the reservation’s special request. Sending neither `slot_token` nor `notes` answers `400 change_required`.

```http
POST /v1/bookings/{bookingId}/change
Content-Type: application/json


{ "notes": "High chair, please", "expected_version": 1, "idempotency_key": "<unique change key>" }
```

```http
POST /v1/bookings/{bookingId}/cancel
Content-Type: application/json


{ "expected_version": 0, "idempotency_key": "<unique cancellation key>" }
```

A partner cancellation lands as `cancelled_by_guest`; a host-stand app with `bookings:manage` cancels for the venue with `POST /v1/bookings/{id}/venue_cancel` instead (see [Cancel as the venue](https://docs.maple.inc/developer-api/concepts/booking-lifecycle.md#cancel-as-the-venue)). A stale version answers `409 stale_version` with `details.expected` and `details.actual`. Provider-backed writes claim the mirrored booking before provider I/O; a competing write can answer `409 operation_in_progress` while that claim is active. A successful replay runs before the version check, so its original `expected_version` does not prevent replay.

Read the booking again before deciding whether to submit a different change. Cancellation and reschedule policy refusals include a `reason` in `details`. Version claims serialize writes through Maple; they do not lock out changes made directly in the provider’s own tools.

## Find a guest’s booking

When a guest calls or texts about their reservation, look it up by the phone they booked with or the confirmation code they were given, instead of paging through the day:

```http
GET /v1/bookings?location_id={locationId}&phone=(415)%20555-0142
GET /v1/bookings?location_id={locationId}&confirmation_code=4815162
```

The phone is normalized before matching, and a lookup without `start`/`end` matches bookings at any time, ordered by slot start. It returns ordinary `booking` resources, ready for a change or cancel. `location_id` is required. See [Listing bookings](https://docs.maple.inc/developer-api/api-reference.md#listing-bookings).

At a provider-managed location (OpenTable), a lookup that includes `phone` first asks the provider, so it also finds a reservation the guest made directly with OpenTable. OpenTable looks reservations up by phone only: a `confirmation_code`-only lookup finds the provider reservations Maple already knows about, so include the phone when you have it.

## Request limits

The default per-app, per-environment budgets are **60 availability searches/minute** and **300 other booking requests/minute**. The budgets are independent and apply across granted locations. Existing order/menu routes are not included. A `429` includes both a `Retry-After` header and `retry_after` in its JSON body; wait before retrying. Maple can adjust these deployment-level budgets.

## Follow changes

Subscribe to [booking webhooks](https://docs.maple.inc/developer-api/webhook-events.md#booking-events). Use `GET /v1/bookings/{id}` for current state, or the [incremental list](https://docs.maple.inc/developer-api/api-reference.md#listing-bookings) to catch up after downtime. Venue operations and the live-floor read are described in [Booking lifecycle](https://docs.maple.inc/developer-api/concepts/booking-lifecycle.md).
