# Quotes & requirements

Use Podyx to resolve the price and the details a customer must provide. Partial selections help you decide which question your interface asks next.

## Inputs and outputs

[Booking quotes](/api/docs#/Studio%20customer%20quotes/v2BookingQuote) accept explicit `sessions[]`. A room/date-only selection can return `status=incomplete`, `missing_inputs` and current requirements. Complete selections include service, concrete setup where required, local date/start, duration and the applicable details.

Returned requirements can include custom forms, guests/seats rules, invoice fields, displayed consent documents and paginated file-transfer choices. Render the actual requirements; do not invent consent defaults or reuse another purchase rail's shape.

Prices, tax, promo eligibility and credits come from Podyx. Amounts are decimal strings with a currency. Caller-supplied prices and provider IDs are refused. `client_id` optionally selects an owned studio client for personalized prices/credits, requires `clients:read`, and does not authenticate a visitor. Email is not accepted in quote input.

## Complete the customer's selection

Collect the missing choices and real acceptance of the displayed documents, then quote again. `status=complete` establishes advisory facts at that moment. It does not hold time, lock a price or authorize charging a customer. Both [native and studio-collected admission](/api/docs/guides/v2/payments) revalidate the same mandatory choices.

A quote creates no client, booking, payment, receipt or job. It is a read-only POST and does not require `Idempotency-Key`. The operation still requires current account/key/public-API and family read authority.

## Different capabilities have different requirements

Ordinary booking quotes, package quotes and [Tour quotes](/api/docs#/Studio%20native%20checkout/v2QuoteTour) are separate contracts. A Tour quote returns a price map rather than generic `status=complete`; use the Tour offer's questions, phone, guest and consent requirements separately. Tour admission requires a pre-updated owned client profile when phone is required. Tour terms-only policy does not imply ordinary booking consent policy.

A Tour's base label can be free while selected add-ons owe money. `line_items[].unit_price` can already be scaled for Tour duration; do not scale it again. Use the returned total.

## Failure and recovery

Validation errors identify malformed or incompatible selections. An incomplete admission can return `422 incomplete_selection`; it is better to collect the missing inputs through quote reads first. A fingerprint identifies current advisory facts, not a price lock. If admission detects stale facts, refresh and obtain any required new customer decision.

## Example

Start with a partial selection on your backend:

```sh
curl "https://app.podyx.com/api/v2/bookings/quotes" \
  -H "Authorization: Bearer $PODYX_API_KEY" \
  -H 'Content-Type: application/json' \
  --data '{"sessions":[{"room_id":"<room-uuid>","date":"2026-11-02"}]}'
```

The partial JSON below illustrates only the facts to inspect, not the full response schema:

```json
{"data":{"status":"incomplete","missing_inputs":["<required-input>"]}}
```

Use the exact `missing_inputs` and requirement structures in the returned response and [OpenAPI](/api/openapi-v2.json). The placeholder above is not a field name to submit.

## Show the service price explanation

Complete booking quotes include `selection.sessions[].price_breakdown`. The legacy
`POST /api/v1/booking-quotes` response includes the same `price_breakdown` beside
its existing totals. Existing `GET /api/v1/bookings/{id}` reads include
`slots[].price_breakdown`; `GET /api/v1/sessions/{id}` reads include
`price_breakdown` directly. This addition does not change existing
quote fields, permissions or booking admission. Quotes remain advisory and
creation validates and prices the selections again.

All monetary strings in a breakdown use its `currency` and that currency's
payable precision. Each `adjustments[]` entry is an adjustment actually used by
the engine, in application order. `label` is the applied rule's checkout name;
`direction` is `discount` or `surcharge`. `amount` is an **unsigned magnitude**:
subtract discounts and add surcharges once. `position` and `source_rule_id` identify
the application and its source where available. `kind` distinguishes a `rule` from
a `duration_tier`. `adjustment_type` and `adjustment_value` preserve the configured
percentage or fixed amount; the applied monetary amount may differ because of
duration, stacking and currency rounding. `running_subtotal` is in the entered
price basis. Do not recalculate adjustments from the configured value.

`catalog_unit_price` and `configured_unit_price` distinguish the catalog rate
from the rate resolved for the selected room/setup. Their corresponding
`*_service_amount` fields scale those rates to the selected duration, before day
rates or dynamic pricing. `base_service_amount` is the base used for this price;
`service_amount` is the final service charge before checkout-level adjustments.
`day_rate` names an applied day rate separately, including its amount and the
by-the-hour comparison. `slot_override` identifies an applied slot override and
its configured value. An absolute override's value is a unit price; a percentage
override's value is a percentage. Neither is presented as a pricing rule.
No matching rules, disabled pricing and slot overrides return `adjustments: []`.

Use `display.base_service_amount`, `display.service_amount` and each row's
`display_amount` to reproduce native checkout's visible tax basis. `display`
also identifies the tax rate/name, tax display setting and price entry mode.
An inclusive display grosses up net-entered amounts; prices already entered
including tax are displayed once. These service rows are separate from the
quote's add-on/transfer amounts, promo `discount`, tax, credits and `amount_due`.
Use the quote's returned checkout totals for those values. Independently rounded
display rows can differ from the displayed final amount by a minor unit; do not
use them to calculate payment totals.

For example, these are the relevant fields from a USD quote with net-entered
prices displayed including 20% tax:

```json
{
  "currency": "USD",
  "base_service_amount": "50.00",
  "service_amount": "54.00",
  "display": {
    "tax_display": "inclusive",
    "price_entry_mode": "excluding_tax",
    "tax_rate": "20",
    "tax_name": "VAT",
    "base_service_amount": "60.00",
    "service_amount": "64.80"
  },
  "adjustments": [
    {"position": 1, "label": "Peak recording", "direction": "surcharge", "adjustment_type": "percentage", "adjustment_value": "20", "amount": "10.00", "display_amount": "12.00"},
    {"position": 2, "label": "Returning client", "direction": "discount", "adjustment_type": "percentage", "adjustment_value": "10", "amount": "6.00", "display_amount": "7.20"}
  ]
}
```

Render the labels as text, preserving the returned order:

```js
const breakdown = quote.selection.sessions[0].price_breakdown;
const money = new Intl.NumberFormat("en-US", {
  style: "currency", currency: breakdown.currency
});
for (const row of breakdown.adjustments) {
  const line = document.createElement("p");
  const sign = row.direction === "discount" ? "−" : "+";
  line.textContent = `${row.label}: ${sign}${money.format(Number(row.display_amount))}`;
  container.append(line);
}
// Peak recording: +$12.00
// Returning client: −$7.20
```

## Historical explanations

New ordinary bookings freeze the explanation, including names, amounts, source
IDs and display metadata, in their pricing snapshot. A read reports
`source: "snapshot"`; renaming, deactivating or deleting rules, or changing the
studio's tax/display configuration, does not rewrite it.

An amendment freezes its revised explanation alongside the revised service
price. Edits that retain the booked service price retain its explanation. When
an older frozen amendment contains no replacement explanation, applying it marks
the explanation unavailable; the original pre-amendment resolution is not reused.

Older records with a captured dynamic-pricing resolution report
`source: "resolution"`. Their stored labels, direction, order and amounts are
returned, including conversion of legacy hourly resolutions to session amounts.
Unavailable catalog/configuration/day-rate metadata and display projections are
`null`; historical display preferences were not captured. A captured slot override
is marked `type: "captured"`, with its final session amount as `value`, because its
original configured type/value may be unavailable.

Records without either captured representation report `source: "unavailable"`,
`adjustments: []` and `null` explanatory amounts/metadata. This does **not** assert
that no rule applied. Use the existing persisted booking totals and omit the
unavailable explanation. Reads never run today's rules or issue a fresh quote to
fill historical gaps. Package and Tour prices retain their existing separate
contracts; an ordinary service-price explanation is returned only where captured.
