Catalog

Add a component to a package

POST
/api/v1/packages/{id}/components

Adds a service to the package: quantity omitted = 1 (1 to 20), sortOrder omitted = last. A service twice in one package is 400 (field: serviceId), so is one with a "from" or on-request price. Answers with the package, the new component's id included. Draft. Audited with the API key as the actor (a package edit). PRO plan and above, as on the admin. Requires the catalog:write scope and an Idempotency-Key header.

Authorization

ApiKeyAuth catalog: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: catalog:write

Path Parameters

id*string

The package id.

Header Parameters

Idempotency-Key*string

Caller-chosen idempotency token for this write. A retry under the SAME key with the SAME body replays the first response; the SAME key with a DIFFERENT body is 422 IDEMPOTENCY_KEY_MISMATCH. See Idempotency above.

Lengthlength <= 64

Request Body

application/json
  1. body
serviceId*string
quantity?integer
Range1 <= value <= 20
Default1
sortOrder?integer

Omitted = last.

Range0 <= value

Response Body

The package.

application/json
  1. response

A package as the package endpoints show it: the DRAFT row the studio edits, archived packages included. id is the logical id every endpoint takes; the live storefront shows the row only after POST /catalog/publish.

id*string
slug*string
name*string
description*

Formatted text (a studio's own wording with bold, italic, links and lists). The same text in three projections, so an integration picks what suits it.

Forward compatibility: json.v is the schema version; new node and mark types may be added in later versions - skip nodes you don't know and fall back to plain.

scope*ServiceScope
Value in"PRIMARY""SECONDARY""ADDON"
basePrice*integer

Amount in minor units of currency (hellers for CZK).

Formatint64
currency*string
durationMinutes*integer
postWorkStrategy*string
Value in"NONE""NEXT_WORKING_DAY""FIXED_DURATION"
postWorkMinutes*integer|null
variants*array<>
discountPercent*integer
multiDay*boolean
sortOrder*integer
highlight*string
Value in"NONE""MOST_POPULAR""BEST_VALUE""RECOMMENDED"
active*boolean

false = archived.

components*array<>
Example Request
curl -X POST 'https://api.shyni.io/api/v1/packages/string/components' \  -H 'Authorization: Bearer shyni_sk_test_YOUR_KEY' \  -H 'Idempotency-Key: 7f3c9a52-1d4e-4b86-a0c7-5e2b9d8f6a13' \  -H 'Content-Type: application/json' \  -d '{  "serviceId": "string"}'

Response 201

The package.

{  "id": "string",  "slug": "string",  "name": "string",  "description": {    "plain": "string",    "html": "string",    "json": {      "v": 0,      "doc": {}    }  },  "scope": "PRIMARY",  "basePrice": 0,  "currency": "string",  "durationMinutes": 0,  "postWorkStrategy": "NONE",  "postWorkMinutes": 0,  "variants": [    {      "code": "string",      "priceMultiplier": 0,      "extraMinutes": 0    }  ],  "discountPercent": 0,  "multiDay": true,  "sortOrder": 0,  "highlight": "NONE",  "active": true,  "components": [    {      "id": "string",      "serviceId": "string",      "quantity": 0,      "sortOrder": 0    }  ]}

Response 400

A parameter, header or request body is missing, malformed, or out of range. A request body property the endpoint does not define answers UNKNOWN_FIELD instead, with field naming it.

{  "code": "INVALID_INPUT",  "message": "Parameter perPage must be between 1 and 100."}

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 404

The resource does not exist, or belongs to a different studio.

{  "code": "NOT_FOUND",  "message": "Order not found."}

Response 422

The same Idempotency-Key was already used for a request with a different body, use a new key (see Idempotency above).

{  "code": "IDEMPOTENCY_KEY_MISMATCH",  "message": "This Idempotency-Key was already used for a different request. Use a new key."}

Response 429

Burst rate limit exceeded.

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