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

# Booking Bundle Groups

> JSON:API contract for grouping booking bundles in admin and native apps. Mirror service groups and add-on groups.

Booking bundles (packages / codes) can be assigned to **booking bundle groups**, the same pattern as [service groups](/developers/models/service-groups) and [add-on groups](/developers/models/add-on-groups).

This is the contract for admin clients and native apps. For JSON:API conventions, see [JSON-API Conventions](/developers/guides/json-api-conventions).

<Info>
  JSON:API type: `booking-bundle-groups`. Bundles expose a nullable `group` relationship (`booking-bundle-groups`). Deleting a group **detaches** bundles (`group_id` is set to `null`); it does not delete the bundles.
</Info>

***

## Resource

|                  |                                                                                            |
| ---------------- | ------------------------------------------------------------------------------------------ |
| Type             | `booking-bundle-groups`                                                                    |
| Admin collection | `GET/POST /api/v1/booking-bundle-groups`                                                   |
| Admin resource   | `GET/PATCH/DELETE /api/v1/booking-bundle-groups/{id}`                                      |
| Public catalog   | Organization relationships `offered-booking-bundle-groups` and `available-booking-bundles` |

Index is readable without a write permission. Create, update, and delete require `create` / `update` on any resource plus access to the organization (`?o=`).

<Note>
  Shop and native apps must load groups from the organization with `include=offered_booking_bundle_groups` or the related resource `GET /api/v1/organizations/{slug}/offered-booking-bundle-groups`.
</Note>

### Attributes

| Attribute          | Type     | Visibility | Description                                |
| ------------------ | -------- | ---------- | ------------------------------------------ |
| `name`             | string   | public     | Resolved name for `Accept-Language`        |
| `description`      | string   | public     | Resolved description for `Accept-Language` |
| `order_index`      | number   | public     | Display order among groups                 |
| `created_at`       | datetime | public     | Creation timestamp                         |
| `local_name_i18n`  | object   | admin      | Full name translation map                  |
| `description_i18n` | object   | admin      | Full description translation map           |

Write `name` (required, max 191) and optional `description` / `order_index`. Send translations as `local_name_i18n.{locale}` and `description_i18n.{locale}`.

### Relationships

| Relationship      | Type                       | Include                   |
| ----------------- | -------------------------- | ------------------------- |
| `booking_bundles` | has-many `booking-bundles` | `include=booking_bundles` |

Allowed sort: `order_index`, `name`, `created_at`. Allowed filters: `filter[ids]`, `filter[search]`.

***

## Bundle relationship and filters

Every `booking-bundles` resource has a nullable `group` relationship. Include it on list, read, and public catalog requests:

```http theme={null}
GET /api/v1/booking-bundles?include=group&sort=order_index
GET /api/v1/booking-bundles/{slug}?include=group
GET /api/v1/organizations/{slug}/available-booking-bundles?include=group,cover_image,organization.logo_image&sort=order_index
```

### Filters on `booking-bundles` and `available-booking-bundles`

| Filter                  | Type                | Description                                           |
| ----------------------- | ------------------- | ----------------------------------------------------- |
| `filter[group_id]`      | string              | Bundles in one group                                  |
| `filter[groups]`        | string or string\[] | Bundles in any of the given group ids                 |
| `filter[without_group]` | string              | Pass `true` (string) to return ungrouped bundles only |

<Warning>
  Send `filter[without_group]=true` as a **string**. A JSON boolean query value is rejected by the JSON:API decoder.
</Warning>

```http theme={null}
GET /api/v1/organizations/{slug}/available-booking-bundles?filter[group_id]=16&include=group&sort=order_index
GET /api/v1/organizations/{slug}/available-booking-bundles?filter[without_group]=true&include=group&sort=order_index
```

Assign or clear a group on create/update via the JSON:API relationship (same as services / add-ons):

```json theme={null}
{
  "data": {
    "type": "booking-bundles",
    "attributes": { "name": "10er Karte" },
    "relationships": {
      "group": {
        "data": { "type": "booking-bundle-groups", "id": "16" }
      }
    }
  }
}
```

