Booking slots are scheduled time windows attached to a product variant. The variant supplies the name, description, images and price; the slot adds when, where, how many people fit, and optional session-level details. Create the bookable product and variant first, then schedule slots against its variant id.

Capacity is managed by the platform: `bookedCount` increments as orders are placed and decrements on cancellations. The `spotsRemaining` field is derived for convenience.

**Scopes:** `storefront:read` for GET, `catalog:write` for POST/PATCH/DELETE. The bookings sub-resource requires `orders:read`.

## List Booking Slots

```
GET /api/v1/booking-slots
```

### Query Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `startDate` | `string` | – | Only slots starting at or after this instant (ISO 8601) |
| `endDate` | `string` | – | Only slots starting at or before this instant (ISO 8601) |
| `status` | `string` | – | Restrict to one status: `draft`, `published`, `full`, `cancelled`, `completed` |
| `productVariantId` | `string` | – | Only slots for this bookable variant (UUID) |
| `limit` | `number` | 100 | Slots to return (1-200) |

### Response

```json
{
  "data": [
    {
      "id": "0191abc0-1234-7def-8000-000000000001",
      "productVariantId": "0191abc0-0000-7000-8000-000000000100",
      "product": {
        "id": "0191abc0-0000-7000-8000-000000000200",
        "name": "Yoga Class",
        "slug": "yoga-class"
      },
      "startsAt": "2025-09-01T10:00:00+02:00",
      "endsAt": "2025-09-01T11:00:00+02:00",
      "timezone": "Europe/Warsaw",
      "maxCapacity": 20,
      "bookedCount": 5,
      "spotsRemaining": 15,
      "status": "published",
      "settings": {
        "location": "Studio B, 3rd floor",
        "instructor": "Jane Doe",
        "autoConfirm": true
      },
      "createdAt": "2025-08-01T12:00:00.000Z",
      "updatedAt": "2025-08-01T12:00:00.000Z"
    }
  ],
  "meta": { "count": 1 }
}
```

---

## Get Booking Slot

```
GET /api/v1/booking-slots/:id
```

Returns a single slot by UUID. Returns `404` if the slot does not exist in this store.

---

## Create Booking Slot

```
POST /api/v1/booking-slots
```

### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `productVariantId` | `string` | Yes | UUID of the bookable variant |
| `startsAt` | `string` | Yes | When the slot begins (ISO 8601 with offset) |
| `endsAt` | `string` | Yes | When the slot ends (ISO 8601 with offset); must be after `startsAt` |
| `timezone` | `string` | No | IANA timezone (default: `"UTC"`) |
| `maxCapacity` | `number` | Yes | How many attendees fit (1–100,000) |
| `status` | `string` | No | `draft`, `published`, `cancelled`, `completed` (default: `"draft"`) |
| `settings` | `object` | No | Slot-specific details (see below) |

### Settings Object

| Field | Type | Description |
|-------|------|-------------|
| `location` | `string \| null` | Physical address or room (max 300 chars) |
| `onlineUrl` | `string \| null` | Join link for a remote session |
| `instructor` | `string \| null` | Who runs it (max 200 chars) |
| `materials` | `string[]` | What attendees should bring (max 50 items, 200 chars each) |
| `requirements` | `string \| null` | Prerequisites or restrictions (max 2000 chars) |
| `cancellationHours` | `number \| null` | Cancellation cut-off for this slot; falls back to the module default |
| `bufferMinutes` | `number \| null` | Gap held after this slot |
| `autoConfirm` | `boolean \| null` | Confirm bookings without merchant review |

```bash
curl -X POST \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "productVariantId": "0191abc0-0000-7000-8000-000000000100",
    "startsAt": "2025-09-01T10:00:00+02:00",
    "endsAt": "2025-09-01T11:00:00+02:00",
    "timezone": "Europe/Warsaw",
    "maxCapacity": 20,
    "status": "published",
    "settings": { "location": "Studio B", "autoConfirm": true }
  }' \
  https://your-store.yns.store/api/v1/booking-slots
```

### Response (201)

Returns the created slot.

### Errors

| Status | Condition |
|--------|-----------|
| `400` | `endsAt` is not after `startsAt` |
| `404` | `productVariantId` does not reference a variant in this store |
| `409` | A slot already exists for that variant at that time |

---

## Update Booking Slot

```
PATCH /api/v1/booking-slots/:id
```

Partially updates a slot. Only the fields you send are changed. Settings fields merge: send only the settings sub-fields you are changing; `null` clears a field.

### Request Body

All fields from Create are accepted and optional. `productVariantId` is changeable.

### Errors

| Status | Condition |
|--------|-----------|
| `400` | Merged `endsAt` is not after merged `startsAt` |
| `400` | `maxCapacity` is below the number of existing bookings |
| `404` | Slot not found |

```bash
curl -X PATCH \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"maxCapacity": 30, "settings": {"instructor": "John Smith"}}' \
  https://your-store.yns.store/api/v1/booking-slots/0191abc0-1234-7def-8000-000000000001
```

### Response (200)

Returns the updated slot.

---

## Delete Booking Slot

```
DELETE /api/v1/booking-slots/:id
```

Permanently deletes a slot. Slots with existing bookings cannot be deleted – set the status to `cancelled` instead to keep orders intact and stop new bookings.

### Errors

| Status | Condition |
|--------|-----------|
| `404` | Slot not found |
| `409` | Slot has existing bookings |

```bash
curl -X DELETE \
  -H "Authorization: Bearer your_api_key" \
  https://your-store.yns.store/api/v1/booking-slots/0191abc0-1234-7def-8000-000000000001
```

### Response (204)

No content.

---

## List Slot Bookings

```
GET /api/v1/booking-slots/:id/bookings
```

Returns the orders booked into a slot. A booking is an order line – cancel or refund one through the Orders API and the slot's `bookedCount` follows. Requires the `orders:read` scope.

```bash
curl -H "Authorization: Bearer your_api_key" \
  https://your-store.yns.store/api/v1/booking-slots/0191abc0-1234-7def-8000-000000000001/bookings
```

### Response

```json
{
  "data": [
    {
      "orderId": "0191abc0-5678-7def-8000-000000000001",
      "orderLookup": "ORD-1234",
      "status": "paid",
      "customer": {
        "id": "0191abc0-9999-7def-8000-000000000001",
        "email": "attendee@example.com"
      },
      "createdAt": "2025-08-10T14:30:00.000Z"
    }
  ],
  "meta": {
    "count": 1,
    "bookedCount": 5,
    "maxCapacity": 20
  }
}
```