Orders

Price a booking before it is made

POST
/api/v1/quotes

The price of a composition BEFORE booking, exactly what POST /api/v1/orders would charge for the same serviceIds, variant, optionAnswers, voucherCode and scheduledStart, computed by the same pricing as the studio's own booking page. Use it instead of calculating prices on your side. Nothing is written or reserved. Also callable with a publishable key (shyni_pk_...).

scheduledStart is optional. With it, the answer carries available, scheduledEnd and pickupAt and any day policy of that date is priced in; without it you get an undated price (no day policy, no times, available: null). Take scheduledStart from GET /api/v1/availability/starts.

A voucherCode that cannot be applied is NOT an error: the answer is 200 with the undiscounted price and codeError (INVALID, EXPIRED, EXHAUSTED or USED, plus an English sentence). A valid code fills discountCode (a studio discount code: lowers totalPrice) or giftVoucher (pays the total, only lowers depositAmount). Money is minor units of currency. depositHoldMinutes says how long an unpaid deposit holds the slot once you book.

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 answers 400 INVALID_INPUT; an unpublished studio 409 STUDIO_NOT_PUBLISHED. Requires the orders:write scope (the verb is POST because of the body; nothing is changed). There is no Idempotency-Key - a quote is always computed fresh. Rate limited like the studio's public price preview, per IP and per studio.

Authorization

ApiKeyAuth orders:write
headerAuthorizationBearer <shyni_sk_live_*>

An API key, issued once and never retrievable again: an organization key in the developer portal, or a studio key in the studio's admin (Moje studio → API). Send as Authorization: Bearer shyni_sk_live_....

Scope: orders:write

Header Parameters

Shyni-Customer-IP?string

POST /orders, POST /quotes, POST /quotes/batch and POST /gift-vouchers/purchases with a secret key only: the end customer's IPv4 or IPv6 address as your server saw it. When present, the storefront's per-IP and per-studio booking (quotes: price preview; gift voucher purchases: purchase) budgets apply to that address (shared with the studio's booking page); an invalid value is 400 INVALID_INPUT. Ignored with a publishable key (the browser's address is used) and on every other endpoint.

Request Body

application/json
  1. body

The pricing fields of OrderCreateRequest, same names and shapes. Unknown fields are 400 UNKNOWN_FIELD (subject and the customer are not part of a price).

serviceIds*array<string>

Service and/or package ids from GET /api/v1/services.

variant*|

Code of the variant the price is for (see POST /orders). Required.

optionAnswers?array<>

Configurator answers, as on POST /orders.

voucherCode?|

A gift voucher or discount code; one that cannot be applied comes back as codeError.

scheduledStart?|

Optional start of the window. Absent = an undated price.

Formatdate-time
waiveDayAdjustment?boolean

Secret key only (publishable + true is 403 FORBIDDEN, field waiveDayAdjustment). Prices as POST /orders does with the same flag: no surcharge day policy, waivedDayAdjustment set.

Defaultfalse
skipDeposit?boolean

Secret key only (publishable + true is 403 FORBIDDEN, field skipDeposit). Prices as POST /orders does with the same flag: depositAmount 0.

Defaultfalse

Response Body

The price.

application/json
  1. response

Money fields are minor units of currency. totalPrice and depositAmount equal what POST /orders charges for the same fields. available, scheduledStart, scheduledEnd and pickupAt are null for an undated quote.

available*|

Whether the window is free (place and team capacity); null for an undated quote.

scheduledStart*|
Formatdate-time
scheduledEnd*|

End of the work; null when undated or when the studio cannot work then (closed day, lead time).

Formatdate-time
pickupAt*|

When the customer can collect (work end plus any curing time).

Formatdate-time
currency*string
totalPrice*integer
Formatint64
depositAmount*integer
Formatint64
durationMinutes*integer
surcharges*array<>

One row per chosen configurator option, in line order.

dayPolicy*|null
discountCode*|null
giftVoucher*|null
codeError*|null
waivedDayAdjustment?|

Day-policy surcharge waived by waiveDayAdjustment (minor units); null/absent when nothing was waived. Then dayPolicy is null.

Formatint64
depositWaivedReason*|
Value in"KNOWN_CAR"null
depositHoldMinutes*|

Minutes an unpaid deposit holds the slot after booking; null when no deposit is due.

Example Request
curl -X POST 'https://api.shyni.io/api/v1/quotes' \  -H 'Authorization: Bearer shyni_sk_test_YOUR_KEY' \  -H 'Content-Type: application/json' \  -d '{  "serviceIds": [    "3b7e9c52-81d4-4f6a-a0c3-2d9e5b8f1a47"  ],  "variant": "MEDIUM",  "scheduledStart": "2026-11-03T08:00:00Z",  "voucherCode": "SPRING10"}'

Response 200

The price.

{  "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}

Response 400

A required field is missing or a value is invalid, or the studio is not taking online bookings right now. field names the offending input when there is one.

{  "code": "INVALID_INPUT",  "message": "Field variant is required.",  "field": "variant"}
{  "code": "INVALID_INPUT",  "message": "The studio quotes this service individually. Send an inquiry instead of a booking.",  "field": "serviceIds"}

Response 401

Missing, malformed, unknown, or revoked API key (INVALID_API_KEY).

{  "code": "INVALID_API_KEY",  "message": "Missing or invalid API key."}

Response 403

The key is valid but the request is refused. Branch on code: INSUFFICIENT_SCOPE (the key lacks the permission this endpoint requires), PLAN_REQUIRED (the studio is not on a plan that includes API keys, Pro and above), ORIGIN_NOT_ALLOWED, SECRET_KEY_IN_BROWSER, or DEMO_STUDIO on a write to the demo studio. See Authentication and Scopes above.

{  "code": "INSUFFICIENT_SCOPE",  "message": "This API key does not have the permission this endpoint requires."}

Response 409

STUDIO_NOT_PUBLISHED - the studio is not published (a test studio always is bookable).

{  "code": "STUDIO_NOT_PUBLISHED",  "message": "The studio is not published; it cannot take bookings."}

Response 429

Burst rate limit exceeded.

{  "code": "RATE_LIMITED",  "message": "Too many requests. Retry after the time given in the Retry-After header."}