Changelog

Every change to the Shyni API contract, newest first.

Every change to the API contract is listed here, newest first. The rules behind it are in Versioning.

2026-11-04

Organizations, businesses and the portal:

  • New guide Subject types and variants: what an order's subject is, how a business defines its subject types and variants, and how they reach POST /orders. The quickstart now uses a General sandbox (a dog grooming salon).
  • New guide Building for a studio: use a studio key from a studio that already uses Shyni, or create a studio for a client and invite its owner.
  • Sign up at dashboard.shyni.io. An organization owns businesses, and every new organization starts with a sandbox business.
  • An organization key acts on a business named in the Shyni-Business header; see Keys and auth. New refusals: 400 BUSINESS_REQUIRED, 400 BUSINESS_HEADER_NOT_ALLOWED and 404 BUSINESS_NOT_FOUND.
  • New scopes businesses:read, businesses:manage, workers:write, payments:read, payments:manage, webhooks:read and webhooks:manage.
  • The API moves to https://api.shyni.io; the staging host stays https://api.shyni.dev. Test mode is free; see Pricing.
  • test-mode is now Sandbox.

The reference now covers organizations, businesses, organization webhooks and the payment gateway:

  • Businesses and Studios: an organization key (without the Shyni-Business header) creates, lists, changes and archives businesses and founds studios for clients.
  • Organization webhooks: one endpoint receives the events of all the organization's businesses.
  • Payment gateway endpoints and new order, review and reschedule webhook events.
  • New 403 BILLING_SUSPENDED for a live organization key whose organization was suspended for an unpaid monthly charge.

2026-10-31

Contract changes on /api/v1:

  • GET and PUT /subject-types: a studio of industry GENERIC defines its subject types (typed attributes, identityKeys, summaryKeys); a detailing studio returns the preset CAR type and PUT is 409.
  • Breaking: subject.attributes on POST /orders and booking links is now a map validated against the studio's type (for CAR: plate, make, model); the typed OrderCreateCarAttributes is gone. The order returns subject {id, type, attributes, summary}.
  • New 400 INVALID_SUBJECT with field naming the attribute key.
  • Catalog variants gain label and customerPickable.

2026-10-30

Contract changes on /api/v1:

  • API plan tiers are retired. Every key has one limit: 600 requests per minute and, for secret keys, 150,000 requests per month. GET /me no longer returns the plan field.

2026-10-29

Contract changes on /api/v1:

  • DELETE /orders/{id}/inspection-photos/{photoId} is now stricter than adding a photo. It returns 409 CONFLICT as soon as the studio has replied to the inspection (any reply, including "come" or "call"), or the term has started or the inspection is closed. Adding a photo stays allowed after a "come" or "call" reply.

2026-10-28

Contract changes on /api/v1:

  • New POST /orders/{id}/revive brings a CANCELLED order back to CONFIRMED; the optional body takes placeId, scheduledStart and notifyCustomer (default true). It works only for an order the studio or the customer cancelled. A deposit that had gone back to the customer becomes NOT_REQUIRED, and a gift voucher is drawn again as far as its balance reaches. Errors: 409 SLOT_UNAVAILABLE, 409 TERM_IN_PAST (new code) and 409 CONFLICT. It emits the new webhook order.revived.
  • New POST /orders/{id}/unready takes the ready mark back (readyAt becomes null) on a CONFIRMED or IN_PROGRESS order that has one; otherwise 409 CONFLICT. It sends nothing to the customer and emits no webhook.

2026-10-27

Contract changes on /api/v1, made in place before the first release (the versioning exception for the time before any integration exists; it ends with the first release):

  • Breaking: the order's vehicle is now subject (order view and POST /orders body). An unknown field such as vehicle is ignored on create, so an old request answers 201 with an order that has no car.
  • Breaking: the webhook order.ready_for_pickup is now order.ready.
  • Breaking: the orders:write scope now only creates a booking (POST /orders) and prices one (POST /quotes, /quotes/batch). Managing existing orders moved to the new orders:manage scope, and money on an order (deposit refund, payments) to the new payments:write scope. A key stored with orders:write stays valid and simply gets narrower. See Keys and auth.
  • POST /places/{id}/unarchive answers 403 when the studio is over its plan's place limit.

2026-10-26

