Price up to 50 compositions in one call
Several POST /api/v1/quotes in one request - one per vehicle size, one per day of a calendar, one per
option set. Answers come back in input order; each is { "quote": ... } or { "error": ... }, so one
composition that cannot be priced (unknown service, missing variant, a service the studio prices by hand)
does not fail the others. An invalid voucherCode is not an error here either, see codeError.
Between 1 and 50 quotes (400 INVALID_INPUT with field quotes otherwise). Failures that concern the
whole studio - paused online bookings, unpublished studio, rate limit - answer the whole call like
POST /api/v1/quotes. The call spends one slot of the per-IP and per-studio preview budgets regardless of
its size. Also callable with a publishable key. Requires the orders:write scope, no Idempotency-Key.
ApiKeyAuth orders:writeAuthorizationBearer <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
Shyni-Customer-IP?stringPOST /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.
application/json- body
quotes*array<>1 <= items <= 50One answer per requested quote, in the same order.
application/json- response
quotes*array<>curl -X POST 'https://api.shyni.io/api/v1/quotes/batch' \ -H 'Authorization: Bearer shyni_sk_test_YOUR_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" } ]}'Response 200
One answer per requested quote, in the same order.
{ "quotes": [ { "quote": { "available": null, "scheduledStart": null, "scheduledEnd": null, "pickupAt": null, "currency": "CZK", "totalPrice": 64000, "depositAmount": 50000, "durationMinutes": 80, "surcharges": [], "dayPolicy": null, "discountCode": null, "giftVoucher": null, "codeError": null, "depositWaivedReason": null, "depositHoldMinutes": 30 } }, { "quote": { "available": null, "scheduledStart": null, "scheduledEnd": null, "pickupAt": null, "currency": "CZK", "totalPrice": 92000, "depositAmount": 50000, "durationMinutes": 110, "surcharges": [], "dayPolicy": null, "discountCode": null, "giftVoucher": null, "codeError": null, "depositWaivedReason": null, "depositHoldMinutes": 30 } }, { "error": { "code": "NOT_FOUND", "message": "Service not found.", "field": "serviceIds" } } ]}Response 400
More than 50 or no quotes (field quotes), or the studio is not taking online bookings right now.
{ "code": "INVALID_INPUT", "message": "Field quotes must hold 1 to 50 quotes.", "field": "quotes"}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."}