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

# Add-Ons and Sub-Bookings

When a customer selects more than one service (quantity > 1), add-ons and sub-bookings must be associated with each individual booking instance. This guide covers the `add_ons_by_service` and `sub_bookings_by_service` payload structures used in checkout.

***

## The Problem with the Old Format

The legacy format sent add-ons as a flat list:

```json theme={null}
{
  "booking_add_on": [
    { "add_on_id": 1, "quantity": 1 },
    { "add_on_id": 2, "quantity": 1 }
  ],
  "sub_bookings": [
    { "resource_id": 1, "service_id": 1, "quantity": 2 }
  ]
}
```

This made it impossible to associate add-ons with specific booking instances when quantity > 1.

***

## New Structure

The new format groups selections by service and booking instance:

### `add_ons_by_service`

```json theme={null}
{
  "add_ons_by_service": {
    "{service_id}": [
      [
        { "add_on_id": 1, "quantity": 1 }
      ],
      [
        { "add_on_id": 1, "quantity": 1 },
        { "add_on_id": 2, "quantity": 2 }
      ]
    ]
  }
}
```

* **Top level key**: service ID
* **Outer array**: one entry per booking of that service
* **Inner array**: the add-ons selected for that specific booking instance

<Note>
  `quantity` is resolved against the add-on's `min_quantity` and `always_apply` (default `true`). When `always_apply` is `true`, omitting an add-on or sending `0` still includes it at `min_quantity`. When `always_apply` is `false`, omit it or send `0` to skip; a quantity greater than `0` is still floored to `min_quantity`. Admin org edits treat an explicit `0` as removal. See [Add-On](/developers/models/add-ons).
</Note>

### `sub_bookings_by_service`

```json theme={null}
{
  "sub_bookings_by_service": {
    "{service_id}": [
      [
        { "resource_id": 1, "service_id": 1, "quantity": 1 }
      ],
      []
    ]
  }
}
```

Same structure: service → booking instances → selections per instance.

***

## Example

Two services, each booked twice:

| Service   | Booking #   | Add-ons                      |
| --------- | ----------- | ---------------------------- |
| Service A | 1st booking | Add-on 1 (×1)                |
| Service A | 2nd booking | Add-on 1 (×1)                |
| Service B | 1st booking | Add-on 1 (×1), Add-on 2 (×2) |
| Service B | 2nd booking | Add-on 2 (×1)                |

```json theme={null}
{
  "add_ons_by_service": {
    "service_id_A": [
      [{ "add_on_id": 1, "quantity": 1 }],
      [{ "add_on_id": 1, "quantity": 1 }]
    ],
    "service_id_B": [
      [
        { "add_on_id": 1, "quantity": 1 },
        { "add_on_id": 2, "quantity": 2 }
      ],
      [{ "add_on_id": 2, "quantity": 1 }]
    ]
  }
}
```

***

## UI Requirements

* Allow selecting add-ons independently for each booking instance (similar to a shopping cart)
* Provide an **"Apply to all"** option that copies the same add-ons/sub-resources to all instances of the same service
* Show the add-on/sub-resource filter and search UI only when 5+ options are available
* Filtering by group and name is client-side only

***

## Sub-Resource Availability

When fetching sub-resources with the availability filter, the response includes `number_available` in the resource metadata:

```http theme={null}
GET /api/v1/resources?filter[availability_exact_match]=1&filter[date]={date}
Authorization: Bearer {token}
```

The `number_available` attribute indicates how many times a resource can still be booked. Enforce this constraint client-side across all booking instances in the current selection.

<Warning>
  The API validates availability on submission but is unaware of the user's current in-progress selection. If a resource is available twice and the user selects it for two bookings, it must be marked as unavailable for any additional bookings **locally** before the request is sent.
</Warning>
