# Catalog & offers

Discover what the studio can sell before deciding how your selection interface works. Room-first and service-first experiences use the same resources and compatibility constraints.

## Inputs and outputs

Read [services](/api/docs#/Studio%20catalog/v2ListService), [rooms](/api/docs#/Studio%20catalog/v2ListRoom), [setups](/api/docs#/Studio%20catalog/v2ListSetup) and [add-ons](/api/docs#/Studio%20catalog/v2ListAddon). Use `view=offers` for customer buyability. The default authoring view can include owned, repairable drafts and is not a customer offer list.

Follow `page`, `per_page` and returned pagination facts. Configured price conditions use their own `prices_page`/`prices_per_page`; one page does not establish that you have the whole catalog.

Responses expose version IDs, images, capacity, relationships and configured rates. Use returned relationships to build compatible service/room/setup combinations. Configured rates and day-rate thresholds are a price list; [quotes](/api/docs/guides/v2/quotes) resolve the dated, personalized selection.

## Choose an entry point

- Room-first: browse rooms, inspect physical [availability windows](/api/docs/guides/v2/availability), then choose a compatible service/setup and duration.
- Service-first: browse offered services, narrow to compatible setups, then ask for concrete starts.
- Known selection: quote the stored selection against the current offer. Previously stored IDs do not guarantee continued buyability.
- Published Tour: use its own published offer, availability, quote and booking operations. A Tour is not an ordinary booking payload.

## Permissions and effects

Catalog read permissions apply to the corresponding families. These reads do not reserve a slot, create a client or freeze a sold price. Editing/archiving an offer is a separate permissioned administrative capability in the full reference; it does not rewrite sold versions.

## Constraints and recovery

An offer can change between discovery and admission. Preserve the chosen IDs and re-quote when Podyx reports a stale or incompatible selection. Do not silently replace the customer's selected service or setup with the first available alternative. Present the permitted alternatives for a new customer decision.

Existing bundle/subscription rights can fund eligible bookings; use the client's current rights and redemption operations rather than treating a catalog plan as owned credit.

## Purchased bundle records for a CRM

[Read a purchased Member Bundle](/api/docs#/Studio%20package%20rights%20and%20management/v2GetMemberBundle) with `GET /api/v2/member-bundles/{id}`. The ID is the `member_bundle_id` from a booking, purchase response or CMS purchase record. This read includes exhausted, expired, pending, declined and cancelled purchases, as well as historical/admin grants with no API purchase record. It requires `purchases:read`, `payments:read` and `sessions:read` on the current studio key.

The response contains purchased identity/status, a curated `plan_snapshot`, validity, usage, included add-ons, the CMS price breakdown and nullable payment details. Snapshot names, terms and add-on prices preserve the sold record. `included_addons` describes the purchased plan version with current add-on display names and one-off consumption; associations outside the current studio and missing current add-on versions are omitted, while the sold snapshot remains available. A missing historical plan version leaves `bundle_plan_version_id=null` and `included_addons=[]`; saved plan identity and snapshot add-ons remain available. No payment credentials, raw metadata or promo code text are exposed.

`usage.sessions` pages the active, non-deleted sessions holding this bundle's units. Use `page` and `per_page` (default 1 and 20; maximum 100 per page), follow `total`, and retain the returned session and booking IDs. Ordering is studio-local date, start time and session ID. `sessions_used`, `linked_units`, `held_units`, `unlinked_units` and `excess_units` always describe the whole set, independent of the selected page. Manual counter changes and historical discrepancies cannot always be attributed to individual sessions; do not reconstruct the counter by counting one page or treating every session as one unit.

`pricing.itemized=false` identifies the CMS legacy fallback rather than a captured itemized quote. Keep purchased value, payment status, fulfillment and approval/status separate. For external payments, `payment.money` states actual collected/refunded/retained amounts and coverage; payable face value is not proof of collection. `payment=null` is valid for a grant without a payment.

`bundle-rights` remains an eligibility read for new bookings: it excludes exhausted and expired bundles even when filtered by `member_package_id`. `/purchases/{id}` instead accepts the separate API purchase ID for payment recovery. Neither ID should be substituted for a catalog plan ID.

```sh
curl --get "https://app.podyx.com/api/v2/member-bundles/$MEMBER_BUNDLE_ID" \
  -H "Authorization: Bearer $PODYX_API_KEY" \
  --data-urlencode 'page=1' \
  --data-urlencode 'per_page=20'
```

An unknown ID or a purchase belonging to another studio returns 404. Reads need no `Idempotency-Key`. The compatibility route is `GET /api/v1/headless/member-bundles/{id}` with the same fields and permissions.

## Example

A service-first interface can discover offers without picking a room yet:

```sh
curl --get "https://app.podyx.com/api/v2/services" \
  -H "Authorization: Bearer $PODYX_API_KEY" \
  --data-urlencode 'view=offers' \
  --data-urlencode 'page=1'
```

Use the returned service/setup relationships and then request availability for the compatible selection. The displayed configured rate is not permission to submit your own final price.
