Packages

Create a package

Creates a new Package at the given site. The minimum body is name + site_id; everything else has sensible defaults. Requires the SETTINGS_MANAGE permission on site_id.

Server-side behaviour worth noting:

  • separate_couples_package_choices is forced to false unless both min_guests and max_guests equal 2.
  • permitted_membership_type_ids is filtered to membership types within the site's brand; others are silently dropped.
  • When overnight_package_config.enabled=true, both rate_id and num_nights are required — otherwise the request fails with 422.
  • organisation_id defaults to the caller's primary organisation when omitted.
post/shop/packages

Request body

namestring required

Display name for the package. Visible to customers in the shopfront and to staff in the admin UI. Plain text only.

descriptionstring

Long-form description shown to customers when they're browsing the package. Markdown is rendered.

product_codestring nullable

Optional product code used to map this package to an external POS or PMS catalog. Free-form string; uniqueness is not enforced server-side.

external_idstring nullable

External identifier used by integrations to reference this offering. Free-form.

currencystring

The ISO-4217 currency code.

standard_priceinteger nullable

The standard price for this package, in the smallest currency unit (e.g. pence). Optional at create time — packages can be created without a standard price and have one configured later.

site_idstring required

ID of the site to offer this package at. The caller's API key must have access to the site; requests for a site they don't own get 422.

organisation_idstring uuid

Optional organisation to attribute this offering to. Defaults to the caller's primary organisation if omitted.

offered_onlineboolean

When true, the package is bookable via the public shopfront. When false, it's bookable only via the admin UI. Defaults to false.

privateboolean

When true, only customers with the direct link can book this package — it's hidden from category pages and search engines.

visibility'public' | 'link_only' | 'private'

Visibility override controlling who can see this package on the shopfront. public shows it everywhere; link_only keeps it out of browse/search (still bookable by direct link); private restricts it to admin booking flows.

customers_onlyboolean

When true, only registered customers (not guests) can book this package.

members_onlyboolean

When true, only members of permitted membership types can book this package.

permitted_membership_type_idsstring[] nullable

Membership type IDs whose members are allowed to book this package. Only relevant when members_only=true. Membership types from outside the site's brand are silently filtered out.

membership_booking_windows_enabledboolean

When true, members can book this package further in advance than the standard window. The exact extra advance per membership type is defined in membership_booking_windows.

item_choicesobject[]

The choices customers make when booking this package (e.g. "pick a massage", "pick a lunch course"). Each choice has an allocation, options, start-time rules, and visibility flags. The detailed shape is documented as PackageItemChoice under the Package schema — the create endpoint accepts the same object shape.

category_idsstring[]

Category IDs the package belongs to. Used for shopfront grouping.

customer_cancellation_permitted'allowed' | 'if_unpaid' | 'disallowed' nullable

Whether and when customers can cancel a booking of this package themselves. allowed = always; if_unpaid = only if no payment has been taken; disallowed = never (admin only).

customer_cancellation_min_durationstring nullable

ISO 8601 duration before the package's start during which cancellation is no longer permitted. e.g. PT24H = no cancellation in the last 24 hours.

customer_reschedule_permitted'allowed' | 'disallowed' | 'unpaid' nullable

Whether and when customers can reschedule a booking of this type themselves. allowed = always. unpaid = only if no payment has been taken against the order. disallowed = never (admin only).

customer_reschedule_min_durationstring nullable

ISO 8601 duration before the package's start during which rescheduling is no longer permitted. e.g. PT24H = no rescheduling in the last 24 hours.

max_daily_durationinteger nullable

Maximum minutes between the start of the first and end of the last item on each day of the package. Use this to cap how spread-out a single-day package can become.

max_advance_bookings_intervalstring nullable

ISO 8601 duration limiting how far in advance customers can book this package. e.g. P2W = up to two weeks ahead.

min_advance_bookings_intervalstring nullable

ISO 8601 duration limiting how soon before the package start a customer can book. e.g. P2D = at least two days' notice.

min_guestsinteger nullable

Minimum number of guests for a single booking of this package.

max_guestsinteger nullable

Maximum number of guests for a single booking. Must be >= min_guests. Server-side, separate_couples_package_choices is forced to false unless both min_guests and max_guests equal 2.

prevent_choice_overlapsboolean nullable

Whether to prevent booking choices in this package from overlapping times.

separate_couples_package_choicesboolean nullable

For couples packages (both min_guests and max_guests equal 2), whether each guest can independently pick choices. Forced to false for any other guest configuration.

include_pricing_on_calendarboolean

Whether to surface the price alongside this package in calendar views.

max_per_basketinteger nullable

Maximum bookings of this package per basket.

max_per_guestinteger nullable

Maximum bookings of this package per individual guest within a basket.

Example request

