> ## 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.

# Configure bookings

> Publish native booking areas, tables, shifts, turn times, pacing, and policies without replacing the menu.

A native location’s booking configuration describes its seating areas and booking rules. Publish the complete document to `POST /v1/locations/{locationId}/booking_configuration`; read it with `GET` on the same path.

## Prerequisites

- Your app is granted the location.
- Publishing requires `bookings:configure`; reading requires `bookings:read`.
- Maple has enabled the location’s `bookings-public-api` rollout flag.
- The location has a Maple-native connection, or no booking connection yet. Provider-managed locations answer `422 capability_unsupported`.

Publishing configuration does **not** enable bookings. The merchant turns bookings on from the Maple dashboard (**Settings › Location › Bookings**), or Maple does it for them. Check `booking_profile.enabled` before offering availability.

## Publish a configuration

Configuration bodies use camelCase, like menu documents. `poolId` and `unitId` are your stable identifiers. Unit IDs must be unique across the document.

```json
{
  "timeZone": "America/New_York",
  "currency": "USD",
  "areas": [
    {
      "poolId": "dining",
      "name": "Dining Room",
      "kind": "seat",
      "units": [
        { "unitId": "t1", "seats": { "min": 1, "max": 2 } },
        { "unitId": "t2", "seats": { "min": 1, "max": 4 }, "tags": ["window"] }
      ],
      "combinations": [
        { "unitIds": ["t1", "t2"], "seats": { "min": 3, "max": 6 } }
      ]
    }
  ],
  "turnBands": [
    { "minGuests": 1, "maxGuests": 2, "minutes": 90 },
    { "minGuests": 3, "maxGuests": 6, "minutes": 120 }
  ],
  "shifts": [
    { "day": "FRI", "start": "17:00", "end": "23:00" },
    { "day": "SAT", "start": "12:00", "end": "23:00" }
  ],
  "pacing": { "intervalMinutes": 15, "maxCovers": 12, "enforcement": "hard" },
  "bookingPolicy": {
    "minLeadMinutes": 60,
    "maxHorizonDays": 60,
    "slotAlignment": "quarter_hourly",
    "autoConfirm": true
  },
  "cancellationPolicy": {
    "requiresCardOnFile": false,
    "tiers": [],
    "rescheduleCutoffHours": 2,
    "policyText": "Please contact the venue for late changes."
  }
}
```

The response contains the normalized configuration plus `object: "booking_configuration"`, `revision`, `location_id`, `version_id`, `published_at`, and `livemode`. Reads materialize policy defaults; they need not reproduce omitted fields byte for byte.

## Publish without overwriting the venue

Restaurant managers can edit the same configuration in the Maple dashboard. To avoid replacing their edits, read the configuration, change it, and publish it with the `revision` you read as `expected_revision`:

```json
{ "...": "the whole configuration", "expected_revision": "<revision from your last read>" }
```

If the configuration changed since your read, the publish answers `409 stale_configuration` and nothing is replaced: read it again, reapply your change, and publish with the new revision. Send `"expected_revision": null` for a first configuration. Omitting `expected_revision` replaces the configuration unconditionally. The `revision` changes only when the configuration does; menu publishes move `version_id` but not `revision`.

## Publication rules

- The document replaces the store’s authored booking configuration, not its menu or unrelated catalog resources and policies. Subsequent provider/menu publishes preserve that booking configuration too.
- Re-publishing an identical configuration does not create another version. There is no `idempotency_key` field for configuration.
- Changed turn times or policies take effect for new availability quotes. Existing bookings retain their pinned terms.
- The currency must match an existing catalog’s currency. A first configuration establishes the currency when no catalog exists.
- Times are venue-local `HH:MM`; a shift’s end must be after its start. Split overnight opening hours across days.
- Turn bands must be ordered and non-overlapping. Combinations must name distinct units from their own area.
- Every area is bookable. Availability tries the first area in `areas`, then the others in `poolId` order (the order a read returns them), and seats the party in the first one with a free table or combination that fits; a booking stays in the area it was seated in. Shifts and closed dates apply to the whole location.
- Unknown fields, invalid zones/dates, and invalid bounds are rejected. `400` validation responses identify the invalid input. `422 configuration_rejected` means the document cannot be applied to the catalog.
- A read before configuration exists answers `404`.