Set `relationships.group.data` to `null` to ungroup.

***

## Public catalog (shop and native customer apps)

Load the live groups that currently have at least one **live** bundle, then load bundles per group.

<Tabs>
  <Tab title="Include on the organization">
    ```http theme={null}
    GET /api/v1/organizations/{slug}?include=offered_booking_bundle_groups,available_booking_bundles.group,available_booking_bundles.cover_image
    ```
  </Tab>

  <Tab title="Related resources">
    ```http theme={null}
    GET /api/v1/organizations/{slug}/offered-booking-bundle-groups?sort=order_index
    GET /api/v1/organizations/{slug}/available-booking-bundles?filter[group_id]={groupId}&include=group,cover_image,organization.logo_image&sort=order_index&page[size]=10
    GET /api/v1/organizations/{slug}/available-booking-bundles?filter[without_group]=true&include=group,cover_image,organization.logo_image&sort=order_index
    ```
  </Tab>
</Tabs>

`offered-booking-bundle-groups` only returns groups that have at least one live bundle. Empty groups do not appear in the shop catalog.

### How to render the package list

Match the web shop: one collapsible section per offered group (title = `name`, optional subtitle = `description`), then an **Other** section for `filter[without_group]=true`.

<Steps>
  <Step title="Load offered groups">
    Request `offered_booking_bundle_groups` (or the related resource). Sort by `order_index`.
  </Step>

  <Step title="Load bundles per group">
    For each group, request `available-booking-bundles` with `filter[group_id]` set to that group's id, plus `include=group,cover_image` and `sort=order_index`.
  </Step>

  <Step title="Load ungrouped bundles">
    Request `available-booking-bundles` with `filter[without_group]=true`. If the collection is empty, hide the Other section.
  </Step>

  <Step title="Reuse service-group UI">
    This is the same layout as services (`offered-service-groups` + ungrouped services). Reuse that component; swap the resource type.
  </Step>
</Steps>

<Tip>
  Do not invent grouping from bundle names. Always use `relationships.group` / `filter[group_id]`. After a group is deleted, previously grouped bundles show up under Other.
</Tip>

***

## Admin (native admin app)

Mirror the web admin:

1. **Bundle form** — taggable group select (create a group by typing a new name, same as service groups). Persist via the `group` relationship. Include `group` when loading a bundle.
2. **Bundle list** — grouped panels by `booking-bundle-groups` (`sort=order_index`), plus an ungrouped section (`filter[without_group]=true`).
3. **CRUD** — create/update/delete groups with `name`, `description`, and translations. On delete, keep the bundles; they become ungrouped.

```http theme={null}
GET /api/v1/booking-bundle-groups?filter[search]=gift&sort=order_index&include=booking_bundles
POST /api/v1/booking-bundle-groups?o={organization_id}
PATCH /api/v1/booking-bundle-groups/{id}?o={organization_id}
DELETE /api/v1/booking-bundle-groups/{id}?o={organization_id}
```

Create payload:

```json theme={null}
{
  "data": {
    "type": "booking-bundle-groups",
    "attributes": {
      "name": "Gift cards",
      "description": "Vouchers and gift packages",
      "order_index": 1
    }
  }
}
```

***

## Example group resource

```json theme={null}
{
  "data": {
    "type": "booking-bundle-groups",
    "id": "16",
    "attributes": {
      "name": "Gift cards",
      "description": "Vouchers and gift packages",
      "order_index": 1,
      "created_at": "2026-08-16T12:00:00+00:00"
    },
    "relationships": {
      "booking_bundles": {
        "data": [
          { "type": "booking-bundles", "id": "geschenkgutschein-50" }
        ]
      }
    }
  }
}
```

***

## Related

* [BookingBundle](/developers/models/booking-bundles) — bundle resource; include `group`
* [BookingBundleGroup](/developers/models/booking-bundle-groups) — group resource
* [ServiceGroup](/developers/models/service-groups) — same grouping pattern for services
* [AddOnGroup](/developers/models/add-on-groups) — same grouping pattern for add-ons