Contract changes on /api/v1:

  • GET /studio returns terms (url, version, publishedAt), the studio's own terms and conditions; it is absent when the studio has none.
  • POST /orders takes an optional acceptedTermsVersion, the terms.version you showed the customer. A version that is not the current one is 409 TERMS_VERSION_STALE (new code); nothing is created. The order views return acceptedTermsVersion when set.
  • Breaking: request bodies are strict. A property the endpoint does not define is 400 UNKNOWN_FIELD (new code) with field naming it, e.g. customer.foo, instead of being ignored. PATCH /studio now refuses the admin-only fields (bank account, currency, allowed domains, reference consent, team, billing, API keys, payment gateway, description) the same way.
  • New POST /orders/{id}/no-show records a customer no-show: a studio cancel with cancellationReason: NO_SHOW, allowed only once the booked term has started. A paid deposit is not refunded automatically. The order.cancelled webhook carries the same reason.
  • POST /orders takes attribution (utmSource, utmMedium, utmCampaign, utmTerm, utmContent, referrer, gclid, gbraid, wbraid, fbclid) and metadata (string to string, capped). Both are returned in the order view, the create response and the webhooks.
  • GET /availability/starts and /availability/calendar take optionAnswers=<questionId>:<optionId>[,...]. Starts beyond the one-year horizon are no longer offered; add-on-only selections get the same 400 INVALID_INPUT as POST /orders.
  • ServiceView gained pricingMode, minAdvanceDays, multiDay, includes, image and storefrontVisible. A publishable key does not list services hidden from the storefront.
  • GET /studio works with a publishable key and returns the public studio view.
  • The spec's examples, required lists and servers (the production and staging hosts) match the real behaviour.

Limits tightened on /api/v1:

  • A publishable key is refused with 403 INSUFFICIENT_SCOPE on everything but GET /services, GET /availability/starts, GET /availability/calendar, GET /studio, POST /quotes, POST /quotes/batch, POST /orders and POST /gift-vouchers/purchases, HEAD and trailing-slash variants included.
  • A request body over 64 KB is 413 FILE_TOO_LARGE; POST /images with a secret key keeps its 60 MB limit.
  • A page too large for a list to hold is 400 INVALID_INPUT on field page.
  • serviceIds on GET /availability/starts and /availability/calendar takes at most 10 ids.
  • The cancel reason is at most 500 characters.
  • A stored idempotent answer is replayed only to the API key that made the first request; another key of the studio reusing the Idempotency-Key gets 422 IDEMPOTENCY_KEY_MISMATCH and nothing is written.

Added for pricing, gift vouchers and free inspections:

  • POST /quotes prices one selection before booking (total, surcharges, deposit) without reserving anything; POST /quotes/batch prices up to 50 at once. See Price before booking.
  • POST /gift-vouchers/purchases sells a gift voucher through the studio's payment gateway. See Gift vouchers online.
  • Free inspections: GET /availability/starts?inspection=true returns inspection windows, POST /orders takes inspection: true, POST /orders/{id}/inspection-photos and DELETE /orders/{id}/inspection-photos/{photoId} manage the customer's photos, and POST /orders/{id}/inspection/reply records the studio's answer (COME, CALL, POINTLESS).
  • Webhook order.inspection_replied when that answer changes.
  • The publishable key may call GET /studio, POST /quotes, POST /quotes/batch and POST /gift-vouchers/purchases.

Added for integrators who keep a copy of the orders:

  • Order lines in serviceLines[] carry serviceId, packageId and optionAnswers (questionId, optionId).
  • The order has updatedAt. GET /orders takes updatedSince= and sortBy=scheduledStart|updatedAt.
  • GET /orders/by-public-id/{publicId} returns one order by its public id.
  • externalRef on the order, the customer and the vehicle: accepted on POST /orders, returned in the views, filterable with ?externalRef= on GET /orders and GET /customers. A collision is 409 CONFLICT.
  • billing (company, company id, VAT id, address) on POST /orders and in the order view.
  • A paid deposit is the first item of payments[]; method is GATEWAY or MANUAL, and payments have a new reference field.
  • Breaking: depositState is NOT_REQUIRED for a zero deposit and for a cancelled order whose deposit was never paid. Such an order is no longer PENDING, so a filter on PENDING no longer matches it.

2026-10-25

Additive changes to POST /orders:

  • The per-studio budget (60 per hour) of the API is separate from the booking page's; the per-IP budget (10 per hour) stays shared.
  • A publishable key at a studio that confirms orders manually can have at most 10 unconfirmed orders; the 11th is 429 PENDING_ORDERS_LIMIT (new code).
  • An unpaid order with a deposit holds the slot 30 minutes with card payment through paymentUrl, 3 hours with bank transfer; the response has holdUntil.
  • An unpublished studio answers 409 STUDIO_NOT_PUBLISHED.

1.0

The first public version of the API, under /api/v1.

On this page