> [!NOTE]
>
> Card-on-file/deposit booking flows and charging cancellation fees are separate capabilities; declaring a policy is not a payment integration.

## Blackout dates

Blackout dates are days or part-days when a location is open but takes no reservations: a private event, a holiday, a walk-ins-only night. They differ from the configuration’s `closedDates`:

|             | `closedDates` (configuration)                           | Blackout dates                                                               |
| ----------- | ------------------------------------------------------- | ---------------------------------------------------------------------------- |
| Meaning     | The venue is shut                                       | The venue is open, but not for reservations                                  |
| Locations   | Maple-native only                                       | Native and provider-managed (Maple enforces them before any provider call)   |
| Granularity | Whole days                                              | Whole days, or a start and end time                                          |
| Written by  | `POST /v1/locations/{locationId}/booking_configuration` | `PUT /v1/locations/{locationId}/booking_blackouts`, or the venue’s dashboard |

Read the current list from `blackout_dates` on `GET /v1/locations/{locationId}/booking_profile` (`bookings:read`). Replace it with `bookings:configure`:

```http
PUT /v1/locations/{locationId}/booking_blackouts
```

```json
{
  "blackout_dates": [
    { "start_date": "2026-12-24", "end_date": "2026-12-26", "reason": "Holidays" },
    { "start_date": "2026-10-10", "end_date": "2026-10-10", "start_time": "18:00", "end_time": "22:00" }
  ],
  "expected_revision": "<blackout_dates_revision from the booking profile>"
}
```

The response is the list as kept, in start order, with `null` for a range’s missing times or note, and its new `revision`: `{ "object": "booking_blackouts", "location_id": "…", "blackout_dates": [...], "revision": "…" }`.

- The body is the whole list: ranges you leave out are removed, and `[]` clears them all. The venue edits the same list in its dashboard, so read `blackout_dates` and `blackout_dates_revision` from the profile, edit, and send that revision as `expected_revision`. If the list changed since you read it, the write answers `409 stale_blackout_dates` and nothing changes: read it again and reapply your edit. Without `expected_revision` the write replaces whatever is there.
- Dates are venue-local and inclusive. A range without times blocks whole days; `start_time` applies to the first day and `end_time` to the last. At most 100 ranges, with notes of up to 200 characters.
- Invalid ranges (a date that doesn’t exist, an end before the start, a malformed time) answer `400 invalid_blackout_dates` and change nothing.
- Availability omits slots inside a blackout; native locations also withhold seatings that would run into one. A quote taken before a blackout was added is refused when held or booked: `409 slot_unavailable` at native locations, `422 date_blocked` at provider-managed ones. Existing bookings inside a new blackout are not cancelled, and neither is a provider-managed booking already in flight when the blackout is saved.
- A blackout stops reservations, not the host stand: walk-ins (`POST /v1/locations/{locationId}/walk_ins`) and seating waitlist parties still work inside one, on normal table availability.
- At a provider-managed location, the provider’s own channels (such as OpenTable’s website) don’t see Maple’s list; the venue blocks the dates there too.

## Provision a booking sandbox

In Maple’s admin portal, select **Native bookings + sample floor** when provisioning the partner sandbox. The option defaults off. It creates an enabled Maple booking connection and, if configuration is absent, seeds four tables, 90-minute turns, and daily opening hours. Re-running it preserves an existing configuration and refuses to replace an OpenTable connection.

The staging provisioning CLI exposes the same option as `--booking-connection`. For a booking-only partner, you can skip the POS connection; a location grant is sufficient for bookings. Existing keys that lack booking scopes need a newly minted key.

The provisioning result’s `bookings_configured` confirms that the step ran, not that rollout is enabled. Maple must target the sandbox location in the staging PostHog project for `bookings-public-api`. Then run [search → hold → create → cancel](https://docs.maple.inc/developer-api/guides/take-bookings.md) using the sandbox credential.