{
  "name": "Couples Spa Day",
  "description": "A full day of relaxation for two — includes a 60-minute\nmassage each, a two-course lunch, and full access to the\nthermal suite.\n",
  "product_code": "PKG-COUPLES-DAY",
  "external_id": "ext-couples-spa-day",
  "currency": "gbp",
  "standard_price": 5000,
  "site_id": "00000000-0000-0000-0000-111111111111",
  "organisation_id": "00000000-0000-0000-0000-aaaaaaaaaaaa",
  "availability_rules": [
    {
      "date_from": "2021-02-15",
      "date_to": "2021-02-15",
      "time_from": "11:00",
      "time_to": "17:00"
    }
  ],
  "visibility": "public",
  "permitted_membership_type_ids": [
    "00000000-0000-0000-0000-membership001"
  ],
  "membership_booking_windows": [
    {
      "max_advance_bookings_interval": "P2W",
      "min_advance_bookings_interval": "P2D"
    }
  ],
  "upsell_offerings": [
    {
      "offering_name": "Twilight massage",
      "offering_type": "appointment"
    }
  ],
  "cross_sell_offerings": [
    {
      "offering_name": "Twilight massage",
      "offering_type": "appointment"
    }
  ],
  "related_retail_offerings": [
    {
      "offering_name": "Twilight massage",
      "offering_type": "appointment"
    }
  ],
  "category_ids": [
    "5e932c0901d210625e3a8766"
  ],
  "meta": {
    "title": "Couples Spa Day at Riverside Retreat",
    "description": "A relaxing day for two with massages, lunch, and thermal suite access."
  },
  "customer_cancellation_min_duration": "PT24H",
  "customer_reschedule_permitted": "allowed",
  "customer_reschedule_min_duration": "PT24H",
  "max_daily_duration": 240,
  "max_advance_bookings_interval": "P2W",
  "min_advance_bookings_interval": "P2D",
  "min_guests": 2,
  "max_guests": 2,
  "overnight_package_config": {
    "enabled": true,
    "rate_id": "rate-king-bb",
    "num_nights": 1
  },
  "max_per_basket": 1,
  "max_per_guest": 1
}

Response

A single Package.

Example response

{
  "data": {
    "id": "5dcb47800000000000000010",
    "categories": [
      {
        "name": "Massages"
      }
    ],
    "cross_sell_offerings": [
      {
        "offering_name": "Twilight massage",
        "offering_type": "appointment"
      }
    ],
    "currency": "gbp",
    "customer_cancellation_min_duration": "P7D",
    "customer_cancellation_permitted": "allowed",
    "customer_reschedule_min_duration": "P7D",
    "customer_reschedule_permitted": "allowed",
    "description": "Buy together and save",
    "email_options": {
      "hide_prices": true
    },
    "image": {
      "file_name": "super-cool-photo.jpg",
      "mime_type": "image/jpeg",
      "original_url": "https://example.com/media/super-cool-photo.jpg",
      "size": 84256,
      "url": "https://example.com/media/super-cool-photo-thumbnail@2x.jpg"
    },
    "item_choices": [
      {
        "id": "5dcb47800000000000000025",
        "allow_overlaps": true,
        "auto_select_timeslot": true,
        "description": "Choose your type of massage.",
        "hide_times": true,
        "name": "Massage",
        "max_options": 2,
        "min_options": 1,
        "option_budget": 2000,
        "optional": true,
        "options": [
          {
            "id": "5dcb47800000000000000026",
            "item_type": "appointment",
            "offering": {
              "id": "5e932c0901d210625e3a8766",
              "categories": [
                {
                  "id": "5e932c0901d210625e3a8766",
                  "name": "Massage"
                }
              ],
              "currency": "gbp",
              "description": "Choose from classic, deep tissue and Swedish massage",
              "discounted_price_from": 4500,
              "duration": 30,
              "durations": [
                30
              ],
              "has_availability": true,
              "image": {
                "file_name": "super-cool-photo.jpg",
                "mime_type": "image/jpeg",
                "original_url": "https://example.com/media/super-cool-photo.jpg",
                "size": 84256,
                "url": "https://example.com/media/super-cool-photo-thumbnail@2x.jpg"
              },
              "max_guests": 2,
              "min_guests": 2,
              "name": "60 minute massage",
              "price_from": 5000,
              "price_to": 6500,
              "type": "appointment"
            },
            "price_change": 2000
          }
        ],
        "start_time_rules": [
          {
            "absolute_time_from": "12:00",
            "absolute_time_to": "12:30",
            "relative_mins_to": 30
          }
        ],
        "visible": true
      }
    ],
    "max_advance_bookings_interval": "P2W",
    "max_daily_duration": 240,
    "max_guests": 2,
    "max_per_basket": 1,
    "max_per_guest": 1,
    "members_only": true,
    "membership_booking_windows": [
      {
        "max_advance_bookings_interval": "P2W",
        "min_advance_bookings_interval": "P2D"
      }
    ],
    "membership_booking_windows_enabled": true,
    "min_advance_bookings_interval": "P2D",
    "min_guests": 2,
    "name": "Treatment and Yoga",
    "overnight_package_config": {
      "enabled": true,
      "rate_id": "5dcb47800000000000000010",
      "num_nights": 1
    },
    "prevent_choice_overlaps": true,
    "price_rules": [
      {
        "id": "5dcb47800000000000000010",
        "package_id": "5dcb47800000000000000010",
        "date_from": "2020-02-01",
        "date_to": "2020-04-01",
        "time_from": "10:00",
        "time_to": "14:00",
        "weekdays": [
          "monday"
        ],
        "price": 20000
      }
    ],
    "product_code": "MAS123",
    "related_retail_offerings": [
      {
        "offering_name": "Twilight massage",
        "offering_type": "appointment"
      }
    ],
    "site_id": "00000000-0000-0000-0000-111111111111",
    "standard_price": 5000,
    "upsell_offerings": [
      {
        "offering_name": "Twilight massage",
        "offering_type": "appointment"
      }
    ],
    "visibility": "public",
    "deleted_at": "2025-02-04T12:00:00+01:00",
    "updated_at": "2025-02-04T12:00:00+01:00"
  }
}

Changes