Price before booking

Quote a composition, show the same price on your booking form, price a list in one call and check a discount or voucher code.

A quote is the price a booking would have, computed by the same pricing as the business's own booking page. Use it instead of calculating prices on your side. Nothing is written or reserved.

You need a key with the orders:write scope. A publishable key (shyni_pk_...) works too, so the form can quote straight from the browser.

BASE_URL="https://api.shyni.io"
API_KEY="shyni_sk_REPLACE_ME"

1. Quote one composition

POST /api/v1/quotes takes the pricing fields of Create an order: serviceIds and variant are required, optionAnswers, voucherCode and scheduledStart are optional. Unknown fields (the customer, for example) are 400 UNKNOWN_FIELD. There is no Idempotency-Key: a quote is always computed fresh.

curl -sS -X POST "$BASE_URL/api/v1/quotes" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "serviceIds": ["3b7e9c52-81d4-4f6a-a0c3-2d9e5b8f1a47"],
    "variant": "MEDIUM",
    "scheduledStart": "2026-11-03T08:00:00Z",
    "voucherCode": "SPRING10"
  }'
{
  "available": true,
  "scheduledStart": "2026-11-03T08:00:00Z",
  "scheduledEnd": "2026-11-03T09:30:00Z",
  "pickupAt": "2026-11-03T09:30:00Z",
  "currency": "CZK",
  "totalPrice": 75780,
  "depositAmount": 50000,
  "durationMinutes": 90,
  "surcharges": [],
  "dayPolicy": null,
  "discountCode": {
    "code": "SPRING10",
    "type": "PERCENT",
    "value": 10,
    "discount": 8420,
    "depositWaived": false
  },
  "giftVoucher": null,
  "codeError": null,
  "depositWaivedReason": null,
  "depositHoldMinutes": 30
}

Money is in minor units of currency (hellers for CZK), so 75780 is 757.80 CZK. depositHoldMinutes says how long an unpaid deposit holds the slot after booking (null when no deposit is due). surcharges has one row per chosen configurator option.

dayPolicy is the policy of the start date (name, kind such as VIP or DISCOUNT, percent, and amount, the signed change in minor units against the same selection without the policy), or null. depositWaivedReason says why a deposit is waived; its only value is KNOWN_CAR, otherwise null.

2. Dated and undated prices

scheduledStart is optional. Take it from free start times.

  • With it the answer carries available, scheduledEnd and pickupAt, and any day policy of that date is priced in. scheduledEnd is null when the studio cannot work then (a closed day, lead time).
  • Without it you get an undated price: no day policy, no times, and available: null. That is what you want for a price list.

Services the studio prices by hand (from-price, on request) cannot be quoted or booked: 400 INVALID_INPUT with field: "serviceIds". A studio that paused online bookings also answers 400 INVALID_INPUT, and an unpublished studio 409 STUDIO_NOT_PUBLISHED.

3. Show the same price on the booking form

Quote whenever the customer changes the service, variant, options, code or start time, and render totalPrice and depositAmount from the answer. When the customer submits, send the same fields to Create an order. totalPrice and depositAmount equal what POST /orders charges for the same serviceIds, variant, optionAnswers, voucherCode and scheduledStart, so the price the customer saw is the price they pay. See Booking on your site.

A server integration with a secret key can send waiveDayAdjustment: true (and skipDeposit: true) on the quote, the order or a booking link. The day-policy surcharge is then not charged, and the amount comes back as waivedDayAdjustment (minor units, absent when nothing was waived) instead of a silent discount; a day discount is never waived. skipDeposit makes the deposit NOT_REQUIRED. A publishable key sending true for either gets 403 FORBIDDEN with field set to the flag, and nothing is created.

4. A price list in one call

POST /api/v1/quotes/batch takes 1 to 50 quotes (400 INVALID_INPUT with field: "quotes" otherwise): one per variant, one per day of a calendar, one per option set.

curl -sS -X POST "$BASE_URL/api/v1/quotes/batch" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "quotes": [
      { "serviceIds": ["3b7e9c52-81d4-4f6a-a0c3-2d9e5b8f1a47"], "variant": "SMALL" },
      { "serviceIds": ["3b7e9c52-81d4-4f6a-a0c3-2d9e5b8f1a47"], "variant": "LARGE" },
      { "serviceIds": ["unknown"], "variant": "LARGE" }
    ]
  }'

Answers come back in input order, each { "quote": ... } or { "error": ... }, so one composition that cannot be priced does not fail the others:

{
  "quotes": [
    { "quote": { "totalPrice": 64000, "depositAmount": 50000, "...": "..." } },
    { "quote": { "totalPrice": 92000, "depositAmount": 50000, "...": "..." } },
    { "error": { "code": "NOT_FOUND", "message": "Service not found.", "field": "serviceIds" } }
  ]
}

Failures that concern the whole studio (paused bookings, unpublished studio, rate limit) answer the whole call, as on a single quote.

5. Check a discount or voucher code

Put the customer's code in voucherCode. A code that cannot be applied is not an error: the answer is 200 with the undiscounted price and codeError.

"codeError": { "code": "EXPIRED", "message": "..." }

codeError.code is INVALID, EXPIRED, EXHAUSTED or USED, and message is an English sentence. Branch on code and write your own text. A valid code fills one of two fields:

  • discountCode: a studio discount code. It lowers totalPrice by discount. Its type is PERCENT, FIXED or DEPOSIT_EXEMPT; the last one has a null value and no price discount, and depositWaived tells whether the code waives the deposit.
  • giftVoucher: the voucher pays applied of the total; totalPrice stays, depositAmount drops to reducedDeposit, and remainder is what is left on the voucher.

Limits

A quote is rate limited like the studio's public price preview, per IP and per studio. A batch spends one slot of those budgets regardless of its size. Over the limit you get 429 RATE_LIMITED; see Rate limits. A secret key calling on behalf of a visitor can name the end customer's address in the Shyni-Customer-IP header so the per-IP budget applies to that visitor.

Reference: Price a booking before it is made and Price up to 50 compositions in one call.

On this page