Server integration
Use a secret key from your backend to read orders, create bookings and watch usage.
A secret key (shyni_sk_...) belongs on your server. It carries the scopes you
chose when you created it. Keep it in an environment variable and never ship it
to a browser; that is refused with 403 SECRET_KEY_IN_BROWSER anyway.
export SHYNI_KEY="shyni_sk_live_..."Check the key
GET /me needs only a valid secret key, whatever its scopes, so use it first.
curl https://api.shyni.io/api/v1/me -H "Authorization: Bearer $SHYNI_KEY"It returns tenantId, keyName, scopes, mode, monthlyQuota,
usedThisMonth and remaining. See Who am I.
Read orders
orders:read lists orders. Filters: state, depositState, fromDate,
toDate, search, customerEmail, sourceChannel, sourceLabel, and sort
(asc or desc on scheduled start).
curl "https://api.shyni.io/api/v1/orders?state=CONFIRMED&fromDate=2026-11-01&perPage=50" \
-H "Authorization: Bearer $SHYNI_KEY"The answer is {"data": [...], "meta": {"page": 1, "perPage": 50, "total": 137}}.
Walk it with page; see Pagination. One order's
full detail is GET /orders/{id} with the id from the list, not the
publicId. See List orders and
Get an order.
Orders carry customer personal data. Handle it as the studio's data processor.
An order's assignedWorkerId is resolved with GET /workers (orders:read,
secret key), which returns {"data": [{"id", "name"}]}. See
List workers. GET /me also returns the
studio's industry (AUTO or GENERIC); tolerate new values.
Read customers
customers:read lists the studio's customers with page, perPage and
search, and GET /customers/{id} returns one. A customer carries contacts,
subjects (what the customer brought, such as dogs with a name and breed, or for auto detailing cars with plate, make and model), orderCount,
firstOrderAt and lastOrderAt. An order's customer.id is the same id.
curl "https://api.shyni.io/api/v1/customers?search=doe&perPage=50" \
-H "Authorization: Bearer $SHYNI_KEY"An anonymized customer is returned without personal data and has anonymizedAt
set. To keep a CRM copy in step, subscribe to the customer.anonymized
webhook and erase your copy of data.id. See
List customers and
Get a customer.
Create bookings from your server
POST /orders needs orders:write and an Idempotency-Key. The booking page
limits apply to it: the per-IP budget is shared with the page, the per-studio
budget is separate. From a server, pass the end customer's address in the
Shyni-Customer-IP header (IPv4 or IPv6, as your server saw it), and the
per-IP and per-studio budgets are applied to that address. Without the header a
secret key is limited by its per-key burst limit only.
curl -X POST https://api.shyni.io/api/v1/orders \
-H "Authorization: Bearer $SHYNI_KEY" \
-H "Idempotency-Key: crm-4411-booking" \
-H "Shyni-Customer-IP: 203.0.113.7" \
-H "Content-Type: application/json" \
-d '{"serviceIds":["3b7e9c52-81d4-4f6a-a0c3-2d9e5b8f1a47"],
"scheduledStart":"2026-11-03T08:00:00Z","scheduledEnd":"2026-11-03T09:30:00Z",
"customer":{"name":"Jane Doe","email":"jane.doe@example.com","phone":"+1 202 555 0104"},
"variant":"MEDIUM",
"sourceLabel":"partner-web",
"attribution":{"utmSource":"newsletter","utmCampaign":"autumn"},
"metadata":{"crmDealId":"4411"}}'attribution and metadata are optional and come back on the order and in its
webhooks; see Booking on your site.
Every field of the body must be one the endpoint defines: an unknown one is
400 UNKNOWN_FIELD. variant is a code from the service's variants; answers to the service's
questions go in optionAnswers. sourceLabel names your integration on the order (lower-case letters, digits
and hyphens, 40 characters at most). The order's channel is always API. The
studio owner can switch off the customer e-mails per key; then you mail the
customer yourself. An unpaid order with a deposit holds the slot until
holdUntil in the response: 30 minutes when the studio takes card payments
(paymentUrl present), 3 hours for bank transfer. At an unpublished studio the
call is 409 STUDIO_NOT_PUBLISHED. See
Create an order.
Read the studio
GET /studio returns the studio's settings with a secret key (catalog:read)
and PATCH /studio (settings:write) changes them. PATCH refuses the fields only the admin may
change (bank account, currency, allowed domains and the like) with
400 UNKNOWN_FIELD instead of ignoring them silently. See
Get the studio settings and
Update the studio settings.
Watch your usage
Secret keys have a monthly quota, counted on successful calls only. Every
response carries X-Quota-Limit, X-Quota-Used and X-Quota-Remaining. On
429 wait the Retry-After seconds. See Rate limits and quota.
Stay in sync without polling
Poll lists on a schedule if you must, but webhooks push an event when an order or voucher changes.
If you keep a copy of the orders, compare webhook snapshots by updatedAt and
keep the newer one. After an outage, catch up with
GET /orders?updatedSince=<last updatedAt you hold>&sortBy=updatedAt. Store your
own id in externalRef on the order, customer or subject, and look it up with
?externalRef=.