# Group availability and pricing

> Read catalog group availability and stay estimates, with explicit price coverage, inclusions, freshness, and host-specific links.


Use one catalog response for directory buttons, map popups, and group pages. Do not
recompute a group minimum from paginated property lists in each component.
These additive operations require a collection-wide `rates.read` grant. A
resource-scoped grant does not authorize an aggregate over other properties.

## Choose an operation

| Operation | Use it for |
| --- | --- |
| [GET bookable product groups](https://developers.kismet.travel/api/reference/list-bookable-product-groups.md) | Browse canonical catalog groups and their membership. |
| [GET bookable product group](https://developers.kismet.travel/api/reference/get-bookable-product-group.md) | Read one group's catalog detail. |
| [GET group availability](https://developers.kismet.travel/api/reference/get-group-availability.md) | Compare availability and observed stay prices for specific dates and guests. |
| [GET group from prices](https://developers.kismet.travel/api/reference/get-group-from-prices.md) | Find observed stay prices across a 90-day arrival window, for an explicit stay length and guest count. |

### Supported group scope

These are catalog group operations, not a separate building API. **The current
pricing implementation supports static groups classified as `BUILDING` only.**
Canonical `NEIGHBORHOOD` parents select their child buildings; they are not priced
as independent aggregates. Other classifications, unclassified groups, and dynamic
groups are not supported by these pricing reads. General catalog group reads do
not imply pricing support for every group. Classify groups using the
[catalog classification guide](https://developers.kismet.travel/guides/catalog-group-classification.md).

## Dated search

```http
GET /v1/developer/collections/{collection}/groups/availability?checkIn=2027-01-11&checkOut=2027-01-16&guests=4&host=staging.example.com
Authorization: Bearer YOUR_DEVELOPER_KEY
```

```ts
import { createKismetClient } from '@kismet-tech/sdk/server';

const kismet = createKismetClient({
  apiKey: process.env.KISMET_SERVER_KEY!,
  baseUrl: process.env.KISMET_API_BASE_URL!, // includes /v1
  collection: 'your-collection',
});

const result = await kismet.bookableProductGroup.availability({
  checkIn: '2027-01-11',
  checkOut: '2027-01-16',
  guests: 4,
  host: 'staging.example.com',
  groupType: 'building',
  neighborhoodSlugs: ['your-neighborhood'],
  groupSlugs: ['another-building'],
});
```

`groupSlug` and `neighborhoodSlug` are repeatable REST parameters. The SDK uses
`groupSlugs` and `neighborhoodSlugs` arrays. Selections form a union: selected
neighborhoods expand through canonical parent IDs, then individually selected
buildings are added. Labels, slug conventions, and marketing regions never
define membership. Unrecognized or incorrectly classified selections return a
validation error. Only published inventory participates. Dynamic group membership
is not supported by this operation and returns an explicit error.

## Undated “from” search

```http
GET /v1/developer/collections/{collection}/groups/from-prices?stayLength=5&guests=4&host=staging.example.com
Authorization: Bearer YOUR_DEVELOPER_KEY
```

```ts
const result = await kismet.bookableProductGroup.fromPrices({
  guests: 4,
  stayLength: 5,
  host: 'staging.example.com',
});
```

The REST path is `/v1/developer/collections/{collection}/groups/from-prices`.
Both `guests` and `stayLength` are required. The response declares the 90-day
arrival window and winning stay dates. The current window starts on the UTC
calendar date, declared in `meta.calendarDayTimezone`; do not describe it as
the property's local “today.” Full stays can depart after the arrival window.
Requests are bounded to 100 selected buildings and 1,000 unique properties.
Oversized requests fail rather than silently returning an incomplete minimum.

## Display prices truthfully

- Each group record identifies itself with `groupId`, `groupSlug`, and
  `classification.semanticType`. The current pricing scope returns `BUILDING`.
  The SDK tolerates older responses without classification; do not infer it from
  the slug or a display name. See [classification filters](https://developers.kismet.travel/guides/catalog-group-classification.md#filter-catalog-groups).
- `availability` is `available`, `unavailable`, or `unknown`. Missing or stale
  evidence is not sold out. Availability is separate from price completeness.
- `lowestStay` is the single display price only when coverage is complete and
  the eligible prices are comparable. Otherwise show **Check availability**.
- `lowestObservedStays` retains alternatives partitioned by currency, inclusion
  basis, and estimate/quote kind. These are observed candidates, not proof of an
  absolute minimum. Never take the numeric minimum across currencies.
- Stay totals and average nightly prices are integer minor units. Read
  `included`, `excluded`, `unknownComponents`, `priceBasis`, and `priceKind`.
  Estimates are not chargeable quotes or reservation guarantees.
- Respect `dataAsOf` and `expiresAt`. Partial responses expire immediately and
  use private/no-store HTTP caching.

### Price coverage

Kismet checks occupancy,
inventory, arrival/departure closures, stay restrictions, rates, and freshness.
Applications consume the normalized response regardless of the underlying inventory
integration. Do not branch on provider names to decide whether to display a price.

Current coverage includes synchronized base rates, not every eligible rate plan.
The response therefore reports `BASE_RATE_ONLY` and
`INCOMPLETE_RATE_PLAN_COVERAGE`; `lowestStay` remains null even when qualified
base estimates are present. Do not label these estimates “lowest bookable rate.”
Use `completeness`, coverage reasons, and `lowestStay` as the display authority.
A successful HTTP response does not by itself mean a complete minimum price exists.

### One display decision for every surface

Share the returned group record between the directory, map popup, and building
page. Do not substitute an observed candidate when `lowestStay` is withheld:

```ts
const group = result.data[0];
if (!group) {
  // No matching published building. Render your empty state.
} else if (group.availability === 'unavailable') {
  // Not available for this selection; this is not a missing-data state.
} else if (group.completeness !== 'complete' || !group.lowestStay) {
  // Show "Check availability", with no single from-price.
} else {
  const { currency, totalMinor, averageNightlyMinor, included, excluded } =
    group.lowestStay;
  // Format using the currency's minor-unit exponent (not always 2).
  // Show stay total, nightly average, and the returned inclusion information.
}
// Render a link only when group?.navigation.status === 'declared';
// use group.navigation.url verbatim so staging never falls back to production.
```

## Request checklist and errors

| Parameter | Meaning |
| --- | --- |
| `checkIn`, `checkOut` | Required ISO calendar dates for the dated endpoint; departure must follow arrival. |
| `stayLength` | Required integer number of nights, 1–90, for the undated endpoint. |
| `guests` | Required integer occupancy, 1–100. |
| `host` | Required registered serving hostname, such as `staging.example.com`; not a URL with a path. |
| `groupType` | Optional; the supported value is `building`. |
| `groupSlug` | Repeat once per individually selected canonical building. |
| `neighborhoodSlug` | Repeat once per canonical neighborhood whose buildings should be included. |

Read non-success responses as `application/problem+json`; retain `code` and
`requestId` for support. A validation error, missing grant, or dependency failure
is not an empty result and must not become a "sold out" label. Check parameter
validation for 400, credentials for 401, collection-wide grants and permitted
origins for 403, and rate-limit guidance for 429. A 503 requires a retry or an
explicit unavailable-data state, not an invented price.

## Links and reusable UI

`host` is a registered serving hostname, not an arbitrary redirect URL. Use
`navigation.url` for the building link: it comes from declared routes and keeps
the requested staging host, dates, and guests. `navigation.status: "unmapped"`
means no safe declared link is available; do not substitute the production host.
TEST/LIVE credentials and staging/production hosts are independent concepts.

Use the same result and selection state in all three surfaces. Fixture wiring
is optional for API adoption; payment and checkout still use the trusted booking
flow. No existing API or fixture is deprecated by these reads.
