Subject types and variants

Describe what a customer brings and how it changes the price, then send both with an order.

Every order is for a subject, the thing the customer brings: a dog, a head of hair, a car. A business describes its subjects with a subject type (which attributes a subject has) and prices them with variants (a size, a length, a tier). The API does not assume one kind of business. Auto detailing is one preset; a dog grooming salon, used as the example below, defines its own.

Use a secret key. Reading needs catalog:read, writing needs catalog:write, and every write needs an Idempotency-Key header. See Keys and auth.

Subject types

GET /subject-types lists the types the business accepts; a subject on an order is checked against them. See Read the subject types.

A business of industry GENERIC defines its own types with PUT /subject-types. The call replaces the whole list. See Replace the subject types.

curl -X PUT https://api.shyni.io/api/v1/subject-types \
  -H "Authorization: Bearer $SHYNI_KEY" \
  -H "Shyni-Business: $SHYNI_BUSINESS" \
  -H "Idempotency-Key: dog-type-1" \
  -H "Content-Type: application/json" \
  -d '{
    "types": [{
      "code": "DOG",
      "label": "Dog",
      "attributes": [
        { "key": "name", "label": "Name", "type": "text", "required": true },
        { "key": "breed", "label": "Breed", "type": "text" },
        { "key": "weightKg", "label": "Weight (kg)", "type": "number" }
      ],
      "identityKeys": ["name"],
      "summaryKeys": ["name", "breed"]
    }]
  }'
  • code is the value of subject.type on an order. It uses A-Z, 0-9 and _, starts with a letter and has at most 32 characters.
  • attributes has up to 30 entries. Each has a key (starts with a lower-case letter), a label, a type (text, number, choice or bool) and required. A choice also lists its options.
  • identityKeys name the attributes that make one subject the same subject again, the same dog on the next visit. Empty means every order is a new subject.
  • summaryKeys name the attributes joined into the one-line summary of a subject.

A wrong definition is 400 INVALID_INPUT with field naming the place, such as types[0].attributes[1].key. Orders already stored keep what they recorded. The answer is {"types": [...]}, the same shape GET /subject-types returns.

Variants

A variant is a code that changes the price and the time of a service: the size of the dog, the length of the hair. GET /catalog/variants returns codes, the variants the business offers, and defaults, the prices a new service is prefilled with (null until the business sets them). See Read the variants.

A business of industry GENERIC defines them with PUT /catalog/variants. See Set the default variant prices.

curl -X PUT https://api.shyni.io/api/v1/catalog/variants \
  -H "Authorization: Bearer $SHYNI_KEY" \
  -H "Shyni-Business: $SHYNI_BUSINESS" \
  -H "Idempotency-Key: dog-variants-1" \
  -H "Content-Type: application/json" \
  -d '{
    "variants": [
      { "code": "SMALL", "label": "Small dog (up to 10 kg)", "priceMultiplier": 1.0 },
      { "code": "MEDIUM", "label": "Medium dog (10-25 kg)", "priceMultiplier": 1.2 },
      { "code": "LARGE", "label": "Large dog (over 25 kg)", "priceMultiplier": 1.5 }
    ]
  }'
  • A list has 1 to 30 entries, kept in the order you send them.
  • code has 1 to 16 characters (A-Z, 0-9, _, starting with a letter).
  • priceMultiplier is between 0.1 and 10. extraMinutes (0 to 600, default 0) lengthens the work.
  • label is shown to people and defaults to the code. customerPickable defaults to true; false means staff only. At least one variant must be pickable.

The new codes are the ones POST /orders and a service's variants accept. Existing services keep their own prices. The change is effective at once and is not part of a publish.

Send them with an order

Read the codes from a service's variants (see Quickstart). Pass the same variant to the start times, so the extra minutes are counted, and to the order:

curl "https://api.shyni.io/api/v1/availability/starts?date=2026-11-03&serviceIds=$SERVICE_ID&variant=MEDIUM" \
  -H "Authorization: Bearer $SHYNI_KEY" \
  -H "Shyni-Business: $SHYNI_BUSINESS"
curl -X POST https://api.shyni.io/api/v1/orders \
  -H "Authorization: Bearer $SHYNI_KEY" \
  -H "Shyni-Business: $SHYNI_BUSINESS" \
  -H "Idempotency-Key: dog-booking-1" \
  -H "Content-Type: application/json" \
  -d '{
    "serviceIds": ["'"$SERVICE_ID"'"],
    "scheduledStart": "2026-11-03T08:00:00Z",
    "scheduledEnd": "2026-11-03T09:00:00Z",
    "customer": { "name": "Jane Doe", "email": "jane.doe@example.com", "phone": "+1 202 555 0104" },
    "variant": "MEDIUM",
    "subject": {
      "type": "DOG",
      "attributes": { "name": "Biscuit", "breed": "Beagle", "weightKg": 11 }
    }
  }'

variant is required and must be one of the service's codes; an unknown code is 400 INVALID_INPUT with field: variant. subject is optional unless the business's subject type has a required attribute; then 400 INVALID_SUBJECT names it in field. A publishable key cannot read GET /subject-types, so the form's fields come from your server (secret key with catalog:read) or from your own configuration. The order returns the subject as subject {id, type, attributes, summary}; here summary reads Biscuit · Beagle.

When the subject does not fit

subject.type is matched case-insensitively. Left out, it means the business's only type; a business with several types must send it. subject.attributes is checked against the type:

  • text takes a string, number a number or a numeric string, choice one of its options, bool true or false.
  • A missing required attribute (name above), a value of the wrong type, a choice outside its options or a key the type does not have is refused.

The refusal is 400 INVALID_SUBJECT. field names the attribute key, or subject.type for an unknown type. Nothing is stored and the Idempotency-Key is not used up, so fix the body and send it again with the same key.

{
  "code": "INVALID_SUBJECT",
  "message": "...",
  "field": "name"
}

Branch on code and field, never on message. See Errors.

Auto detailing preset

A business of industry AUTO comes with the built-in subject type CAR (attributes plate, make and model, all optional) and the vehicle-size variants. It cannot change them: PUT /subject-types answers 409 CONFLICT, and PUT /catalog/variants must list the existing codes once each. An order for a car sends "subject": {"attributes": {"plate": "1AB 2345", "make": "Skoda", "model": "Octavia"}} with the code of the vehicle size as variant.

On this page