{
  "openapi" : "3.1.0",
  "info" : {
    "description" : "The Cliento API gives your own systems server-to-server access to your organisation's data in\nCliento.\n\nEverything in the API belongs to a location. A location is one place of business, such as one\nsalon or one branch of a car wash. Start with `GET /v1/locations` to list the locations your key\ncan use. Almost every other call takes a location id in its path. An item can also be read by its\nid, without its location; see \"Identifiers\".\n\nThis introduction covers what applies to every call. Each part of the reference describes its own\nAPI.\n\n## Industry-specific APIs\n\nMost APIs are used by every organisation, whatever its industry. A few are made for one industry,\nsuch as the Recipes API for hair and beauty and the Vehicles API for automotive. The reference\nlabels such an API with its industry. It can label a single field or value in the same way. The\nlabel is information only: the API does not check an organisation's industry, and no call is\nrefused because of it.\n\n## Authentication\n\nCreate an API key for your organisation and send it on every request:\n\n```\nAuthorization: Bearer cl_live_...\n```\n\nA key belongs to one organisation and is shown only once, when it is created. Keep it on your\nserver; never ship it in a browser or mobile app. If a key leaks, revoke it and create a new one.\n\nEach key carries **scopes** that decide what it may do, for example `customers:read` and\n`customers:write`. A call outside the key's scopes is answered with `403` and code `forbidden`.\n\n## Identifiers\n\nEvery id is an opaque string with a prefix that says what it identifies. Store ids as strings and\ndo not parse them: their length and the characters after the prefix may change. An id from one host, such\nas our staging host, does not exist on another.\n\nAn id is enough to read the item it identifies. Every item has a path under its location, such as\n`/v1/locations/{locationId}/persons/{personId}`, and one without it, `/v1/persons/{personId}`.\nUse the second when you have an id and not its location: an id you stored, or one that another\nitem refers to. It works for an item at any location your key can use, and the answer includes\nthe item's `locationId`. An id at a location your key cannot use is answered like an id that does\nnot exist, with `entity_not_found`.\n\n| Prefix | Identifies | Example |\n|---|---|---|\n| `loc_` | A location | `loc_4fK9xQ2mLr8TzB1vNc7HdW` |\n| `per_` | A person | `per_2QCtWVSaVO7yCRYBj1zsbA` |\n| `com_` | A company | `com_6Tn3wRd8Kq1Vb5Xs9LmPyA` |\n| `veh_` | A vehicle | `veh_3Ks7pQx2Wn9Tb4Vd6HyRcE` |\n| `stf_` | A staff member | `stf_7Qm2xLd9Kc4Vb1Ts8HpWzE` |\n| `mer_` | A merchant | `mer_2Rk8sVh3Yq6Nf1Tz0JwUcM` |\n| `gft_` | A gift card | `gft_7Qm2xN0cT1kPz4Wb9LrEaD` |\n| `prd_` | A product | `prd_5Tg8mPq1Xc7Jd2Ls9WbNrK` |\n| `mov_` | A stock movement | `mov_9Bd2fHk4Mn7Pq1Sv6XzCtA` |\n| `res_` | A resource | `res_2QCtWVSaVO7yCRYBj1zsbA` |\n| `rgr_` | A resource group | `rgr_5Jk9wQe2Ty6Nb3Vx8MzLcA` |\n| `svc_` | A service | `svc_3Gk8rTn5Ym1Wc7Zp2LdQxB` |\n| `sgr_` | A service group | `sgr_8Vc2nXd5Rt1Kq7Lm4HwYpZ` |\n| `rcp_` | A recipe | `rcp_4Kd8sWn2Qy6Hb9Tf3LmVxA` |\n| `bkg_` | A booking | `bkg_5Rt8mKq2Wn7Xb4Vd9HyLcA` |\n| `bks_` | A series of bookings | `bks_6Wq2nYd8Rk4Tc9Xb3HmLzP` |\n| `sal_` | A sale | `sal_8Hd3kWq5Zn2Tb7Xc4LmPyR` |\n\n## Created and updated times\n\nPersons, companies, vehicles, resources, services, products, gift cards and recipes have\n`createdAt` and `updatedAt`, both in UTC.\n\n`updatedAt` is the time the stored item last changed. Use it to tell whether your copy of an item\nis current. `createdAt` is the time the item was created. It is left out where it is not known,\nsuch as for a service created before these times were kept.\n\nA service's `updatedAt` also moves when one of its resource services is added, changed or\nremoved, because a service is read with them.\n\nThree things do not move `updatedAt`:\n\n- A value that changes with time alone, such as a gift card's `status` when the card expires.\n- A change to the location's own settings, such as its time zone.\n- A barcode that a supplier adds to the catalogue article a product is linked to.\n\n## Live and test keys\n\nA key that starts with `cl_live_` belongs to an organisation with real data, in production. Every\nother key starts with `cl_test_`. In production a `cl_test_` key belongs to a sandbox: an\norganisation of its own on the same API and the same host, with data that is yours to create and\nthrow away. A sandbox sends no SMS or email to customers\nand takes no payments. Outside production, such as on our staging host, all keys are `cl_test_`.\n\n## Updating\n\nUpdates use `PATCH` and change only what you send. A field you leave out keeps its value. To\nclear a field, send it as `null`. One request is applied as a whole or not at all.\n\n## Paging\n\nMost lists are paged. Pass the `nextCursor` of one page as `after` to get the next, and go on\nuntil a page has none. Follow `nextCursor` even on a list that fits in one page today. Locations,\nstaff and merchants are returned in one response.\n\nA cursor names the last item you read. Some lists refuse a cursor whose item has been removed\nsince, with `validation_failed` on `after`. Read the list again from the start when that\nhappens.\n\n## Errors\n\nEvery non-2xx response has the same JSON body:\n\n```json\n{ \"code\": \"validation_failed\", \"message\": \"...\", \"details\": { \"fieldErrors\": { \"email\": \"...\" } } }\n```\n\nBranch on `code`; `message` is for humans and may change. An id that does not exist, or that your\nkey may not access, is answered with `400` and code `entity_not_found`. `404` is reserved for a\nURL that is not an endpoint at all.\n\nRequests are validated strictly: an unknown JSON field is rejected with `invalid_request` rather\nthan ignored.\n\n## Rate limits\n\nRequests are limited per API key. When the limit is exceeded the answer is `429` with code\n`rate_limited` and a `Retry-After` header giving the number of seconds to wait.\n\nA request body may be at most 1 MB, far more than any call needs. A larger one is answered with\n`413` and code `invalid_request`.\n\n## Versioning\n\nThe version is in the URL (`/v1`). Every operation in the reference is marked **stable** or\n**preview**.\n\n- A **stable** operation only changes in backwards-compatible ways: new optional request fields,\n  new response fields and new values in open-ended enumerations. Your client must tolerate\n  response fields and enumeration values it does not know. A breaking change to a stable\n  operation means a new version, and the previous version keeps working during a published\n  deprecation period.\n- A **preview** operation works and can be built against, but its fields, paths and answers may\n  still change without a new version. Tell us when you build against one, so that we know to\n  make it stable.\n\nNew operations can be added at any time.\n\n## Amounts\n\nMoney is a decimal string in the location's currency, such as `\"500.00\"`, never a JSON number.\nSend at most two decimals.\n\n## Retrying safely\n\nA timeout or a dropped connection can hide the answer to a request that did succeed. Every call\nthat changes something can be made safe to send again.\n\n**`Idempotency-Key`** is the general way. It is a header that every `POST`, `PATCH` and `DELETE`\nthat changes something accepts, and it holds a value you make up for one request, such as a UUID.\nSend the same value when you retry. The first request runs. For 24 hours every repeat is answered\nwith the first request's answer, marked with the header `Idempotent-Replayed: true`, and does not\nrun again. A value belongs to the API key it was sent with: the same value sent with another key\nis a different request. Send one on every write.\n\n- Using an `Idempotency-Key` for a different request (another path or body) is refused with `422`\n  and `idempotency_key_reused`.\n- A repeat that arrives while the first request is still running is refused with `409` and\n  `request_in_progress`; wait a moment and send it again.\n- An answer of `500` or above is not kept, so a retry runs the request again.\n- A refusal such as `validation_failed` is kept like any other answer: correct the request and\n  send it with a new `Idempotency-Key`.\n\n**`externalReference`** is a second way, for a call that creates something. It is your own id for\nwhat you create, such as an order number or the id in your own system, sent in the body. When a\ncall arrives with a reference that is already in use, nothing is created or changed: the existing\nitem is returned as it is, with `200` instead of `201`. A reference that belongs to a customer\nof another type is refused with `duplicate` instead. Unlike an `Idempotency-Key`, a reference\nlasts as long as the item does, and you can find the item by it later. Persons, companies,\nvehicles, gift cards and stock documents (the stock changes recorded in one call) take one. When\nyou have an id of your own, send it as well as the header.\n",
    "title" : "Cliento API",
    "version" : "v1"
  },
  "servers" : [ {
    "url" : "https://api.cliento.com"
  } ],
  "security" : [ {
    "apiKey" : [ ]
  } ],
  "tags" : [ {
    "description" : "The Locations API lists the locations your API key can use. Start here: almost every other call\ntakes a location id in its path, as `/v1/locations/{locationId}/...`.\n\nA location has an id of the form `loc_...`. An API key belongs to one organisation. It can be\nlimited to some of the organisation's locations.\n\nA customer is registered at one location, and is changed or deleted through that location's path.\nAn organisation can share its customers between its locations. `GET /v1/customers` then lists\nthem for every location your key can use, each with its `locationId`.\n\nAn item can also be read by its id, without its location. See \"Identifiers\" in the introduction.\n",
    "name" : "Locations"
  }, {
    "description" : "The Staff API lists the people who work at a location. Use it when another call needs to say who\ndid something, such as who took stock out or who mixed a colour. Those calls take a staff id\n(`stf_...`).\n\nA staff member is not the same as a resource. A resource is what gets booked in the calendar. A\nstaff member can be linked to one or more resources, or to none.\n\n**PIN check.** If staff use your application, they can identify themselves with the PIN they\nalready use in Cliento's point of sale. Send the typed PIN to\n`POST /v1/locations/{locationId}/staff/pin-check` and you get the staff member back. The check\nsigns nobody in: you still send the staff id on the calls that take one. PINs are never returned.\nThe check needs the scope `staff:pin-check`.\n\nA location chooses whether its staff use PINs. `pinsInUse` in the staff list says which. An\n`Idempotency-Key` is ignored on the check: every check runs.\n",
    "name" : "Staff"
  }, {
    "description" : "Read the hours staff have worked, and record them from your own time clock.",
    "name" : "Staff hours",
    "x-coming-soon" : true
  }, {
    "description" : "The Persons API lets an integration list a location's persons, find one, and create, change and\ndelete them.\n\nA person is an individual customer: someone who books an appointment, or the contact person for a\ncompany or a vehicle. The location communicates with persons, so the phone number, the email\naddress and the consents for SMS and email reminders are on the person.\n\nA person belongs to one location and has an id of the form `per_...`. Reading needs the scope\n`customers:read`, and writing needs `customers:write`.\n\n## Finding a person\n\nFilter the list. Each filter matches the whole value, and several together match persons that have\nall of them:\n\n- `?email=`, an email address. Letter case is ignored.\n- `?phoneNumber=`, in E.164 format with the plus sign percent-encoded (`%2B46701234567`). A number\n  that staff typed in another format is not found.\n- `?identityNumber=`, a personnummer or other identity number as it is stored.\n- `?externalReference=`, the id you gave the person when you created it.\n\nOnly `externalReference` is unique: no two persons, companies or vehicles at a location share one,\nthough the reference of a deleted one can be used again. Two persons can share an email address or\na phone number, and sometimes an identity number, so the other filters can return several persons\nand you choose between them.\n\nWhen you do not know whether you are looking for a person, a company or a vehicle, search\nCustomers instead.\n\n## Identity numbers\n\nA person can have one identity number issued by an authority. It is sent and returned as\n`identityNumber`, with a `type` and a `number`. An `identityNumber` that is not `null` needs\nboth. `null` in an update removes it. The number is kept as you send it. Only surrounding spaces\nand any HTML markup are removed, before it is checked and stored. A personnummer and a\nsamordningsnummer are digits only, with no hyphen or space.\n\n| `type` | What it is | Accepted `number` | Example |\n|---|---|---|---|\n| `se_personnummer` | Swedish personal identity number | Twelve digits, `YYYYMMDDNNNC`: the full year, the day between 01 and 31, and a valid check digit | `198604122385` |\n| `se_samordningsnummer` | Swedish coordination number | Twelve digits like a personnummer, with 60 added to the day (61 to 91), and a valid check digit | `198604722382` |\n| `se_reservnummer` | Swedish reserve number, given in healthcare to someone without the two above | 3 to 32 characters: upper-case letters A to Z, digits and hyphens | `19860412AB27` |\n| `foreign` | A personal identity number from another country | Any text of 1 to 255 characters | `120486-1234` |\n| `other` | Anything else | Any text of 1 to 255 characters | `EMP-00417` |\n\nThe check digit is the last digit and follows the Luhn algorithm over the last ten digits, that\nis, without the century. A number that does not fit its type is refused with `validation_failed`\nand `identityNumber.number` among the `fieldErrors`. More types may be added, so accept a `type`\nyou do not know in a response.\n\nAn identity number is not always unique. A create or an update is normally refused with\n`duplicate` when another person at the location already has the number with the same `type`.\nStaff can still register one number on several persons in Cliento, on purpose. A list filtered on\nthat number then returns each of them. Do not rely on a `type` and `number` identifying exactly\none person.\n",
    "name" : "Persons"
  }, {
    "description" : "The Recipes API is for hair salons. It lets an integration save the colour that was mixed for a\ncustomer, so that staff can read it in Cliento at the next visit. It is meant for a system that\nmixes or measures colour itself, such as a scale or a dispenser.\n\nA recipe belongs to a person and describes one visit. It has one or more bowls. Each bowl lists\nits components and the amount of each.\n\nRecipes are read and added under `/v1/locations/{locationId}/persons/{personId}/recipes`. A\nrecipe has an id of the form `rcp_...`. Reading needs the scope `customers:read`, and adding\nneeds `customers:write`.\n\n- The list is newest first, so its first recipe is what the person got the last time.\n- A recipe also shows in the person's log, the notes staff keep on a person in Cliento, written\n  out as text. Staff can change or remove it there.\n- A recipe is not changed through the API. When the mix is adjusted, add a new one.\n- A recipe moves no stock. A component can name the product it was taken from (`productId`), but\n  the withdrawal is recorded through the Stock movements API. To tie the two together, put the\n  `externalReference` of that stock withdrawal in the recipe's `stockReference`.\n- Amounts are decimal strings, such as `\"22.5\"`, in the unit you send with them.\n- `properties` are your own names and values, for what has no field, such as a processing time.\n  They can be set on the recipe, a bowl or a component. Staff see them in Cliento, written out\n  with the recipe in the person's log.\n- `appData` is a JSON object for your own system's use: it is stored and returned as it is, and\n  never shown in Cliento.\n- A location that keeps medical records, such as a clinic, takes no new recipes: adding one there\n  is refused with `not_allowed`.\n",
    "name" : "Recipes",
    "x-industries" : [ "hair-and-beauty" ]
  }, {
    "description" : "The Companies API lets an integration list a location's companies, find one, and create, change\nand delete them.\n\nA company is a business customer: the owner of a vehicle, or the one that pays the invoice for a\njob. The location does not communicate with a company about a booking. It communicates with a\nperson, such as the company's contact person.\n\nA company belongs to one location and has an id of the form `com_...`. Reading needs the scope\n`customers:read`, and writing needs `customers:write`.\n\n- **`organisationNumber`** is the Swedish organisation number: ten digits with a valid check\n  digit, with no hyphen. A number that does not fit is refused with `validation_failed`.\n- **`department`** names one part of a company, such as `Used cars`, for a location that registers\n  each part as a company of its own.\n- **To find a company**, filter the list on `organisationNumber` or on the `externalReference` you\n  gave it.\n- **An organisation number is not always unique.** A create or an update is normally refused with\n  `duplicate` when another company at the location already has the number, whatever its\n  `department`. Staff can still register one number on several companies in Cliento, on purpose.\n  That is how the parts of one company come to be several companies. Do not rely on a number\n  identifying exactly one company.\n",
    "name" : "Companies"
  }, {
    "description" : "The Vehicles API is for garages, car washes and other automotive businesses. It lets an\nintegration list a location's vehicles, find one, and create, change and delete them.\n\nA vehicle is what the job is done on: the car, not its owner. The owner is a person or a company.\nThe location communicates with the vehicle's contact person, who is a person.\n\nA vehicle belongs to one location and has an id of the form `veh_...`. Reading needs the scope\n`customers:read`, and writing needs `customers:write`.\n\n- **`registrationNumber`** identifies the vehicle. A vehicle needs a registration number or a\n  `description`; it can have both.\n- **`make`, `model`, `modelYear` and `colour`** are filled in from the Swedish vehicle register\n  when staff look the registration number up in Cliento. They are not set through the API and are\n  left out for a vehicle that has not been looked up.\n- **To find a vehicle**, filter the list on `registrationNumber` or on the `externalReference` you\n  gave it.\n- **A registration number is not always unique.** A create or an update is normally refused with\n  `duplicate` when another vehicle at the location already has the number. Staff can still\n  register one number on several vehicles in Cliento, on purpose. Do not rely on a number\n  identifying exactly one vehicle.\n",
    "name" : "Vehicles",
    "x-industries" : [ "automotive" ]
  }, {
    "description" : "The Customers API searches persons, companies and vehicles at once. Use it when you do not know\nwhich type of customer you are looking for. You can search by email address, by phone number, by\nyour own reference, or by an identifying number such as a personnummer or a vehicle registration\nnumber.\n\nA search returns a page of matching customers. Each match is a short entry that says which type\nit is. That type's API has the full record.\n\nThe Customers API is read-only. Each type is created, changed and deleted through its own API.\n\n- **Each entry is short**: its id, its `type`, its `locationId`, its name (for a vehicle, its\n  `description`) and the number that identifies it. The id's prefix (`per_`, `com_`, `veh_`) and\n  `type` say where to read the rest: Persons, Companies or Vehicles.\n- **Filters** match the whole value, and several together match what has all of them: `?email=`,\n  `?phoneNumber=` (in E.164 format with the plus sign percent-encoded), `?number=` (an identity\n  number, an organisation number or a registration number) and `?externalReference=`. Add\n  `?type=` to keep one type, and repeat it for several.\n- An `externalReference` is unique among everything at a location, whatever its type.\n",
    "name" : "Customers"
  }, {
    "description" : "Read and add the notes and files staff keep on a person, a company or a vehicle.",
    "name" : "Customer log",
    "x-coming-soon" : true
  }, {
    "description" : "The Resources API lists a location's resources and the services each one can be booked for. Use\nit to show which resources can be booked. A resource's resource services give the duration and\nprice of each service.\n\nA resource is what gets booked in the calendar. It is most often a person, sometimes a room or a\npiece of equipment. Every booking is made on at least one resource.\n\nA resource belongs to one location and has an id of the form `res_...`.\n`GET /v1/locations/{locationId}/resources` lists the resources. Reading resources needs the scope\n`resources:read`, and reading a resource's services needs `services:read`.\n\n- **A resource is not a staff member.** A staff member is a person who works at the location. A\n  resource is what bookings are made on. A staff member names the resources it is linked to in\n  `resourceIds`.\n- **Resources are listed in groups.** `groupId` names the group.\n  `GET /v1/locations/{locationId}/resource-groups` lists the groups in the order the location\n  shows them.\n- **The services a resource can be booked for** are its resource services, read with\n  `GET /v1/locations/{locationId}/resources/{resourceId}/services`. Each is one service as this\n  resource offers it. It has the service's name, description, group, type and VAT. Its duration\n  and price are the resource's own where the resource has them, and the service's defaults where\n  it does not.\n- **The same services with the location's defaults** are read with\n  `GET /v1/locations/{locationId}/services?resourceId=...`.\n- **`onlineBookingEnabled`** says whether the resource is open for online booking at all. Whether\n  a given service can be booked online with it is `onlineBookingAllowed` on the resource service.\n- **`status`** is `paused` for a resource that is paused for billing, up to and including\n  `pausedUntil`. A paused resource keeps its bookings but takes no new online bookings.\n- **A deleted resource** is left out of the list. It still answers by id, with `status`\n  `deleted`, because earlier bookings refer to it.\n- **`onlineName`** is the name customers see in online booking, when it differs from `name`.\n",
    "name" : "Resources"
  }, {
    "description" : "The Services API lists the services a location offers, with their names, durations and prices.\nUse it to show what can be booked at a location.\n\nA service is something a location offers, such as a haircut, a massage or a class. The location\ngives it a name and a default duration and price.\n\nA resource service is a service as one resource offers it. It uses the service's defaults unless\nthe resource has its own duration or price. To read what one resource offers, read that\nresource's resource services.\n\nA service belongs to one location and has an id of the form `svc_...`.\n`GET /v1/locations/{locationId}/services` lists the services in the order the location shows\nthem. Reading needs the scope `services:read`.\n\n- **Services are listed in groups.** `groupId` names the group.\n  `GET /v1/locations/{locationId}/service-groups` lists the groups in display order.\n- **A resource service** says that a resource can be booked for a service. It has the service's\n  name, description, group, type and VAT. Its duration, price and online booking setting are the\n  ones that apply to that resource. Use them when you know which resource a booking is for.\n- **`ownValues`** says where a resource service's values come from. When it is false, the\n  resource service follows the service's defaults and changes when they do. When it is true, it\n  has its own values, which stay when the service changes.\n- **Resource services are read in two places**: in `resourceServices` on the service, one for each\n  resource it can be booked for, and per resource under\n  `/v1/locations/{locationId}/resources/{resourceId}/services`.\n- **A service can be booked online with a resource when three things hold**: the service is shown\n  online (`shownOnline`), the resource service allows online booking (`onlineBookingAllowed`), and\n  the resource is open for online booking (`onlineBookingEnabled` on the resource). A resource\n  that is paused takes no online bookings whatever the three say.\n- **`onlineBookingAllowed` on the service is the default.** The resource service follows it\n  unless the resource has its own setting. A service can be shown without being bookable, so\n  that customers see it and call.\n- **`afterTimeMinutes`** is time kept free after the booking. It is not part of what the customer\n  is told the service takes.\n- **`priceFrom`** marks a starting price: the final price is set at the visit.\n- **An add-on** (`addOn`) is booked together with another service, never on its own.\n- **A service can have parts with different VAT rates**, listed in `vatParts`. A vaccination is\n  an example: the vaccine is a medical part with no VAT, and the certificate issued with it is a\n  part at 25 % VAT. The service's `price` is then the sum of the parts' prices. When the service\n  is paid for at the point of sale or online, each part is sold at its own rate. The service's\n  `vatPercent` is the single rate used where the service is not split into its parts.\n- **A deleted service** is left out of the list. It still answers by id, with `status` `deleted`,\n  because earlier bookings refer to it.\n- A location can charge more in the evening or on holidays. Those prices are not returned yet;\n  `price` is the ordinary one.\n- Prices follow \"Amounts\" in the introduction.\n",
    "name" : "Services"
  }, {
    "description" : "Read and change when a location is open and when each resource works.",
    "name" : "Schedules",
    "x-coming-soon" : true
  }, {
    "description" : "Ask when a service can be booked, across the resources that offer it.",
    "name" : "Availability",
    "x-coming-soon" : true
  }, {
    "description" : "The Bookings API lets an integration read a location's bookings: the ones that start in a period,\nor one by id. Use it when another system needs to show or count what is booked, such as a\ncustomer portal, a reporting tool or a screen in the salon. Creating, moving and cancelling\nbookings through the API is coming soon.\n\nA booking is a time on one or more resources. Its `type` says what kind it is:\n\n- An `appointment` has the services that were booked and the customers it is for.\n- A `class` has services and customers too. It has a number of places, and each customer books\n  one.\n- A `reservation` only blocks the time, such as a lunch break or a staff meeting. It has no\n  service and no customer.\n\nA booking belongs to one location and has an id of the form `bkg_...`. Reading needs the scope\n`bookings:read`.\n\n- **The list takes a period**: `from` and `to`, at most 62 days apart, matched against\n  `startsAt`. Add `resourceId` for one resource's bookings, or `customerId` for one person's,\n  company's or vehicle's.\n- **`resources`** names the resources the booking is on. Exactly one is the primary resource, the\n  one the booking was made with.\n- **`customers`** names the persons, companies and vehicles the booking is for. The id's prefix\n  says which type, and that type's API has the full record.\n- **A customer on a booking has its `name`**, when the customer has one. A key that also has the scope\n  `customers:read` gets the contact details too: the phone numbers, the email address, the\n  number that identifies the customer and what the customer wrote when booking\n  (`customerNote`). That number is `identityNumber` for a person, `organisationNumber` for a\n  company and `registrationNumber` for a vehicle.\n- **A customer registered at a location your key cannot use** is returned with its `name` only.\n- **The customer's values are the current ones**, read with the booking. Read and change the\n  customer itself through its own API.\n- **The services are kept as they were when the booking was made**: `name`, `durationMinutes` and\n  `price`. A later change to the service, or to the resource service, does not change a booking\n  already made. `serviceId` still refers to the service.\n- **`startsAt` and `endsAt` are in UTC.** They are the start and end of the time taken on the\n  resource. The last `afterTimeMinutes` of that time are kept free after the services, so the\n  services end that many minutes before `endsAt`.\n- **`status`** is `booked` until staff set `show`, `no_show` or `done`, or the booking is\n  `cancelled`. A cancelled booking stays readable and stays in the list.\n- **Each customer has a status of their own.** On an appointment it is the booking's. On a class,\n  one customer can cancel while the others stay booked. `places` and `placesBooked` say how many\n  places the class has and how many are taken.\n- **A `nextCursor` here marks a start time, not a booking.** It stays good when the last booking\n  of a page is moved or removed before you ask for the next page.\n- **A booking that is moved while you page** can show up twice if it moves to a later time, and\n  can be missed if it moves to an earlier one. Read the period again when that matters.\n- **A booking also has** its `payments`, the `sales` it was sold in, its SMS `reminders`, the\n  location's own `customFields`, and the `series` it recurs in.\n- **A sale** is identified by `saleId`. It is read in full from the Sales API, which is coming\n  soon. Reviews are read from the Reviews API, which is coming soon too.\n- **A reservation says what the time is blocked for** in `reservationType`, such as `lunch` or\n  `vacation`, when staff gave a reason.\n- **Nothing from a patient's medical record is returned.** A location that keeps medical records\n  has its health declarations and warnings in the record, not in the booking.\n- **A booking that a customer has started online and not yet completed is not returned**, in the\n  list or by id.\n- Prices follow \"Amounts\" in the introduction.\n",
    "name" : "Bookings"
  }, {
    "description" : "The Merchants API lists a location's merchants. Use it when a call takes or returns a\n`merchantId`, such as the call that imports a gift card.\n\nA merchant is the company that sells at a location: the company whose name is on the receipt.\nMost locations have one merchant, the organisation's own company. Everything that is sold belongs\nto a merchant, not directly to the location.\n\nSome locations have several merchants. In a hair salon with rented chairs, each renter is an\nindependent company. Each is a merchant of its own, with its own point of sale and its own card\nagreement with the bank.\n\nA gift card is sold by one merchant and redeemed at that merchant's point of sale. A location can\nchoose to accept its merchants' gift cards at each other's points of sale.\n\nA merchant belongs to one location and has an id of the form `mer_...`.\n`GET /v1/locations/{locationId}/merchants` lists them. Where a request takes a `merchantId`, you\ncan leave it out when the location has one merchant. With several, a call without it is refused\nwith `merchant_required`.\n",
    "name" : "Merchants"
  }, {
    "description" : "The Gift cards API lets an integration read a location's gift cards, import a card that was sold\noutside Cliento, and void a card that has not been used. It is meant for a system that sells gift\ncards somewhere else, such as a web shop, so that they can be redeemed at the point of sale.\n\nA gift card has a balance. A merchant sold the card and owes that balance to the card's holder.\nThe holder pays with the card's code at the merchant's point of sale.\n\nA gift card belongs to one merchant and has an id of the form `gft_...`. It can be linked to a\ncustomer: a person, a company or a vehicle. Reading needs the scope `giftcards:read`, and\nimporting and voiding need `giftcards:write`.\n\n- **An imported card can be redeemed at once.** Cliento records no sale and makes no receipt for\n  it.\n- **Give each card your own `externalReference`**, such as the order number. Sending the same\n  card again then creates nothing.\n- **A card that has not been used can be voided**, for example when its order is refunded.\n",
    "name" : "Gift cards"
  }, {
    "description" : "Read a merchant's points of sale: the registers its sales are made at.",
    "name" : "Points of sale",
    "x-coming-soon" : true
  }, {
    "description" : "Read what a merchant has sold, line by line, with who sold it and to whom.",
    "name" : "Sales",
    "x-coming-soon" : true
  }, {
    "description" : "Read the receipt of a sale or a refund.",
    "name" : "Receipts",
    "x-coming-soon" : true
  }, {
    "description" : "Read how each sale was paid, such as by card or gift card.",
    "name" : "Payments",
    "x-coming-soon" : true
  }, {
    "description" : "Read the invoices a merchant has sent its customers and whether they are paid.",
    "name" : "Invoices",
    "x-coming-soon" : true
  }, {
    "description" : "The Products API lists a location's products, each with its current stock balance. Use it to show\nwhat a location sells and has in stock, or to look a product up from a scanned barcode or a\ndelivery note.\n\nA product is an article the location sells or uses. A product with stock tracking has a balance.\nThe Products API returns only the current balance. What changed it is read from the Stock\nmovements API.\n\nA product belongs to one location and has an id of the form `prd_...`.\n`GET /v1/locations/{locationId}/products` lists the products a page at a time. Reading needs the\nscope `products:read`.\n\n- **To look one product up**, add `ean` or `articleNumber` to the list.\n- **Quantities are whole numbers**: ten bottles are a `stockQuantity` of 10.\n- **`stockQuantity` is there only for a product the location keeps stock of** (`stockTracked`).\n- **Purchase prices** (`latestPurchasePrice`, `averageCost`) are returned only to a key that also\n  has the scope `stock:cost`.\n",
    "name" : "Products"
  }, {
    "description" : "The Stock movements API lets an integration read every change to a location's stock, and record\ndeliveries and withdrawals of its own. Use it when you need to know what changed a balance and\nwhy, or when you keep your own copy of the balances. The Products API returns only the current\nbalance.\n\nA location has one stock. Every product with stock tracking has a balance in it. A balance is\nnever set directly. It changes when something happens, such as:\n\n- A merchant sells the product at a point of sale.\n- A delivery arrives.\n- Staff take out what they use.\n- A stocktake corrects the balance to what was counted.\n\nA stock movement is the record of one such change to one balance. A movement is never changed or\nremoved. A mistake is undone by a new movement that corrects it.\n\nMovements made by a sale or a stocktake are read here too. The Sales API and the Stocktakes API\nare coming soon.\n\nA colour bar item has a balance in the same stock, counted in packs. A change to it is a stock\nmovement too. A movement has a `productId` or a `colourBarItemId`, never both. When a colour bar\nmix finishes a pack, the pack leaves the stock as a `withdrawal` with the mix's `colourBarMixId`.\nSuch a movement is not corrected on its own; the mix is undone.\n\nA stock movement belongs to one location and has an id of the form `mov_...`.\n`GET /v1/locations/{locationId}/stock-movements` lists the movements oldest first. Reading needs\nthe scope `stock:read`.\n\nThe list can be followed. Keep the id of the last movement you have read and pass it as `after`\nto get what has happened since. That is how to keep your own copy of the balances up to date\nwithout reading every product again. A movement appears in the list about ten seconds after it is\nmade.\n\n## Recording movements\n\n`POST /v1/locations/{locationId}/stock-movements` records a stock document: one or more stock\nchanges that belong together, such as the rows of a delivery note, or products taken from the\nshelf for the location's own use. It needs the scope `stock:write`.\n\n- **A `delivery` or `withdrawal` line names a product or a colour bar item**, with `productId` or\n  `colourBarItemId`. For a colour bar item the quantity is whole packs: a delivery of twelve tubes\n  is `12`. A `correction` line names neither, only the movement it undoes.\n- **Every line is applied, or none.** A line that takes out more than is in stock gets the whole\n  document refused with `insufficient_stock`; `details` names the product or the colour bar item,\n  its balance and the quantity asked for.\n- **`listValue`** is what a withdrawal is worth at the supplier's list price, VAT excluded. It is\n  there when what was taken is linked to an article in a supplier's catalogue that has a list\n  price. A location uses it to charge staff for what they take for themselves, with the `reason`\n  `own-use`.\n- **Give the document your own `externalReference`**, such as the delivery note number. Sending\n  the same reference again changes nothing and answers `200` with the movements made the first\n  time, however long ago.\n- **`staffId` names the staff member who made the change.** Read it from the Staff API: from the\n  staff list, or from the PIN check.\n- **Two line types need a further scope.** A `correction` undoes a withdrawal and needs\n  `stock:correct`. A `delivery` sets what the goods cost and needs `stock:cost`. A key for a\n  screen where staff record what they use needs neither.\n",
    "name" : "Stock movements"
  }, {
    "description" : "Start a count of a location's stock, send what was counted and read the differences.",
    "name" : "Stocktakes",
    "x-coming-soon" : true
  }, {
    "description" : "The Colour bar ranges API lets an integration read a salon's ranges and create, change and delete\nthem.\n\nA range is a supplier's series, such as one brand's permanent colours. It has an id of the form\n`cbr_...` and belongs to a location. The items of a range share three settings, which are set on\nthe range: `kind`, `packSize` and `listPrice`.\n\nRanges are under `/v1/locations/{locationId}/colour-bar-ranges`. A range is also read by its id,\nwithout its location, at `/v1/colour-bar-ranges/{colourBarRangeId}`. Reading needs the scope\n`products:read`. Writing needs `stock:write`.\n\n- `kind` is a lower-case word such as `colour`, `bleach` or `developer`. The list is open: send\n  the word that fits.\n- A range without a `packSize` is one whose items are used a whole pack at a time, such as a\n  treatment. Its items have no open pack, except an item with a `packSize` of its own.\n- `listPrice` is the supplier's list price of one pack, VAT excluded. It is what the location\n  values its use of the range's items at, and is not what the location paid.\n- An item can have a `packSize` and a `listPrice` of its own. Changing the range's `packSize` or\n  `listPrice` changes what applies to every item without a value of its own.\n- A range that still has items cannot be deleted. Delete its items first.\n",
    "name" : "Colour bar ranges",
    "x-industries" : [ "hair-and-beauty" ]
  }, {
    "description" : "The Colour bar items API lets an integration read a salon's colour bar items and create, change\nand delete them.\n\nAn item is one shade in a range. It can also be one strength of a developer or one tub of bleach.\nIt has an id of the form `cbi_...` and belongs to its range's location.\n\nEach item has its own stock. The stock is counted in packs. The item also keeps how many grams\nhave been taken from the open pack.\n\nAn item is not a product. A product is sold at a point of sale and is read through the Products\nAPI. An item is used in the salon and is never sold.\n\nItems are under `/v1/locations/{locationId}/colour-bar-items`. An item is also read by its id,\nwithout its location, at `/v1/colour-bar-items/{colourBarItemId}`. Reading needs the scope\n`products:read`. Writing needs `stock:write`.\n\n- **Stock is counted in packs, and the open pack counts as one.** `packs` is `4` for three\n  unopened tubes and one that is open. `openUsed` is the grams that have been taken from the open\n  pack, and `0` when none is open.\n- `unopenedPacks`, `openLeft` and `gramsLeft` are worked out from `packs`, `openUsed` and the pack\n  size, and are read-only.\n- **`packs` and `openUsed` are read-only here.** A delivery adds packs and a withdrawal takes\n  them: record both through the Stock movements API, on a line with the item's `colourBarItemId`\n  and the quantity in whole packs. `openUsed` changes when a colour bar mix is registered, in\n  Cliento or through the Colour bar mixes API.\n- An item follows its range's `packSize` and `listPrice` unless it has its own. The item's own\n  values are `packSize` and `listPrice`; `packSizeInUse` and `listPriceInUse` are what applies.\n- `eans` holds the item's barcodes. An item made from an article in a supplier's catalogue also\n  has that article's barcodes, which `catalogueEans` lists. Only the item's own barcodes are added\n  and removed here.\n- To find the item for a scanned barcode or a row of a delivery note, list with `ean` or\n  `articleNumber`. Look up the barcode first: a supplier can change its article numbers, and a\n  barcode stays with the pack.\n- A range holds a shade once: a second item with the same `shade` is refused with `duplicate`.\n- An item without a `name` of its own is called by its `shade`.\n",
    "name" : "Colour bar items",
    "x-industries" : [ "hair-and-beauty" ]
  }, {
    "description" : "The Colour bar mixes API lets an integration register what staff mix, read it, undo it and total\nit per staff member. It is for an integration that weighs or doses colour itself and wants\nCliento's stock and totals to follow.\n\nA mix is what a staff member mixed for one customer. It has an id of the form `cbm_...` and\nbelongs to a location. A mix has one or more bowls. A bowl has components. A component names one\ncolour bar item and how many grams were taken from it.\n\nRegistering a mix takes the grams used from each item's stock. The grams are added to the item's\n`openUsed`. When a pack is finished, the item's `packs` goes down by one. That change is recorded\nas a stock movement of the type `withdrawal`.\n\nMixes are under `/v1/locations/{locationId}/colour-bar-mixes`. A mix is also read by its id,\nwithout its location, at `/v1/colour-bar-mixes/{colourBarMixId}`. Reading needs the scope\n`products:read`. Registering needs `stock:write`. Undoing needs `stock:correct`.\n\n- **Amounts are whole grams, as weighed.** For an item with no pack size the amount is whole\n  packs.\n- **A pack leaves the stock when it is finished.** The caller says so with `openedNewPack: true`\n  on the component for which a new pack was opened. If the finished pack ran out midway,\n  `fromFinishedPack` says how many grams of the amount still came out of it. What was never taken\n  from the finished pack is kept on the component as `residue`.\n- **A pack also counts as finished without the caller saying so**, when more has been taken from\n  it than it holds, unless the location has switched that off in Cliento. That setting is not\n  readable through the API: read `packsFinished` on each component of the answer to see what the\n  mix took from stock.\n- **A mix is never refused because an item is out of stock.** What was mixed has been mixed; the\n  item's `packs` stays at `0`.\n- **`listValue` is what the mix took at the supplier's list price**, VAT excluded, worked out\n  when the mix is registered and kept. A later change of a list price does not change it. A\n  component's `listValue` has up to four decimals; the mix's is their sum rounded to two.\n- **Give the mix your own `externalReference`.** Sending the same reference again changes\n  nothing and answers `200` with the mix made the first time, also when that mix has been undone.\n  A reference already used for a mix by another staff member is refused with `not_allowed`. To\n  find the mix for a reference, list with `externalReference`.\n- **`staffId` names the staff member who mixed.** Read it from the Staff API.\n- **`personId` also saves the mix on that person as a recipe**, in the same call, and the mix\n  then carries its `recipeId`. It needs the scope `customers:write` as well.\n- **A mix is not changed.** A mistake is undone: every pack the mix took is put back, the grams\n  it added to each item's `openUsed` are taken off again, and its recipe is removed. The mix is\n  then registered again under a new `externalReference`. An undone mix stays readable, with\n  `undoneAt`.\n- **The summary** totals the mixes of a period per staff member at `listValue`, leaving out the\n  ones that were undone. A location uses these totals to charge the colour used to a staff member\n  who rents a chair.\n- A location that keeps medical records, such as a clinic, has no colour bar mixes: every call\n  is refused with `not_allowed`.\n",
    "name" : "Colour bar mixes",
    "x-industries" : [ "hair-and-beauty" ]
  }, {
    "description" : "Read the SMS and email campaigns a location has sent, and who received them.",
    "name" : "Campaigns",
    "x-coming-soon" : true
  }, {
    "description" : "Read the reviews customers have left after their visits, and a location's rating.",
    "name" : "Reviews",
    "x-coming-soon" : true
  }, {
    "description" : "Read a location's figures over a period: bookings, sales, occupancy and staff hours.",
    "name" : "Reports",
    "x-coming-soon" : true
  } ],
  "paths" : {
    "/v1/bookings/{bookingId}" : {
      "get" : {
        "description" : "The same booking as under its location, found from its id. For a caller that holds an id and not the location it belongs to. The answer carries `locationId`.",
        "operationId" : "getBookingById",
        "parameters" : [ {
          "in" : "path",
          "name" : "bookingId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Booking"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Get a booking by id",
        "tags" : [ "Bookings" ],
        "x-required-scope" : "bookings:read",
        "x-stability" : "preview"
      }
    },
    "/v1/colour-bar-items/{colourBarItemId}" : {
      "get" : {
        "description" : "The same colour bar item as under its location, found from its id. For a caller that holds an id and not the location it belongs to. The answer carries `locationId`.",
        "operationId" : "getColourBarItemById",
        "parameters" : [ {
          "in" : "path",
          "name" : "colourBarItemId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ColourBarItem"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Get a colour bar item by id",
        "tags" : [ "Colour bar items" ],
        "x-required-scope" : "products:read",
        "x-stability" : "preview"
      }
    },
    "/v1/colour-bar-mixes/{colourBarMixId}" : {
      "get" : {
        "description" : "The same colour bar mix as under its location, found from its id. For a caller that holds an id and not the location it belongs to, such as one read from a stock movement. The answer carries `locationId`.",
        "operationId" : "getColourBarMixById",
        "parameters" : [ {
          "in" : "path",
          "name" : "colourBarMixId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ColourBarMix"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Get a colour bar mix by id",
        "tags" : [ "Colour bar mixes" ],
        "x-required-scope" : "products:read",
        "x-stability" : "preview"
      }
    },
    "/v1/colour-bar-ranges/{colourBarRangeId}" : {
      "get" : {
        "description" : "The same colour bar range as under its location, found from its id. For a caller that holds an id and not the location it belongs to. The answer carries `locationId`.",
        "operationId" : "getColourBarRangeById",
        "parameters" : [ {
          "in" : "path",
          "name" : "colourBarRangeId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ColourBarRange"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Get a colour bar range by id",
        "tags" : [ "Colour bar ranges" ],
        "x-required-scope" : "products:read",
        "x-stability" : "preview"
      }
    },
    "/v1/companies/{companyId}" : {
      "get" : {
        "description" : "The same company as under its location, found from its id. For a caller that holds an id and not the location it belongs to. The answer carries `locationId`.",
        "operationId" : "getCompanyById",
        "parameters" : [ {
          "in" : "path",
          "name" : "companyId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Company"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Get a company by id",
        "tags" : [ "Companies" ],
        "x-required-scope" : "customers:read",
        "x-stability" : "preview"
      }
    },
    "/v1/customers" : {
      "get" : {
        "description" : "Lists the persons, companies and vehicles of every location this API key can be used on, as short entries, one page at a time. The call works only when sharing customers between locations is turned on for the organisation in Cliento. Otherwise it is refused with `not_allowed`, and you list customers per location instead. Each entry has the `locationId` it is registered at. Read the full record, change it or delete it under that location, in Persons, Companies or Vehicles. The filters work as on a location's list.",
        "operationId" : "listSharedCustomers",
        "parameters" : [ {
          "description" : "Only those with exactly this email address. Letter case is ignored.",
          "example" : "anna.lindqvist@example.com",
          "in" : "query",
          "name" : "email",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only those with exactly this mobile number, in E.164 format. Percent-encode the plus sign as `%2B`.",
          "example" : "+46701234567",
          "in" : "query",
          "name" : "phoneNumber",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only those you created with this `externalReference`. A reference is unique within a location, not across them, so the list can return one match per location.",
          "example" : "crm-88121",
          "in" : "query",
          "name" : "externalReference",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only those with exactly this number: an identity number, an organisation number or a registration number.",
          "example" : "ABC123",
          "in" : "query",
          "name" : "number",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only these kinds: `person`, `company` or `vehicle`. Repeat the parameter for several.",
          "example" : "person",
          "in" : "query",
          "name" : "type",
          "required" : false,
          "schema" : {
            "type" : "array",
            "items" : {
              "type" : "string"
            }
          }
        }, {
          "description" : "Page size, 1 to 200. Default 50.",
          "in" : "query",
          "name" : "limit",
          "required" : false,
          "schema" : {
            "type" : "integer",
            "format" : "int32"
          }
        }, {
          "description" : "The `nextCursor` of the previous page.",
          "in" : "query",
          "name" : "after",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/CustomerList"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "List customers across locations",
        "tags" : [ "Customers" ],
        "x-required-scope" : "customers:read",
        "x-stability" : "preview"
      }
    },
    "/v1/customers/{customerId}" : {
      "get" : {
        "description" : "The short entry for a person, a company or a vehicle, found from its id. For a caller that holds an id and not the location it belongs to. The answer carries `locationId`.",
        "operationId" : "getCustomerById",
        "parameters" : [ {
          "in" : "path",
          "name" : "customerId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Customer"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Get a customer of any type by id",
        "tags" : [ "Customers" ],
        "x-required-scope" : "customers:read",
        "x-stability" : "preview"
      }
    },
    "/v1/gift-cards/{giftCardId}" : {
      "get" : {
        "description" : "The same gift card as under its location, found from its id. For a caller that holds an id and not the location it belongs to. The answer carries `locationId`.",
        "operationId" : "getGiftCardById",
        "parameters" : [ {
          "in" : "path",
          "name" : "giftCardId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/GiftCard"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Get a gift card by id",
        "tags" : [ "Gift cards" ],
        "x-required-scope" : "giftcards:read",
        "x-stability" : "preview"
      }
    },
    "/v1/locations" : {
      "get" : {
        "description" : "The locations this API key can be used on. Almost everything else is addressed under one of them.",
        "operationId" : "listLocations",
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/LocationList"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "List locations",
        "tags" : [ "Locations" ],
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}" : {
      "get" : {
        "operationId" : "getLocation",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Location"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Get a location",
        "tags" : [ "Locations" ],
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/bookings" : {
      "get" : {
        "description" : "The location's bookings that start in a period, in order of start time, one page at a time. The period is at most 62 days; read a longer one in several calls. Cancelled bookings are included, with `status` `cancelled`. A booking a customer has started online and not yet completed is not.",
        "operationId" : "listBookings",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only bookings that start at or after this time, in UTC.",
          "example" : "2026-10-20T00:00:00Z",
          "in" : "query",
          "name" : "from",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only bookings that start before this time, in UTC.",
          "example" : "2026-10-27T00:00:00Z",
          "in" : "query",
          "name" : "to",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only bookings on this resource.",
          "in" : "query",
          "name" : "resourceId",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only bookings for this person, company or vehicle.",
          "in" : "query",
          "name" : "customerId",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Page size, 1 to 200. Default 50.",
          "in" : "query",
          "name" : "limit",
          "required" : false,
          "schema" : {
            "type" : "integer",
            "format" : "int32"
          }
        }, {
          "description" : "The `nextCursor` of the previous page.",
          "in" : "query",
          "name" : "after",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/BookingList"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "List bookings",
        "tags" : [ "Bookings" ],
        "x-required-scope" : "bookings:read",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/bookings/{bookingId}" : {
      "get" : {
        "operationId" : "getBooking",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "bookingId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Booking"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Get a booking",
        "tags" : [ "Bookings" ],
        "x-required-scope" : "bookings:read",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/colour-bar-items" : {
      "get" : {
        "description" : "The location's colour bar items, range by range and shade by shade, one page at a time. With `ean` or `articleNumber` it is a lookup: the item a scanned barcode or a delivery note row belongs to, at most 20 items. Send one of `rangeId`, `ean` and `articleNumber`, or none.",
        "operationId" : "listColourBarItems",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Page size, 1 to 200. Default 50.",
          "in" : "query",
          "name" : "limit",
          "required" : false,
          "schema" : {
            "type" : "integer",
            "format" : "int32"
          }
        }, {
          "description" : "The `nextCursor` of the previous page.",
          "in" : "query",
          "name" : "after",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only the items of this colour bar range.",
          "in" : "query",
          "name" : "rangeId",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only items with this barcode: their own, or that of the supplier's article they were made from.",
          "in" : "query",
          "name" : "ean",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only items with exactly this article number.",
          "in" : "query",
          "name" : "articleNumber",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ColourBarItemList"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "List colour bar items",
        "tags" : [ "Colour bar items" ],
        "x-required-scope" : "products:read",
        "x-stability" : "preview"
      },
      "post" : {
        "description" : "Adds an item to a colour bar range. It starts with no stock. Send an `Idempotency-Key` so that a repeat of the call does not add the item twice. A shade the range already holds, or a barcode that another item or a product of the location has, is refused with `duplicate`.",
        "operationId" : "createColourBarItem",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "A value you make up for this one request, such as a UUID. Send the same value when you retry and the first answer is returned; see Retrying safely.",
          "example" : "8e03978e-40d5-43e8-bc93-6894a57f9324",
          "in" : "header",
          "name" : "Idempotency-Key",
          "required" : false,
          "schema" : {
            "type" : "string",
            "maxLength" : 255
          }
        } ],
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/ColourBarItemCreate"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "201" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ColourBarItem"
                }
              }
            },
            "description" : "Created"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Create a colour bar item",
        "tags" : [ "Colour bar items" ],
        "x-required-scope" : "stock:write",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/colour-bar-items/{colourBarItemId}" : {
      "delete" : {
        "description" : "Removes the item from the colour bar with whatever stock it holds, and frees its own barcodes.",
        "operationId" : "deleteColourBarItem",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "colourBarItemId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "A value you make up for this one request, such as a UUID. Send the same value when you retry and the first answer is returned; see Retrying safely.",
          "example" : "8e03978e-40d5-43e8-bc93-6894a57f9324",
          "in" : "header",
          "name" : "Idempotency-Key",
          "required" : false,
          "schema" : {
            "type" : "string",
            "maxLength" : 255
          }
        } ],
        "responses" : {
          "204" : {
            "description" : "No Content"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Delete a colour bar item",
        "tags" : [ "Colour bar items" ],
        "x-required-scope" : "stock:write",
        "x-stability" : "preview"
      },
      "get" : {
        "operationId" : "getColourBarItem",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "colourBarItemId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ColourBarItem"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Get a colour bar item",
        "tags" : [ "Colour bar items" ],
        "x-required-scope" : "products:read",
        "x-stability" : "preview"
      },
      "patch" : {
        "description" : "Changes only the fields in the request. Either all of them are changed or none. See `ColourBarItemUpdate` for how to clear a field. An item stays in its range, and its stock is not changed here.",
        "operationId" : "updateColourBarItem",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "colourBarItemId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "A value you make up for this one request, such as a UUID. Send the same value when you retry and the first answer is returned; see Retrying safely.",
          "example" : "8e03978e-40d5-43e8-bc93-6894a57f9324",
          "in" : "header",
          "name" : "Idempotency-Key",
          "required" : false,
          "schema" : {
            "type" : "string",
            "maxLength" : 255
          }
        } ],
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/ColourBarItemUpdate"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ColourBarItem"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Update a colour bar item",
        "tags" : [ "Colour bar items" ],
        "x-required-scope" : "stock:write",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/colour-bar-items/{colourBarItemId}/eans" : {
      "post" : {
        "description" : "Gives the item a barcode of its own, as when a delivery row was matched by its article number and its barcode is new. A barcode that another item or a product of the location has is refused with `duplicate`.",
        "operationId" : "addColourBarItemBarcode",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "colourBarItemId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "A value you make up for this one request, such as a UUID. Send the same value when you retry and the first answer is returned; see Retrying safely.",
          "example" : "8e03978e-40d5-43e8-bc93-6894a57f9324",
          "in" : "header",
          "name" : "Idempotency-Key",
          "required" : false,
          "schema" : {
            "type" : "string",
            "maxLength" : 255
          }
        } ],
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/ColourBarBarcode"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ColourBarItem"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Add a barcode to a colour bar item",
        "tags" : [ "Colour bar items" ],
        "x-required-scope" : "stock:write",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/colour-bar-items/{colourBarItemId}/eans/{ean}" : {
      "delete" : {
        "description" : "Removes one of the item's own barcodes. Removing one of its `catalogueEans` is refused with `not_allowed`.",
        "operationId" : "removeColourBarItemBarcode",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "colourBarItemId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "ean",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "A value you make up for this one request, such as a UUID. Send the same value when you retry and the first answer is returned; see Retrying safely.",
          "example" : "8e03978e-40d5-43e8-bc93-6894a57f9324",
          "in" : "header",
          "name" : "Idempotency-Key",
          "required" : false,
          "schema" : {
            "type" : "string",
            "maxLength" : 255
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ColourBarItem"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Remove a barcode from a colour bar item",
        "tags" : [ "Colour bar items" ],
        "x-required-scope" : "stock:write",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/colour-bar-mixes" : {
      "get" : {
        "description" : "The location's mixes, newest first, one page at a time, including those that were undone. With `staffId`, one staff member's. With `externalReference`, the mix registered under that reference, if there is one; it is sent without `staffId` and `after`.",
        "operationId" : "listColourBarMixes",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Page size, 1 to 200. Default 50.",
          "in" : "query",
          "name" : "limit",
          "required" : false,
          "schema" : {
            "type" : "integer",
            "format" : "int32"
          }
        }, {
          "description" : "The `nextCursor` of the previous page.",
          "in" : "query",
          "name" : "after",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only the mixes of this staff member.",
          "in" : "query",
          "name" : "staffId",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only the mix with this external reference. Not together with `staffId` or `after`.",
          "in" : "query",
          "name" : "externalReference",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ColourBarMixList"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "List colour bar mixes",
        "tags" : [ "Colour bar mixes" ],
        "x-required-scope" : "products:read",
        "x-stability" : "preview"
      },
      "post" : {
        "description" : "Registers what was mixed and changes the colour bar items it names. It is never refused because an item is out of stock: what was mixed has been mixed. Send an `externalReference`, your own id for the mix, to make the call safe to repeat: when the location already has a mix under it, that mix is returned unchanged with `200`, and a reference used for another staff member's mix is refused with `not_allowed`. With `personId` the mix is also saved on that person as a recipe, which needs the scope `customers:write` as well. Not available at a location that keeps medical records.",
        "operationId" : "createColourBarMix",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "A value you make up for this one request, such as a UUID. Send the same value when you retry and the first answer is returned; see Retrying safely.",
          "example" : "8e03978e-40d5-43e8-bc93-6894a57f9324",
          "in" : "header",
          "name" : "Idempotency-Key",
          "required" : false,
          "schema" : {
            "type" : "string",
            "maxLength" : 255
          }
        } ],
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/ColourBarMixCreate"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ColourBarMix"
                }
              }
            },
            "description" : "A mix with this `externalReference` already existed."
          },
          "201" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ColourBarMix"
                }
              }
            },
            "description" : "The mix was registered."
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Register a colour bar mix",
        "tags" : [ "Colour bar mixes" ],
        "x-required-scope" : "stock:write",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/colour-bar-mixes/summary" : {
      "get" : {
        "description" : "What was mixed from `from` to `to`, both days included, in the location's time zone, per staff member, at list price. Mixes that were undone are left out. The period is at most 366 days. A location uses these totals to charge the colour used to a staff member who rents a chair.",
        "operationId" : "summariseColourBarMixes",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "The first day, as `YYYY-MM-DD`.",
          "in" : "query",
          "name" : "from",
          "required" : true,
          "schema" : {
            "type" : "string",
            "format" : "date"
          }
        }, {
          "description" : "The last day, as `YYYY-MM-DD`.",
          "in" : "query",
          "name" : "to",
          "required" : true,
          "schema" : {
            "type" : "string",
            "format" : "date"
          }
        }, {
          "description" : "Only this staff member.",
          "in" : "query",
          "name" : "staffId",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ColourBarMixSummary"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Total colour bar mixes per staff member",
        "tags" : [ "Colour bar mixes" ],
        "x-required-scope" : "products:read",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/colour-bar-mixes/{colourBarMixId}" : {
      "get" : {
        "operationId" : "getColourBarMix",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "colourBarMixId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ColourBarMix"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Get a colour bar mix",
        "tags" : [ "Colour bar mixes" ],
        "x-required-scope" : "products:read",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/colour-bar-mixes/{colourBarMixId}/undo" : {
      "post" : {
        "description" : "Undoes the whole mix, once: every pack it took is put back, the grams it added to each item's `openUsed` are taken off again, and the recipe it was saved as is removed. The mix stays, with `undoneAt`. A mix that is already undone is refused with `not_allowed`.",
        "operationId" : "undoColourBarMix",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "colourBarMixId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "A value you make up for this one request, such as a UUID. Send the same value when you retry and the first answer is returned; see Retrying safely.",
          "example" : "8e03978e-40d5-43e8-bc93-6894a57f9324",
          "in" : "header",
          "name" : "Idempotency-Key",
          "required" : false,
          "schema" : {
            "type" : "string",
            "maxLength" : 255
          }
        } ],
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/ColourBarMixUndo"
              }
            }
          }
        },
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ColourBarMix"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Undo a colour bar mix",
        "tags" : [ "Colour bar mixes" ],
        "x-required-scope" : "stock:correct",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/colour-bar-ranges" : {
      "get" : {
        "description" : "The location's colour bar ranges by name, one page at a time.",
        "operationId" : "listColourBarRanges",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Page size, 1 to 200. Default 50.",
          "in" : "query",
          "name" : "limit",
          "required" : false,
          "schema" : {
            "type" : "integer",
            "format" : "int32"
          }
        }, {
          "description" : "The `nextCursor` of the previous page.",
          "in" : "query",
          "name" : "after",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ColourBarRangeList"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "List colour bar ranges",
        "tags" : [ "Colour bar ranges" ],
        "x-required-scope" : "products:read",
        "x-stability" : "preview"
      },
      "post" : {
        "description" : "Adds a range to the location's colour bar. It holds no items until they are created. Send an `Idempotency-Key` so that a repeat of the call does not add the range twice.",
        "operationId" : "createColourBarRange",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "A value you make up for this one request, such as a UUID. Send the same value when you retry and the first answer is returned; see Retrying safely.",
          "example" : "8e03978e-40d5-43e8-bc93-6894a57f9324",
          "in" : "header",
          "name" : "Idempotency-Key",
          "required" : false,
          "schema" : {
            "type" : "string",
            "maxLength" : 255
          }
        } ],
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/ColourBarRangeCreate"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "201" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ColourBarRange"
                }
              }
            },
            "description" : "Created"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Create a colour bar range",
        "tags" : [ "Colour bar ranges" ],
        "x-required-scope" : "stock:write",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/colour-bar-ranges/{colourBarRangeId}" : {
      "delete" : {
        "description" : "Deleting a range that still has items is refused with `not_allowed`. Delete its items first.",
        "operationId" : "deleteColourBarRange",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "colourBarRangeId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "A value you make up for this one request, such as a UUID. Send the same value when you retry and the first answer is returned; see Retrying safely.",
          "example" : "8e03978e-40d5-43e8-bc93-6894a57f9324",
          "in" : "header",
          "name" : "Idempotency-Key",
          "required" : false,
          "schema" : {
            "type" : "string",
            "maxLength" : 255
          }
        } ],
        "responses" : {
          "204" : {
            "description" : "No Content"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Delete a colour bar range",
        "tags" : [ "Colour bar ranges" ],
        "x-required-scope" : "stock:write",
        "x-stability" : "preview"
      },
      "get" : {
        "operationId" : "getColourBarRange",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "colourBarRangeId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ColourBarRange"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Get a colour bar range",
        "tags" : [ "Colour bar ranges" ],
        "x-required-scope" : "products:read",
        "x-stability" : "preview"
      },
      "patch" : {
        "description" : "Changes only the fields in the request. Either all of them are changed or none. See `ColourBarRangeUpdate` for how to clear a field.",
        "operationId" : "updateColourBarRange",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "colourBarRangeId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "A value you make up for this one request, such as a UUID. Send the same value when you retry and the first answer is returned; see Retrying safely.",
          "example" : "8e03978e-40d5-43e8-bc93-6894a57f9324",
          "in" : "header",
          "name" : "Idempotency-Key",
          "required" : false,
          "schema" : {
            "type" : "string",
            "maxLength" : 255
          }
        } ],
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/ColourBarRangeUpdate"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ColourBarRange"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Update a colour bar range",
        "tags" : [ "Colour bar ranges" ],
        "x-required-scope" : "stock:write",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/companies" : {
      "get" : {
        "description" : "The location's companies, oldest first, one page at a time. To find one, filter on its organisation number or your own reference.",
        "operationId" : "listCompanies",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only companies with exactly this organisation number, ten digits.",
          "example" : 5560360793,
          "in" : "query",
          "name" : "organisationNumber",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only the company you created with this `externalReference`.",
          "example" : "crm-88121",
          "in" : "query",
          "name" : "externalReference",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Page size, 1 to 200. Default 50.",
          "in" : "query",
          "name" : "limit",
          "required" : false,
          "schema" : {
            "type" : "integer",
            "format" : "int32"
          }
        }, {
          "description" : "The `nextCursor` of the previous page.",
          "in" : "query",
          "name" : "after",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/CompanyList"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "List companies",
        "tags" : [ "Companies" ],
        "x-required-scope" : "customers:read",
        "x-stability" : "preview"
      },
      "post" : {
        "description" : "Creates a company at the location. Send an `externalReference`, your own id for the company, to make the call safe to repeat. When a company at the location already has that reference, that company is returned unchanged with `200` and nothing is created. A company with an organisation number that another company at the location already has is normally refused with `duplicate`.",
        "operationId" : "createCompany",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "A value you make up for this one request, such as a UUID. Send the same value when you retry and the first answer is returned; see Retrying safely.",
          "example" : "8e03978e-40d5-43e8-bc93-6894a57f9324",
          "in" : "header",
          "name" : "Idempotency-Key",
          "required" : false,
          "schema" : {
            "type" : "string",
            "maxLength" : 255
          }
        } ],
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/CompanyCreate"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Company"
                }
              }
            },
            "description" : "A company with this `externalReference` already existed."
          },
          "201" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Company"
                }
              }
            },
            "description" : "The company was created."
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Create a company",
        "tags" : [ "Companies" ],
        "x-required-scope" : "customers:write",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/companies/{companyId}" : {
      "delete" : {
        "description" : "A company with upcoming bookings cannot be deleted. Those bookings are cancelled or deleted in Cliento first.",
        "operationId" : "deleteCompany",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "companyId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "A value you make up for this one request, such as a UUID. Send the same value when you retry and the first answer is returned; see Retrying safely.",
          "example" : "8e03978e-40d5-43e8-bc93-6894a57f9324",
          "in" : "header",
          "name" : "Idempotency-Key",
          "required" : false,
          "schema" : {
            "type" : "string",
            "maxLength" : 255
          }
        } ],
        "responses" : {
          "204" : {
            "description" : "No Content"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Delete a company",
        "tags" : [ "Companies" ],
        "x-required-scope" : "customers:write",
        "x-stability" : "preview"
      },
      "get" : {
        "operationId" : "getCompany",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "companyId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Company"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Get a company",
        "tags" : [ "Companies" ],
        "x-required-scope" : "customers:read",
        "x-stability" : "preview"
      },
      "patch" : {
        "description" : "Changes only the fields in the request. Either all of them are changed or none. See `CompanyUpdate` for how to clear a field.",
        "operationId" : "updateCompany",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "companyId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "A value you make up for this one request, such as a UUID. Send the same value when you retry and the first answer is returned; see Retrying safely.",
          "example" : "8e03978e-40d5-43e8-bc93-6894a57f9324",
          "in" : "header",
          "name" : "Idempotency-Key",
          "required" : false,
          "schema" : {
            "type" : "string",
            "maxLength" : 255
          }
        } ],
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/CompanyUpdate"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Company"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Update a company",
        "tags" : [ "Companies" ],
        "x-required-scope" : "customers:write",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/customers" : {
      "get" : {
        "description" : "The location's persons, companies and vehicles in one list of short entries, oldest first. Use it to find something when you do not know its type, for example from a phone number; then read it in full from Persons, Companies or Vehicles, as the id's prefix and `type` say.",
        "operationId" : "listCustomers",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only these kinds: `person`, `company` or `vehicle`. Repeat the parameter for several.",
          "example" : "person",
          "in" : "query",
          "name" : "type",
          "required" : false,
          "schema" : {
            "type" : "array",
            "items" : {
              "type" : "string"
            }
          }
        }, {
          "description" : "Only those with exactly this email address. Letter case is ignored.",
          "example" : "anna.lindqvist@example.com",
          "in" : "query",
          "name" : "email",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only those with exactly this mobile number, in E.164 format. Percent-encode the plus sign as `%2B`.",
          "example" : "+46701234567",
          "in" : "query",
          "name" : "phoneNumber",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only those with exactly this number: an identity number, an organisation number or a registration number.",
          "example" : "ABC123",
          "in" : "query",
          "name" : "number",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only the one you created with this `externalReference`.",
          "example" : "crm-88121",
          "in" : "query",
          "name" : "externalReference",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Page size, 1 to 200. Default 50.",
          "in" : "query",
          "name" : "limit",
          "required" : false,
          "schema" : {
            "type" : "integer",
            "format" : "int32"
          }
        }, {
          "description" : "The `nextCursor` of the previous page.",
          "in" : "query",
          "name" : "after",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/CustomerList"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "List customers of every type",
        "tags" : [ "Customers" ],
        "x-required-scope" : "customers:read",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/customers/{customerId}" : {
      "get" : {
        "description" : "The short entry for a person, a company or a vehicle, by its id.",
        "operationId" : "getCustomer",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "customerId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Customer"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Get a customer of any type",
        "tags" : [ "Customers" ],
        "x-required-scope" : "customers:read",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/gift-cards" : {
      "get" : {
        "description" : "The location's gift cards, newest first, one page at a time. To read them all, repeat the call with `after` set to the `nextCursor` of the previous page until a page has none.",
        "operationId" : "listGiftCards",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Page size, 1 to 200. Default 50.",
          "in" : "query",
          "name" : "limit",
          "required" : false,
          "schema" : {
            "type" : "integer",
            "format" : "int32"
          }
        }, {
          "description" : "The `nextCursor` of the previous page.",
          "in" : "query",
          "name" : "after",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only this customer's cards.",
          "in" : "query",
          "name" : "customerId",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only cards with this external reference.",
          "in" : "query",
          "name" : "externalReference",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only the card with this code.",
          "in" : "query",
          "name" : "code",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/GiftCardList"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "List gift cards",
        "tags" : [ "Gift cards" ],
        "x-required-scope" : "giftcards:read",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/gift-cards/import" : {
      "post" : {
        "description" : "Records a gift card that was sold somewhere else, such as a web shop. Cliento records no sale and makes no receipt for it. The card can be redeemed at the merchant's point of sale at once. Answers `201` for a new card and `200` with the existing card when `externalReference` was used before, so the call is safe to retry.",
        "operationId" : "importGiftCard",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "A value you make up for this one request, such as a UUID. Send the same value when you retry and the first answer is returned; see Retrying safely.",
          "example" : "8e03978e-40d5-43e8-bc93-6894a57f9324",
          "in" : "header",
          "name" : "Idempotency-Key",
          "required" : false,
          "schema" : {
            "type" : "string",
            "maxLength" : 255
          }
        } ],
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/GiftCardImport"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/GiftCard"
                }
              }
            },
            "description" : "A card with this `externalReference` already existed. It is returned as it is now; nothing in this request was applied."
          },
          "201" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/GiftCard"
                }
              }
            },
            "description" : "The card was created."
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Import a gift card",
        "tags" : [ "Gift cards" ],
        "x-required-scope" : "giftcards:write",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/gift-cards/{giftCardId}" : {
      "get" : {
        "operationId" : "getGiftCard",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "giftCardId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/GiftCard"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Get a gift card",
        "tags" : [ "Gift cards" ],
        "x-required-scope" : "giftcards:read",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/gift-cards/{giftCardId}/void" : {
      "post" : {
        "description" : "Cancels a card that has not been used, for example when its web shop order is refunded. A card that has been used, even in part, or is already void is refused with `gift_card_already_used`.",
        "operationId" : "voidGiftCard",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "giftCardId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "A value you make up for this one request, such as a UUID. Send the same value when you retry and the first answer is returned; see Retrying safely.",
          "example" : "8e03978e-40d5-43e8-bc93-6894a57f9324",
          "in" : "header",
          "name" : "Idempotency-Key",
          "required" : false,
          "schema" : {
            "type" : "string",
            "maxLength" : 255
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/GiftCard"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Void a gift card",
        "tags" : [ "Gift cards" ],
        "x-required-scope" : "giftcards:write",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/merchants" : {
      "get" : {
        "description" : "The merchants of a location, oldest first. Most locations have exactly one. Where a request takes a `merchantId`, it can be left out when the location has one.",
        "operationId" : "listMerchants",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/MerchantList"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "List merchants",
        "tags" : [ "Merchants" ],
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/persons" : {
      "get" : {
        "description" : "The location's persons, oldest first, one page at a time. To find one, use the filters instead of reading the whole list; \"Finding a person\" under Persons says how each one matches.",
        "operationId" : "listPersons",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only persons with exactly this email address, whatever its letter case.",
          "example" : "anna.lindqvist@example.com",
          "in" : "query",
          "name" : "email",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only persons with exactly this mobile number, in E.164 format. Percent-encode the plus sign as `%2B`.",
          "example" : "+46701234567",
          "in" : "query",
          "name" : "phoneNumber",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only persons with exactly this identity number, as it is stored: a Swedish personnummer is twelve digits.",
          "example" : 198604122385,
          "in" : "query",
          "name" : "identityNumber",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only the person you created with this `externalReference`.",
          "example" : "crm-88121",
          "in" : "query",
          "name" : "externalReference",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Page size, 1 to 200. Default 50.",
          "in" : "query",
          "name" : "limit",
          "required" : false,
          "schema" : {
            "type" : "integer",
            "format" : "int32"
          }
        }, {
          "description" : "The `nextCursor` of the previous page.",
          "in" : "query",
          "name" : "after",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/PersonList"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "List persons",
        "tags" : [ "Persons" ],
        "x-required-scope" : "customers:read",
        "x-stability" : "preview"
      },
      "post" : {
        "description" : "Creates a person at the location. Send an `externalReference`, your own id for the person, to make the call safe to repeat. When a person at the location already has that reference, that person is returned unchanged with `200` and nothing is created. A person with an identity number that another person at the location already has, with the same `type`, is normally refused with `duplicate`.",
        "operationId" : "createPerson",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "A value you make up for this one request, such as a UUID. Send the same value when you retry and the first answer is returned; see Retrying safely.",
          "example" : "8e03978e-40d5-43e8-bc93-6894a57f9324",
          "in" : "header",
          "name" : "Idempotency-Key",
          "required" : false,
          "schema" : {
            "type" : "string",
            "maxLength" : 255
          }
        } ],
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/PersonCreate"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Person"
                }
              }
            },
            "description" : "A person with this `externalReference` already existed."
          },
          "201" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Person"
                }
              }
            },
            "description" : "The person was created."
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Create a person",
        "tags" : [ "Persons" ],
        "x-required-scope" : "customers:write",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/persons/{personId}" : {
      "delete" : {
        "description" : "A person with upcoming bookings cannot be deleted. Those bookings are cancelled or deleted in Cliento first.",
        "operationId" : "deletePerson",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "personId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "A value you make up for this one request, such as a UUID. Send the same value when you retry and the first answer is returned; see Retrying safely.",
          "example" : "8e03978e-40d5-43e8-bc93-6894a57f9324",
          "in" : "header",
          "name" : "Idempotency-Key",
          "required" : false,
          "schema" : {
            "type" : "string",
            "maxLength" : 255
          }
        } ],
        "responses" : {
          "204" : {
            "description" : "No Content"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Delete a person",
        "tags" : [ "Persons" ],
        "x-required-scope" : "customers:write",
        "x-stability" : "preview"
      },
      "get" : {
        "operationId" : "getPerson",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "personId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Person"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Get a person",
        "tags" : [ "Persons" ],
        "x-required-scope" : "customers:read",
        "x-stability" : "preview"
      },
      "patch" : {
        "description" : "Changes only the fields in the request. Either all of them are changed or none. See `PersonUpdate` for how to clear a field.",
        "operationId" : "updatePerson",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "personId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "A value you make up for this one request, such as a UUID. Send the same value when you retry and the first answer is returned; see Retrying safely.",
          "example" : "8e03978e-40d5-43e8-bc93-6894a57f9324",
          "in" : "header",
          "name" : "Idempotency-Key",
          "required" : false,
          "schema" : {
            "type" : "string",
            "maxLength" : 255
          }
        } ],
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/PersonUpdate"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Person"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Update a person",
        "tags" : [ "Persons" ],
        "x-required-scope" : "customers:write",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/persons/{personId}/recipes" : {
      "get" : {
        "description" : "The person's recipes, newest first, one page at a time. The first one is what was mixed the last time.",
        "operationId" : "listRecipes",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "personId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Page size, 1 to 200. Default 50.",
          "in" : "query",
          "name" : "limit",
          "required" : false,
          "schema" : {
            "type" : "integer",
            "format" : "int32"
          }
        }, {
          "description" : "The `nextCursor` of the previous page.",
          "in" : "query",
          "name" : "after",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/RecipeList"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "List a person's recipes",
        "tags" : [ "Recipes" ],
        "x-required-scope" : "customers:read",
        "x-stability" : "preview"
      },
      "post" : {
        "description" : "Adds a recipe to the person. It also shows in the person's log in Cliento, where staff read it. A recipe is not changed afterwards through the API: when the mix is adjusted, add a new recipe. Send an `Idempotency-Key` so that a repeat of the call does not add the recipe twice. Not available at a location that keeps medical records.",
        "operationId" : "createRecipe",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "personId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "A value you make up for this one request, such as a UUID. Send the same value when you retry and the first answer is returned; see Retrying safely.",
          "example" : "8e03978e-40d5-43e8-bc93-6894a57f9324",
          "in" : "header",
          "name" : "Idempotency-Key",
          "required" : false,
          "schema" : {
            "type" : "string",
            "maxLength" : 255
          }
        } ],
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/RecipeCreate"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "201" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Recipe"
                }
              }
            },
            "description" : "Created"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Add a recipe to a person",
        "tags" : [ "Recipes" ],
        "x-required-scope" : "customers:write",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/persons/{personId}/recipes/{recipeId}" : {
      "get" : {
        "operationId" : "getRecipe",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "personId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "recipeId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Recipe"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Get a recipe",
        "tags" : [ "Recipes" ],
        "x-required-scope" : "customers:read",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/products" : {
      "get" : {
        "description" : "The location's products, one page at a time, including those that are not shown on the point-of-sale screen as products that can be sold. To read them all, repeat the call with `after` set to the `nextCursor` of the previous page until a page has none. With `ean` or `articleNumber` it is a lookup: the product a scanned barcode or a delivery note row belongs to.",
        "operationId" : "listProducts",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Page size, 1 to 200. Default 50.",
          "in" : "query",
          "name" : "limit",
          "required" : false,
          "schema" : {
            "type" : "integer",
            "format" : "int32"
          }
        }, {
          "description" : "The `nextCursor` of the previous page.",
          "in" : "query",
          "name" : "after",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only products with this barcode. Normally one; Two can match, because a product also has the barcodes of the supplier's article it is linked to; see `eans`.",
          "in" : "query",
          "name" : "ean",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only products with exactly this article number.",
          "in" : "query",
          "name" : "articleNumber",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ProductList"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "List products",
        "tags" : [ "Products" ],
        "x-required-scope" : "products:read",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/products/{productId}" : {
      "get" : {
        "operationId" : "getProduct",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "productId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Product"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Get a product",
        "tags" : [ "Products" ],
        "x-required-scope" : "products:read",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/resource-groups" : {
      "get" : {
        "description" : "The groups the location's resources are listed under, in display order.",
        "operationId" : "listResourceGroups",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Page size, 1 to 200. Default 50.",
          "in" : "query",
          "name" : "limit",
          "required" : false,
          "schema" : {
            "type" : "integer",
            "format" : "int32"
          }
        }, {
          "description" : "The `nextCursor` of the previous page.",
          "in" : "query",
          "name" : "after",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ResourceGroupList"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "List resource groups",
        "tags" : [ "Resources" ],
        "x-required-scope" : "resources:read",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/resources" : {
      "get" : {
        "description" : "The location's resources, without those that are deleted.",
        "operationId" : "listResources",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Page size, 1 to 200. Default 50.",
          "in" : "query",
          "name" : "limit",
          "required" : false,
          "schema" : {
            "type" : "integer",
            "format" : "int32"
          }
        }, {
          "description" : "The `nextCursor` of the previous page.",
          "in" : "query",
          "name" : "after",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ResourceList"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "List resources",
        "tags" : [ "Resources" ],
        "x-required-scope" : "resources:read",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/resources/{resourceId}" : {
      "get" : {
        "description" : "Also answers for a deleted resource, with `status` `deleted`.",
        "operationId" : "getResource",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "resourceId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Resource"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Get a resource",
        "tags" : [ "Resources" ],
        "x-required-scope" : "resources:read",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/resources/{resourceId}/services" : {
      "get" : {
        "description" : "Lists the services the resource can be booked for, in the order the location lists its services. Each is a resource service. It has the duration, price and online booking setting that apply for this resource. `ownValues` says whether they are the resource's own or the service's defaults.",
        "operationId" : "listResourceServices",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "resourceId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Page size, 1 to 200. Default 50.",
          "in" : "query",
          "name" : "limit",
          "required" : false,
          "schema" : {
            "type" : "integer",
            "format" : "int32"
          }
        }, {
          "description" : "The `nextCursor` of the previous page: a service id.",
          "in" : "query",
          "name" : "after",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ResourceServiceList"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "List a resource's resource services",
        "tags" : [ "Resources" ],
        "x-required-scope" : "services:read",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/resources/{resourceId}/services/{serviceId}" : {
      "get" : {
        "description" : "One service as this resource offers it.",
        "operationId" : "getResourceService",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "resourceId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "serviceId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ResourceService"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Get a resource service",
        "tags" : [ "Resources" ],
        "x-required-scope" : "services:read",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/service-groups" : {
      "get" : {
        "description" : "The groups the location's services are listed under, in display order.",
        "operationId" : "listServiceGroups",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Page size, 1 to 200. Default 50.",
          "in" : "query",
          "name" : "limit",
          "required" : false,
          "schema" : {
            "type" : "integer",
            "format" : "int32"
          }
        }, {
          "description" : "The `nextCursor` of the previous page.",
          "in" : "query",
          "name" : "after",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ServiceGroupList"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "List service groups",
        "tags" : [ "Services" ],
        "x-required-scope" : "services:read",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/services" : {
      "get" : {
        "description" : "The location's services in the order the location lists them, without those that are deleted.",
        "operationId" : "listServices",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only the services that have a resource service for this resource.",
          "in" : "query",
          "name" : "resourceId",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Page size, 1 to 200. Default 50.",
          "in" : "query",
          "name" : "limit",
          "required" : false,
          "schema" : {
            "type" : "integer",
            "format" : "int32"
          }
        }, {
          "description" : "The `nextCursor` of the previous page.",
          "in" : "query",
          "name" : "after",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ServiceList"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "List services",
        "tags" : [ "Services" ],
        "x-required-scope" : "services:read",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/services/{serviceId}" : {
      "get" : {
        "description" : "Also answers for a deleted service, with `status` `deleted`.",
        "operationId" : "getService",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "serviceId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Service"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Get a service",
        "tags" : [ "Services" ],
        "x-required-scope" : "services:read",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/staff" : {
      "get" : {
        "description" : "The location's staff members, sorted by name. Use a staff id to say who did something, for example who took stock out. PINs are never returned; `hasPin` says whether a person has one.",
        "operationId" : "listStaff",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/StaffList"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "List staff",
        "tags" : [ "Staff" ],
        "x-required-scope" : "staff:read",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/staff/pin-check" : {
      "post" : {
        "description" : "Says whose PIN was typed, so your own screen can let staff identify themselves with the PIN they already use at the point of sale. A PIN belongs to one person at a location. Nothing is signed in and no token is issued: send the staff id on the calls that take one. A PIN nobody has is refused with `pin_not_recognised`. An `Idempotency-Key` is ignored here.",
        "operationId" : "checkStaffPin",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/PinCheck"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/PinCheckResult"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Check a PIN",
        "tags" : [ "Staff" ],
        "x-required-scope" : "staff:pin-check",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/stock-movements" : {
      "get" : {
        "description" : "Every change of a stock balance at the location, oldest first, including sales at the point of sale. The list can be followed: keep the id of the last movement you have read and pass it as `after` to get what has happened since. A movement appears in the list about ten seconds after it is made.",
        "operationId" : "listStockMovements",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Page size, 1 to 200. Default 50.",
          "in" : "query",
          "name" : "limit",
          "required" : false,
          "schema" : {
            "type" : "integer",
            "format" : "int32"
          }
        }, {
          "description" : "Only movements made after this one: a `nextCursor` or the id of any movement.",
          "in" : "query",
          "name" : "after",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only this product's movements.",
          "in" : "query",
          "name" : "productId",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only this colour bar item's movements.",
          "in" : "query",
          "name" : "colourBarItemId",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only movements of this `type`.",
          "in" : "query",
          "name" : "type",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only movements with this external reference.",
          "in" : "query",
          "name" : "externalReference",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/StockMovementList"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "List stock movements",
        "tags" : [ "Stock movements" ],
        "x-required-scope" : "stock:read",
        "x-stability" : "preview"
      },
      "post" : {
        "description" : "Records a document of stock changes: a delivery note, products taken from the shelf for the location's own use. Every line is applied, or none: a line that takes out more than is in stock gets the whole document refused with `insufficient_stock`. Answers `201` with the movements made, and `200` with the movements made the first time when `externalReference` was used before. A `correction` line needs the scope `stock:correct` as well, and a `delivery` line the scope `stock:cost`, since it sets what the goods cost.",
        "operationId" : "recordStockMovements",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "A value you make up for this one request, such as a UUID. Send the same value when you retry and the first answer is returned; see Retrying safely.",
          "example" : "8e03978e-40d5-43e8-bc93-6894a57f9324",
          "in" : "header",
          "name" : "Idempotency-Key",
          "required" : false,
          "schema" : {
            "type" : "string",
            "maxLength" : 255
          }
        } ],
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/StockDocument"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/StockDocumentResult"
                }
              }
            },
            "description" : "The `externalReference` was used before. Nothing in this request was applied; the movements made then are returned."
          },
          "201" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/StockDocumentResult"
                }
              }
            },
            "description" : "The movements were made."
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Record stock movements",
        "tags" : [ "Stock movements" ],
        "x-required-scope" : "stock:write",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/vehicles" : {
      "get" : {
        "description" : "The location's vehicles, oldest first, one page at a time. To find one, filter on its registration number or your own reference.",
        "operationId" : "listVehicles",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only vehicles with exactly this registration number.",
          "example" : "ABC123",
          "in" : "query",
          "name" : "registrationNumber",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Only the vehicle you created with this `externalReference`.",
          "example" : "crm-88121",
          "in" : "query",
          "name" : "externalReference",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "Page size, 1 to 200. Default 50.",
          "in" : "query",
          "name" : "limit",
          "required" : false,
          "schema" : {
            "type" : "integer",
            "format" : "int32"
          }
        }, {
          "description" : "The `nextCursor` of the previous page.",
          "in" : "query",
          "name" : "after",
          "required" : false,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/VehicleList"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "List vehicles",
        "tags" : [ "Vehicles" ],
        "x-required-scope" : "customers:read",
        "x-stability" : "preview"
      },
      "post" : {
        "description" : "Creates a vehicle at the location. Send an `externalReference`, your own id for the vehicle, to make the call safe to repeat. When a vehicle at the location already has that reference, that vehicle is returned unchanged with `200` and nothing is created. A vehicle with a registration number that another vehicle at the location already has is normally refused with `duplicate`.",
        "operationId" : "createVehicle",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "A value you make up for this one request, such as a UUID. Send the same value when you retry and the first answer is returned; see Retrying safely.",
          "example" : "8e03978e-40d5-43e8-bc93-6894a57f9324",
          "in" : "header",
          "name" : "Idempotency-Key",
          "required" : false,
          "schema" : {
            "type" : "string",
            "maxLength" : 255
          }
        } ],
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/VehicleCreate"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Vehicle"
                }
              }
            },
            "description" : "A vehicle with this `externalReference` already existed."
          },
          "201" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Vehicle"
                }
              }
            },
            "description" : "The vehicle was created."
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Create a vehicle",
        "tags" : [ "Vehicles" ],
        "x-required-scope" : "customers:write",
        "x-stability" : "preview"
      }
    },
    "/v1/locations/{locationId}/vehicles/{vehicleId}" : {
      "delete" : {
        "description" : "A vehicle with upcoming bookings cannot be deleted. Those bookings are cancelled or deleted in Cliento first.",
        "operationId" : "deleteVehicle",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "vehicleId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "A value you make up for this one request, such as a UUID. Send the same value when you retry and the first answer is returned; see Retrying safely.",
          "example" : "8e03978e-40d5-43e8-bc93-6894a57f9324",
          "in" : "header",
          "name" : "Idempotency-Key",
          "required" : false,
          "schema" : {
            "type" : "string",
            "maxLength" : 255
          }
        } ],
        "responses" : {
          "204" : {
            "description" : "No Content"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Delete a vehicle",
        "tags" : [ "Vehicles" ],
        "x-required-scope" : "customers:write",
        "x-stability" : "preview"
      },
      "get" : {
        "operationId" : "getVehicle",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "vehicleId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Vehicle"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Get a vehicle",
        "tags" : [ "Vehicles" ],
        "x-required-scope" : "customers:read",
        "x-stability" : "preview"
      },
      "patch" : {
        "description" : "Changes only the fields in the request. Either all of them are changed or none. See `VehicleUpdate` for how to clear a field.",
        "operationId" : "updateVehicle",
        "parameters" : [ {
          "in" : "path",
          "name" : "locationId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "in" : "path",
          "name" : "vehicleId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "description" : "A value you make up for this one request, such as a UUID. Send the same value when you retry and the first answer is returned; see Retrying safely.",
          "example" : "8e03978e-40d5-43e8-bc93-6894a57f9324",
          "in" : "header",
          "name" : "Idempotency-Key",
          "required" : false,
          "schema" : {
            "type" : "string",
            "maxLength" : 255
          }
        } ],
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/VehicleUpdate"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Vehicle"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Update a vehicle",
        "tags" : [ "Vehicles" ],
        "x-required-scope" : "customers:write",
        "x-stability" : "preview"
      }
    },
    "/v1/persons/{personId}" : {
      "get" : {
        "description" : "The same person as under its location, found from its id. For a caller that holds an id and not the location it belongs to. The answer carries `locationId`.",
        "operationId" : "getPersonById",
        "parameters" : [ {
          "in" : "path",
          "name" : "personId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Person"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Get a person by id",
        "tags" : [ "Persons" ],
        "x-required-scope" : "customers:read",
        "x-stability" : "preview"
      }
    },
    "/v1/products/{productId}" : {
      "get" : {
        "description" : "The same product as under its location, found from its id. For a caller that holds an id and not the location it belongs to. The answer carries `locationId`.",
        "operationId" : "getProductById",
        "parameters" : [ {
          "in" : "path",
          "name" : "productId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Product"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Get a product by id",
        "tags" : [ "Products" ],
        "x-required-scope" : "products:read",
        "x-stability" : "preview"
      }
    },
    "/v1/resources/{resourceId}" : {
      "get" : {
        "description" : "The same resource as under its location, found from its id. For a caller that holds an id and not the location it belongs to. The answer carries `locationId`.",
        "operationId" : "getResourceById",
        "parameters" : [ {
          "in" : "path",
          "name" : "resourceId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Resource"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Get a resource by id",
        "tags" : [ "Resources" ],
        "x-required-scope" : "resources:read",
        "x-stability" : "preview"
      }
    },
    "/v1/services/{serviceId}" : {
      "get" : {
        "description" : "The same service as under its location, found from its id. For a caller that holds an id and not the location it belongs to. The answer carries `locationId`.",
        "operationId" : "getServiceById",
        "parameters" : [ {
          "in" : "path",
          "name" : "serviceId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Service"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Get a service by id",
        "tags" : [ "Services" ],
        "x-required-scope" : "services:read",
        "x-stability" : "preview"
      }
    },
    "/v1/vehicles/{vehicleId}" : {
      "get" : {
        "description" : "The same vehicle as under its location, found from its id. For a caller that holds an id and not the location it belongs to. The answer carries `locationId`.",
        "operationId" : "getVehicleById",
        "parameters" : [ {
          "in" : "path",
          "name" : "vehicleId",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Vehicle"
                }
              }
            },
            "description" : "OK"
          },
          "default" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Error"
                }
              }
            },
            "description" : "Any failure. See the Errors section."
          }
        },
        "summary" : "Get a vehicle by id",
        "tags" : [ "Vehicles" ],
        "x-required-scope" : "customers:read",
        "x-stability" : "preview"
      }
    }
  },
  "components" : {
    "schemas" : {
      "Address" : {
        "type" : "object",
        "properties" : {
          "city" : {
            "type" : [ "string", "null" ],
            "example" : "Stockholm"
          },
          "country" : {
            "type" : [ "string", "null" ],
            "description" : "Free text; an ISO 3166-1 alpha-2 code is recommended.",
            "example" : "SE"
          },
          "line1" : {
            "type" : [ "string", "null" ],
            "example" : "Storgatan 12"
          },
          "line2" : {
            "type" : [ "string", "null" ],
            "example" : "Lgh 1102"
          },
          "line3" : {
            "type" : [ "string", "null" ]
          },
          "postCode" : {
            "type" : [ "string", "null" ],
            "example" : "111 51"
          }
        }
      },
      "Booking" : {
        "type" : "object",
        "description" : "A time on one or more resources, at one location. An appointment or a class has the services that were booked and the customers it is for. A reservation only blocks the time. The services' names, durations and prices are kept as they were when the booking was made, so a later change to a service does not change the booking.",
        "properties" : {
          "afterTimeMinutes" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "Time kept free on the resource after the services, for cleaning up or resting. It is part of the time between `startsAt` and `endsAt`.",
            "example" : 15
          },
          "createdAt" : {
            "type" : "string",
            "format" : "date-time"
          },
          "customFields" : {
            "type" : "array",
            "description" : "The location's own fields on the booking, with their values.",
            "items" : {
              "$ref" : "#/components/schemas/CustomField"
            }
          },
          "customers" : {
            "type" : "array",
            "description" : "The customers the booking is for: persons, companies and vehicles. Empty for a reservation, and for a class nobody has booked yet.",
            "items" : {
              "$ref" : "#/components/schemas/BookingCustomer"
            }
          },
          "description" : {
            "type" : "string",
            "description" : "A text of its own for the booking: what a reservation is for, or what staff wrote in place of a service.",
            "example" : "Staff meeting"
          },
          "doneAt" : {
            "type" : "string",
            "format" : "date-time",
            "description" : "When staff set the booking as done. Left out until then."
          },
          "endsAt" : {
            "type" : "string",
            "format" : "date-time",
            "description" : "When the time held on the resource ends, in UTC. The last `afterTimeMinutes` of it are the time kept free after the services.",
            "example" : "2026-10-20T08:45:00Z"
          },
          "id" : {
            "type" : "string",
            "example" : "bkg_5Rt8mKq2Wn7Xb4Vd9HyLcA"
          },
          "locationId" : {
            "type" : "string",
            "example" : "loc_4fK9xQ2mLr8TzB1vNc7HdW"
          },
          "minPlaces" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "For a class: the fewest customers it runs with. Left out for other bookings.",
            "example" : 3
          },
          "note" : {
            "type" : "string",
            "description" : "Staff's note on the booking. Not shown to the customer."
          },
          "payments" : {
            "type" : "array",
            "description" : "The payments made for the booking.",
            "items" : {
              "$ref" : "#/components/schemas/BookingPayment"
            }
          },
          "places" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "For a class: how many places it has. Left out for other bookings.",
            "example" : 12
          },
          "placesBooked" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "For a class: how many places are booked. Left out for other bookings.",
            "example" : 7
          },
          "price" : {
            "type" : "string",
            "description" : "The price of the booking as a whole, when it has one. Follows \"Amounts\" in the introduction.",
            "example" : "595.00"
          },
          "priceFrom" : {
            "type" : "boolean",
            "description" : "True when the price is a starting price.",
            "example" : false
          },
          "reminders" : {
            "type" : "array",
            "description" : "The SMS reminders for the booking.",
            "items" : {
              "$ref" : "#/components/schemas/BookingReminder"
            }
          },
          "reservationType" : {
            "type" : "string",
            "description" : "For a reservation: what the time is blocked for, in lower case, such as `lunch` or `vacation`. Left out when staff gave none. The list is open.",
            "example" : "lunch"
          },
          "resources" : {
            "type" : "array",
            "description" : "The resources the booking is on. One of them is the primary resource.",
            "items" : {
              "$ref" : "#/components/schemas/BookingResource"
            }
          },
          "sales" : {
            "type" : "array",
            "description" : "The sales the booking was sold in.",
            "items" : {
              "$ref" : "#/components/schemas/BookingSale"
            }
          },
          "series" : {
            "$ref" : "#/components/schemas/BookingSeries",
            "description" : "For a booking that recurs: the series it belongs to."
          },
          "services" : {
            "type" : "array",
            "description" : "The services booked, as they were when booked. Empty for a reservation.",
            "items" : {
              "$ref" : "#/components/schemas/BookingService"
            }
          },
          "startsAt" : {
            "type" : "string",
            "format" : "date-time",
            "description" : "When the booking starts, in UTC.",
            "example" : "2026-10-20T08:00:00Z"
          },
          "status" : {
            "type" : "string",
            "description" : "`booked`; `show` and `no_show`, which staff set when the customer came or did not; `done`, which staff set when the work is finished; or `cancelled`. For a class, each customer has a status of their own. More values may be added.",
            "example" : "booked"
          },
          "type" : {
            "type" : "string",
            "description" : "`appointment`: a time for one customer, or for the customers that come together, such as a person and their vehicle. `class`: a time several customers book a place at. `reservation`: time blocked on the resource, with no customer. The list is open, so accept a value you do not know.",
            "example" : "appointment"
          },
          "updatedAt" : {
            "type" : "string",
            "format" : "date-time"
          }
        },
        "required" : [ "afterTimeMinutes", "createdAt", "customFields", "customers", "endsAt", "id", "locationId", "payments", "priceFrom", "reminders", "resources", "sales", "services", "startsAt", "status", "type", "updatedAt" ]
      },
      "BookingCustomer" : {
        "type" : "object",
        "description" : "A customer on a booking: a person, a company or a vehicle, with that customer's own status on the booking. It has the customer's name, when there is one. An API key that also has the scope `customers:read` gets the contact details and the number that identifies the customer too, so a booking can be shown without reading each customer. These are the customer's current values. The customer is changed through its own API.",
        "properties" : {
          "bookedAs" : {
            "type" : "string",
            "description" : "The name the customer was booked under, when it differs from the customer's own."
          },
          "bookedAt" : {
            "type" : "string",
            "format" : "date-time"
          },
          "cancelledAt" : {
            "type" : "string",
            "format" : "date-time"
          },
          "cancelledChannel" : {
            "type" : "string",
            "description" : "Where the customer was cancelled from, with the same values as `channel`. Left out for a customer who is not cancelled.",
            "example" : "web"
          },
          "channel" : {
            "type" : "string",
            "description" : "Where the customer was booked from: `web` for online booking, `calendar` for staff in Cliento, `api` for this API. More values may be added.",
            "example" : "web"
          },
          "customFields" : {
            "type" : "array",
            "description" : "The location's own fields for this customer on the booking.",
            "items" : {
              "$ref" : "#/components/schemas/CustomField"
            }
          },
          "customerId" : {
            "type" : "string",
            "description" : "The person, company or vehicle. The id's prefix says which, and that type's API has the rest.",
            "example" : "per_2QCtWVSaVO7yCRYBj1zsbA"
          },
          "customerNote" : {
            "type" : "string",
            "description" : "What the customer wrote when booking online. Only with the scope `customers:read`."
          },
          "email" : {
            "type" : "string",
            "description" : "Only with the scope `customers:read`.",
            "example" : "anna.lindqvist@example.com"
          },
          "identityNumber" : {
            "$ref" : "#/components/schemas/IdentityNumber",
            "description" : "For a person: the identity number, such as a personnummer. Only with the scope `customers:read`."
          },
          "name" : {
            "type" : "string",
            "description" : "The customer's name; for a vehicle, what it is called in Cliento.",
            "example" : "Anna Lindqvist"
          },
          "organisationNumber" : {
            "type" : "string",
            "description" : "For a company: the organisation number. Only with the scope `customers:read`.",
            "example" : "5560360793"
          },
          "otherPhoneNumber" : {
            "type" : "string",
            "description" : "Another phone number. Only with the scope `customers:read`.",
            "example" : "+4686543210"
          },
          "phoneNumber" : {
            "type" : "string",
            "description" : "Mobile number. Only with the scope `customers:read`.",
            "example" : "+46701234567"
          },
          "price" : {
            "type" : "string",
            "description" : "This customer's price, where each customer has one, as on a class.",
            "example" : "195.00"
          },
          "priceFrom" : {
            "type" : "boolean",
            "description" : "True when the price is a starting price.",
            "example" : false
          },
          "registrationNumber" : {
            "type" : "string",
            "description" : "For a vehicle: the registration number. Only with the scope `customers:read`.",
            "example" : "ABC123"
          },
          "status" : {
            "type" : "string",
            "description" : "This customer's status on the booking, with the same values as the booking's `status`. It differs from the booking's on a class, where one customer can cancel and the others stay booked.",
            "example" : "booked"
          },
          "type" : {
            "type" : "string",
            "description" : "`person`, `company` or `vehicle`. More values may be added.",
            "example" : "person"
          }
        },
        "required" : [ "bookedAt", "channel", "customFields", "customerId", "priceFrom", "status", "type" ]
      },
      "BookingList" : {
        "type" : "object",
        "properties" : {
          "data" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/Booking"
            }
          },
          "nextCursor" : {
            "type" : "string",
            "description" : "Pass as `after` to fetch the next page. Absent on the last page.",
            "example" : "1792483200000.bkg_5Rt8mKq2Wn7Xb4Vd9HyLcA"
          }
        },
        "required" : [ "data" ]
      },
      "BookingPayment" : {
        "type" : "object",
        "description" : "A payment for a booking: made online when booking, in advance, or at the point of sale. Belongs to the booking, and to one of its customers when it was made for one.",
        "properties" : {
          "amount" : {
            "type" : "string",
            "example" : "595.00"
          },
          "customerId" : {
            "type" : "string",
            "description" : "The customer the payment was made for, when it was made for one.",
            "example" : "per_2QCtWVSaVO7yCRYBj1zsbA"
          },
          "paidOn" : {
            "type" : "string",
            "format" : "date",
            "description" : "The day of the payment, in the location's time zone.",
            "example" : "2026-10-18"
          },
          "provider" : {
            "type" : "string",
            "description" : "Who handled the payment, in lower case, such as `stripe` or `klarna`. The list is open, so accept a value you do not know.",
            "example" : "stripe"
          },
          "reference" : {
            "type" : "string",
            "description" : "The payment's reference, as shown to the customer."
          },
          "refundedAmount" : {
            "type" : "string",
            "example" : "0.00"
          },
          "refundedOn" : {
            "type" : "string",
            "format" : "date",
            "description" : "The day of the refund, when there is one."
          },
          "status" : {
            "type" : "string",
            "description" : "`paid`, `unpaid`, or a refund state such as `refunded` or `refunded_partially`. More values may be added.",
            "example" : "paid"
          },
          "type" : {
            "type" : "string",
            "description" : "`online` for a payment made when booking online, `pre_paid` for one made in advance, `pos` for one made at the point of sale. More values may be added.",
            "example" : "online"
          }
        },
        "required" : [ "amount", "provider", "refundedAmount", "status", "type" ]
      },
      "BookingReminder" : {
        "type" : "object",
        "description" : "An SMS reminder sent, or to be sent, to a customer about a booking.",
        "properties" : {
          "customerId" : {
            "type" : "string",
            "example" : "per_2QCtWVSaVO7yCRYBj1zsbA"
          },
          "status" : {
            "type" : "string",
            "description" : "How far the message has come, in lower case, such as `pending`, `sent` or `delivered`. The list is open, so accept a value you do not know.",
            "example" : "delivered"
          },
          "statusAt" : {
            "type" : "string",
            "format" : "date-time",
            "description" : "When it got that status."
          },
          "type" : {
            "type" : "string",
            "description" : "`pre_reminder` for one sent before the booking; `post_reminder1` and `post_reminder2` for those sent after it. More values may be added.",
            "example" : "pre_reminder"
          }
        },
        "required" : [ "customerId", "status", "statusAt", "type" ]
      },
      "BookingResource" : {
        "type" : "object",
        "description" : "A resource a booking is on.",
        "properties" : {
          "primary" : {
            "type" : "boolean",
            "description" : "True for the booking's primary resource: the one it was booked with. A booking has exactly one. The others are held for the same time, such as a room.",
            "example" : true
          },
          "resourceId" : {
            "type" : "string",
            "example" : "res_4Hn7pQw2Zt9Lk3Vd6XyRbA"
          }
        },
        "required" : [ "primary", "resourceId" ]
      },
      "BookingSale" : {
        "type" : "object",
        "description" : "A sale that a booking was sold in: a sale at the point of sale, an order or an invoice. The sale belongs to a merchant and is read in full from the Sales API, which is coming soon; this is the part a booking refers to.",
        "properties" : {
          "createdAt" : {
            "type" : "string",
            "format" : "date-time",
            "description" : "When the sale was created."
          },
          "customerId" : {
            "type" : "string",
            "description" : "The customer the sale was made to, when it names one.",
            "example" : "per_2QCtWVSaVO7yCRYBj1zsbA"
          },
          "invoiceStatus" : {
            "type" : "string",
            "description" : "For a sale with an invoice: whether the invoice is paid, such as `unpaid` or `paid`. The list is open.",
            "example" : "unpaid"
          },
          "paidAmount" : {
            "type" : "string",
            "description" : "What has been paid in the sale as a whole. A sale can cover more than this booking.",
            "example" : "595.00"
          },
          "saleId" : {
            "type" : "string",
            "example" : "sal_8Hd3kWq5Zn2Tb7Xc4LmPyR"
          },
          "status" : {
            "type" : "string",
            "description" : "The sale's state, in lower case, such as `open`, `paid` or `void`. The list is open, so accept a value you do not know.",
            "example" : "paid"
          },
          "type" : {
            "type" : "string",
            "description" : "`pos_sale`, `order` or `invoice`. More values may be added.",
            "example" : "pos_sale"
          }
        },
        "required" : [ "paidAmount", "saleId", "status", "type" ]
      },
      "BookingSeries" : {
        "type" : "object",
        "description" : "A series of bookings that recur. Each booking in it is a booking of its own and has the series' id. The series has the rule the bookings were made from.",
        "properties" : {
          "id" : {
            "type" : "string",
            "example" : "bks_6Wq2nYd8Rk4Tc9Xb3HmLzP"
          },
          "recurrenceRule" : {
            "type" : "string",
            "description" : "The recurrence rule, as an iCalendar RRULE.",
            "example" : "FREQ=WEEKLY;COUNT=10"
          }
        },
        "required" : [ "id", "recurrenceRule" ]
      },
      "BookingService" : {
        "type" : "object",
        "description" : "A service on a booking, with its name, duration and price as they were when it was booked.",
        "properties" : {
          "durationMinutes" : {
            "type" : "integer",
            "format" : "int32",
            "example" : 45
          },
          "name" : {
            "type" : "string",
            "example" : "Haircut"
          },
          "price" : {
            "type" : "string",
            "example" : "595.00"
          },
          "priceFrom" : {
            "type" : "boolean",
            "description" : "True when the price is a starting price.",
            "example" : false
          },
          "serviceId" : {
            "type" : "string",
            "example" : "svc_3Gk8rTn5Ym1Wc7Zp2LdQxB"
          }
        },
        "required" : [ "durationMinutes", "name", "priceFrom", "serviceId" ]
      },
      "ColourBarBarcode" : {
        "type" : "object",
        "properties" : {
          "ean" : {
            "type" : "string",
            "description" : "The barcode, at most 50 characters.",
            "example" : "4064666230412"
          }
        },
        "required" : [ "ean" ]
      },
      "ColourBarItem" : {
        "type" : "object",
        "description" : "Represents one thing a location mixes from at its colour bar: one shade of a colour, one strength of a developer, one tub of bleach. Belongs to a colour bar range and to the range's location. Holds its own balance in the location's stock, counted in packs, and how much has been taken from the pack that is open. It is not a product: it is never sold at a point of sale.",
        "properties" : {
          "articleNumber" : {
            "type" : "string",
            "description" : "The supplier's article number, as read from a delivery note.",
            "example" : "99350118254"
          },
          "catalogueEans" : {
            "type" : "array",
            "description" : "Read-only. Those of `eans` that the item has from the article in a supplier's catalogue it was made from. They are not the item's own and cannot be removed from it. Can be empty.",
            "example" : [ ],
            "items" : {
              "type" : "string"
            }
          },
          "createdAt" : {
            "type" : "string",
            "format" : "date-time",
            "example" : "2026-10-12T08:15:30Z"
          },
          "eans" : {
            "type" : "array",
            "description" : "The item's barcodes: its own, and those in `catalogueEans`. Can be empty.",
            "example" : [ "4064666230412" ],
            "items" : {
              "type" : "string"
            }
          },
          "gramsLeft" : {
            "type" : "integer",
            "format" : "int64",
            "description" : "Read-only. Grams left in all the item's packs together. Left out when the item has no pack size.",
            "example" : 203
          },
          "id" : {
            "type" : "string",
            "description" : "Colour bar item id.",
            "example" : "cbi_3Vn8Kq5TzL1xWc7PdM2RbJ"
          },
          "listPrice" : {
            "type" : "string",
            "description" : "The list price of one pack, VAT excluded, where the item differs from its range. Left out when the item follows the range's `listPrice`.",
            "example" : "137.00"
          },
          "listPriceInUse" : {
            "type" : "string",
            "description" : "Read-only. The list price that applies: the item's own, otherwise its range's.",
            "example" : "137.00"
          },
          "locationId" : {
            "type" : "string",
            "example" : "loc_4fK9xQ2mLr8TzB1vNc7HdW"
          },
          "name" : {
            "type" : "string",
            "description" : "What the item is called. An item without a name of its own is called by its `shade`, and follows it when the shade is changed.",
            "example" : "7/10"
          },
          "openLeft" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "Read-only. Grams left in the open pack. Left out when no pack is open or the item has no pack size.",
            "example" : 23
          },
          "openUsed" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "Read-only. Grams taken from the open pack. `0` when no pack is open.",
            "example" : 37
          },
          "packSize" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "Grams in one pack, where the item differs from its range. Left out when the item follows the range's `packSize`.",
            "example" : 60
          },
          "packSizeInUse" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "Read-only. The pack size that applies: the item's own, otherwise its range's. Left out for an item used a whole pack at a time.",
            "example" : 60
          },
          "packs" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "Read-only. The packs in stock, the open one included. Never negative.",
            "example" : 4
          },
          "rangeId" : {
            "type" : "string",
            "description" : "The range the item belongs to. Set when the item is created.",
            "example" : "cbr_7Hc2Lm9QxT4vBn6KdR1WsP"
          },
          "shade" : {
            "type" : "string",
            "description" : "The shade of a colour, as the supplier writes it.",
            "example" : "7/10"
          },
          "strengthPercent" : {
            "type" : "string",
            "description" : "The strength of a developer in per cent, as a decimal string.",
            "example" : "6"
          },
          "unopenedPacks" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "Read-only. The packs that are not opened.",
            "example" : 3
          },
          "updatedAt" : {
            "type" : "string",
            "format" : "date-time",
            "description" : "When the item last changed, which includes its stock and its own barcodes.",
            "example" : "2026-10-12T08:15:30Z"
          }
        },
        "required" : [ "catalogueEans", "createdAt", "eans", "id", "locationId", "name", "openUsed", "packs", "rangeId", "unopenedPacks", "updatedAt" ]
      },
      "ColourBarItemCreate" : {
        "type" : "object",
        "properties" : {
          "articleNumber" : {
            "type" : "string",
            "example" : "99350118254"
          },
          "eans" : {
            "type" : "array",
            "description" : "The item's own barcodes. A barcode that another item or a product of the location has is refused.",
            "example" : [ "4064666230412" ],
            "items" : {
              "type" : "string"
            }
          },
          "listPrice" : {
            "type" : "string",
            "description" : "The list price of one pack, VAT excluded, when the item differs from its range.",
            "example" : "149.00"
          },
          "name" : {
            "type" : "string",
            "description" : "What the item is called. Required when there is no `shade`.",
            "example" : "Blondor Multi Blonde"
          },
          "packSize" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "Grams in one pack, when the item differs from its range.",
            "example" : 60
          },
          "rangeId" : {
            "type" : "string",
            "description" : "The range the item belongs to.",
            "example" : "cbr_7Hc2Lm9QxT4vBn6KdR1WsP"
          },
          "shade" : {
            "type" : "string",
            "description" : "The shade of a colour, at most 50 characters. A range holds a shade once.",
            "example" : "7/10"
          },
          "strengthPercent" : {
            "type" : "string",
            "description" : "The strength of a developer in per cent, greater than 0, at most 100.",
            "example" : "6"
          }
        },
        "required" : [ "rangeId" ]
      },
      "ColourBarItemList" : {
        "type" : "object",
        "properties" : {
          "data" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/ColourBarItem"
            }
          },
          "nextCursor" : {
            "type" : "string",
            "description" : "Pass as `after` to fetch the next page. Absent on the last page.",
            "example" : "cbi_3Vn8Kq5TzL1xWc7PdM2RbJ"
          }
        },
        "required" : [ "data" ]
      },
      "ColourBarItemUpdate" : {
        "type" : "object",
        "description" : "A JSON merge patch: send only what changes. A field you leave out keeps its value; a field set to `null` is cleared, but an item must keep a `name` or a `shade`. A `name` equal to the `shade` is not a name of its own: the item is called by its shade and follows it. Clearing `packSize` or `listPrice` makes the item follow its range again.",
        "properties" : {
          "articleNumber" : {
            "type" : "string",
            "example" : "99350118254"
          },
          "listPrice" : {
            "type" : "string",
            "example" : "149.00"
          },
          "name" : {
            "type" : "string",
            "example" : "Blondor Multi Blonde"
          },
          "packSize" : {
            "type" : "integer",
            "format" : "int32",
            "example" : 60
          },
          "shade" : {
            "type" : "string",
            "example" : "7/10"
          },
          "strengthPercent" : {
            "type" : "string",
            "example" : "6"
          }
        }
      },
      "ColourBarMix" : {
        "type" : "object",
        "description" : "Represents what one staff member mixed at a location's colour bar at one time: one or more bowls, each with the colour bar items it took from and how much of each. Belongs to a location. Registering it changes the items it names: what was taken is added to an item's `openUsed`, and a pack is taken from stock when one is finished. A mix is not changed afterwards; it can be undone.",
        "properties" : {
          "bowls" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/ColourBarMixBowl"
            }
          },
          "createdAt" : {
            "type" : "string",
            "format" : "date-time",
            "example" : "2026-10-12T08:15:30Z"
          },
          "externalReference" : {
            "type" : "string",
            "description" : "Your own id for the mix.",
            "example" : "mix-2026-10-12-0042"
          },
          "id" : {
            "type" : "string",
            "description" : "Colour bar mix id.",
            "example" : "cbm_8Wq3Nd6HtY1mKx4LcP9RvB"
          },
          "listValue" : {
            "type" : "string",
            "description" : "What the mix took, at the list prices then, VAT excluded: its components' `listValue` together, rounded to two decimals, as a decimal string in the location's currency.",
            "example" : "91.33"
          },
          "locationId" : {
            "type" : "string",
            "example" : "loc_4fK9xQ2mLr8TzB1vNc7HdW"
          },
          "note" : {
            "type" : "string",
            "example" : "35 min"
          },
          "recipeId" : {
            "type" : "string",
            "description" : "The recipe the mix was saved as on a person, when it was.",
            "example" : "rcp_6Jn2Xw8QdT5bVh3ZmK7LsF"
          },
          "registeredBy" : {
            "type" : "string",
            "description" : "The name of who mixed: the staff member, or the API key when none was named.",
            "example" : "Anna"
          },
          "staffId" : {
            "type" : "string",
            "description" : "The staff member who mixed. Left out when none was named.",
            "example" : "stf_2Hs7Bq4MnX9cLk1VtR6PzD"
          },
          "title" : {
            "type" : "string",
            "example" : "Roots and lengths"
          },
          "undoneAt" : {
            "type" : "string",
            "format" : "date-time",
            "description" : "When the mix was undone. Left out for a mix that stands.",
            "example" : "2026-10-12T09:02:11Z"
          },
          "undoneBy" : {
            "type" : "string",
            "description" : "The name of who undid the mix.",
            "example" : "Anna"
          },
          "updatedAt" : {
            "type" : "string",
            "format" : "date-time",
            "description" : "When the mix last changed: when it was undone, otherwise `createdAt`.",
            "example" : "2026-10-12T08:15:30Z"
          }
        },
        "required" : [ "bowls", "createdAt", "id", "listValue", "locationId", "updatedAt" ]
      },
      "ColourBarMixBowl" : {
        "type" : "object",
        "description" : "One bowl of a mix.",
        "properties" : {
          "components" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/ColourBarMixComponent"
            }
          },
          "ratio" : {
            "type" : "string",
            "description" : "Colour to developer, as it was sent. Kept as it is.",
            "example" : "1:1"
          },
          "title" : {
            "type" : "string",
            "example" : "Roots"
          }
        },
        "required" : [ "components" ]
      },
      "ColourBarMixBowlCreate" : {
        "type" : "object",
        "properties" : {
          "components" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/ColourBarMixComponentCreate"
            }
          },
          "ratio" : {
            "type" : "string",
            "description" : "Colour to developer, such as `1:1` or `1:1.5`. Stored as it is.",
            "example" : "1:1"
          },
          "title" : {
            "type" : "string",
            "example" : "Roots"
          }
        },
        "required" : [ "components" ]
      },
      "ColourBarMixComponent" : {
        "type" : "object",
        "description" : "What a mix took from one colour bar item, and how that left the item.",
        "properties" : {
          "amount" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "How much was taken: grams, or whole packs of an item that has no pack size.",
            "example" : 30
          },
          "colourBarItemId" : {
            "type" : "string",
            "example" : "cbi_3Vn8Kq5TzL1xWc7PdM2RbJ"
          },
          "fromFinishedPack" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "The grams of `amount` that came out of the finished pack before it ran out, when a new pack was opened midway.",
            "example" : 12
          },
          "listValue" : {
            "type" : "string",
            "description" : "What `amount` was worth at the list price then, VAT excluded, as a decimal string with up to four decimals. Left out when the item had no list price.",
            "example" : "68.5000"
          },
          "name" : {
            "type" : "string",
            "description" : "What the item was called when the mix was registered.",
            "example" : "7/10"
          },
          "openUsed" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "The item's `openUsed` after this component.",
            "example" : 30
          },
          "openedNewPack" : {
            "type" : "boolean",
            "description" : "Whether a new pack was opened for this component.",
            "example" : false
          },
          "packs" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "The item's `packs` after this component.",
            "example" : 4
          },
          "packsFinished" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "The packs this component finished, which is what it took from stock.",
            "example" : 0
          },
          "residue" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "Set only when `openedNewPack` is `true`: the pack size less the grams that had been taken from the finished pack.",
            "example" : 3
          },
          "shade" : {
            "type" : "string",
            "description" : "The item's shade when the mix was registered.",
            "example" : "7/10"
          }
        },
        "required" : [ "amount", "colourBarItemId", "name", "openUsed", "openedNewPack", "packs", "packsFinished" ]
      },
      "ColourBarMixComponentCreate" : {
        "type" : "object",
        "properties" : {
          "amount" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "How much was taken, as a whole number greater than 0: grams as weighed, or whole packs of an item that has no pack size.",
            "example" : 30
          },
          "colourBarItemId" : {
            "type" : "string",
            "example" : "cbi_3Vn8Kq5TzL1xWc7PdM2RbJ"
          },
          "fromFinishedPack" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "With `openedNewPack`: the grams of `amount` that still came out of the finished pack before it ran out. Leave out when the whole amount came from the new pack. Refused with `validation_failed` for an item that has no pack size.",
            "example" : 12
          },
          "openedNewPack" : {
            "type" : "boolean",
            "description" : "`true` when a new pack was opened for this component: the pack that was open before is then finished and leaves the stock.",
            "example" : false
          }
        },
        "required" : [ "amount", "colourBarItemId" ]
      },
      "ColourBarMixCreate" : {
        "type" : "object",
        "properties" : {
          "bowls" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/ColourBarMixBowlCreate"
            }
          },
          "externalReference" : {
            "type" : "string",
            "description" : "Your own id for the mix, at most 100 characters. A mix the location already has under it is returned unchanged.",
            "example" : "mix-2026-10-12-0042"
          },
          "note" : {
            "type" : "string",
            "example" : "35 min"
          },
          "personId" : {
            "type" : "string",
            "description" : "A person to save the mix on as a recipe, in the same call. Needs the scope `customers:write` as well.",
            "example" : "per_9Tk4Yc1WvN7pQe2XhB5MdG"
          },
          "staffId" : {
            "type" : "string",
            "description" : "The staff member who mixed. Read it from the Staff API.",
            "example" : "stf_2Hs7Bq4MnX9cLk1VtR6PzD"
          },
          "title" : {
            "type" : "string",
            "example" : "Roots and lengths"
          }
        },
        "required" : [ "bowls" ]
      },
      "ColourBarMixList" : {
        "type" : "object",
        "properties" : {
          "data" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/ColourBarMix"
            }
          },
          "nextCursor" : {
            "type" : "string",
            "description" : "Pass as `after` to fetch the next page. Absent on the last page.",
            "example" : "cbm_8Wq3Nd6HtY1mKx4LcP9RvB"
          }
        },
        "required" : [ "data" ]
      },
      "ColourBarMixStaffTotal" : {
        "type" : "object",
        "description" : "What one staff member mixed over the period, mixes that were undone left out.",
        "properties" : {
          "listValue" : {
            "type" : "string",
            "description" : "Their `listValue` together, as a decimal string.",
            "example" : "3480.25"
          },
          "mixes" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "How many mixes.",
            "example" : 42
          },
          "name" : {
            "type" : "string",
            "example" : "Anna"
          },
          "staffId" : {
            "type" : "string",
            "description" : "The staff member. Left out for mixes registered with no staff member.",
            "example" : "stf_2Hs7Bq4MnX9cLk1VtR6PzD"
          }
        },
        "required" : [ "listValue", "mixes" ]
      },
      "ColourBarMixSummary" : {
        "type" : "object",
        "description" : "What was mixed at a location from one day to another, per staff member, at list price.",
        "properties" : {
          "from" : {
            "type" : "string",
            "format" : "date",
            "example" : "2026-10-01"
          },
          "listValue" : {
            "type" : "string",
            "description" : "The `listValue` of all `staff` together, as a decimal string.",
            "example" : "12940.75"
          },
          "staff" : {
            "type" : "array",
            "description" : "One total per staff member who mixed.",
            "items" : {
              "$ref" : "#/components/schemas/ColourBarMixStaffTotal"
            }
          },
          "to" : {
            "type" : "string",
            "format" : "date",
            "example" : "2026-10-31"
          }
        },
        "required" : [ "from", "listValue", "staff", "to" ]
      },
      "ColourBarMixUndo" : {
        "type" : "object",
        "properties" : {
          "staffId" : {
            "type" : "string",
            "description" : "The staff member who undoes the mix. Read it from the Staff API.",
            "example" : "stf_2Hs7Bq4MnX9cLk1VtR6PzD"
          }
        }
      },
      "ColourBarRange" : {
        "type" : "object",
        "description" : "Represents a supplier's series as a location keeps it at its colour bar, such as one brand's permanent colours. Belongs to a location. Holds what its items have in common: their kind, the size of a pack and its list price. Colour bar items belong to it.",
        "properties" : {
          "brand" : {
            "type" : "string",
            "example" : "Wella"
          },
          "createdAt" : {
            "type" : "string",
            "format" : "date-time",
            "example" : "2026-10-12T08:15:30Z"
          },
          "id" : {
            "type" : "string",
            "description" : "Colour bar range id.",
            "example" : "cbr_7Hc2Lm9QxT4vBn6KdR1WsP"
          },
          "kind" : {
            "type" : "string",
            "description" : "What the range's items are used as, such as `colour` or `developer`. A lower-case word; the list is open.",
            "example" : "colour"
          },
          "listPrice" : {
            "type" : "string",
            "description" : "The supplier's list price of one pack, VAT excluded, as a decimal string in the location's currency.",
            "example" : "137.00"
          },
          "locationId" : {
            "type" : "string",
            "example" : "loc_4fK9xQ2mLr8TzB1vNc7HdW"
          },
          "name" : {
            "type" : "string",
            "description" : "What the range is called at the location.",
            "example" : "Koleston Perfect ME+"
          },
          "packSize" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "Grams in one pack of the range's items. Left out for a range whose items are used a whole pack at a time.",
            "example" : 60
          },
          "series" : {
            "type" : "string",
            "description" : "The supplier's name of the series.",
            "example" : "KP ME+"
          },
          "updatedAt" : {
            "type" : "string",
            "format" : "date-time",
            "description" : "When the range itself was last changed.",
            "example" : "2026-10-12T08:15:30Z"
          }
        },
        "required" : [ "createdAt", "id", "kind", "locationId", "name", "updatedAt" ]
      },
      "ColourBarRangeCreate" : {
        "type" : "object",
        "properties" : {
          "brand" : {
            "type" : "string",
            "example" : "Wella"
          },
          "kind" : {
            "type" : "string",
            "description" : "What the range's items are used as, such as `colour` or `developer`: a lower-case word of at most 30 characters.",
            "example" : "colour"
          },
          "listPrice" : {
            "type" : "string",
            "description" : "The supplier's list price of one pack, VAT excluded.",
            "example" : "137.00"
          },
          "name" : {
            "type" : "string",
            "description" : "What the range is called at the location, at most 255 characters.",
            "example" : "Koleston Perfect ME+"
          },
          "packSize" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "Grams in one pack. Leave out for a range whose items are used a whole pack at a time.",
            "example" : 60
          },
          "series" : {
            "type" : "string",
            "description" : "The supplier's name of the series.",
            "example" : "KP ME+"
          }
        },
        "required" : [ "kind", "name" ]
      },
      "ColourBarRangeList" : {
        "type" : "object",
        "properties" : {
          "data" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/ColourBarRange"
            }
          },
          "nextCursor" : {
            "type" : "string",
            "description" : "Pass as `after` to fetch the next page. Absent on the last page.",
            "example" : "cbr_7Hc2Lm9QxT4vBn6KdR1WsP"
          }
        },
        "required" : [ "data" ]
      },
      "ColourBarRangeUpdate" : {
        "type" : "object",
        "description" : "A JSON merge patch: send only what changes. A field you leave out keeps its value; a field set to `null` is cleared. `name` and `kind` cannot be cleared.",
        "properties" : {
          "brand" : {
            "type" : "string",
            "example" : "Wella"
          },
          "kind" : {
            "type" : "string",
            "example" : "colour"
          },
          "listPrice" : {
            "type" : "string",
            "example" : "137.00"
          },
          "name" : {
            "type" : "string",
            "example" : "Koleston Perfect ME+"
          },
          "packSize" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "Grams in one pack. Changing it changes the pack size of every item of the range that has none of its own.",
            "example" : 60
          },
          "series" : {
            "type" : "string",
            "example" : "KP ME+"
          }
        }
      },
      "Communication" : {
        "type" : "object",
        "description" : "What the person has agreed to receive. In an update, a field you leave out keeps its value. For a new person `smsReminders`, `emailReminders` and `reviewReminders` start as true and `marketing` as false: send `marketing` as true only when the person has agreed to marketing.",
        "properties" : {
          "emailReminders" : {
            "type" : "boolean",
            "description" : "Booking reminders by email."
          },
          "marketing" : {
            "type" : "boolean",
            "description" : "Marketing messages."
          },
          "reviewReminders" : {
            "type" : "boolean",
            "description" : "Requests to review a visit."
          },
          "smsReminders" : {
            "type" : "boolean",
            "description" : "Booking reminders by SMS."
          }
        }
      },
      "Company" : {
        "type" : "object",
        "description" : "Represents a business a location serves. Belongs to one location.",
        "properties" : {
          "address" : {
            "$ref" : "#/components/schemas/Address"
          },
          "createdAt" : {
            "type" : "string",
            "format" : "date-time"
          },
          "department" : {
            "type" : "string",
            "description" : "A part of the company, when the location serves several of them.",
            "example" : "Used cars"
          },
          "email" : {
            "type" : "string",
            "example" : "info@example.com"
          },
          "externalReference" : {
            "type" : "string",
            "description" : "Your own id for it, as sent when it was created.",
            "example" : "crm-88121"
          },
          "id" : {
            "type" : "string",
            "example" : "com_6Tn3wRd8Kq1Vb5Xs9LmPyA"
          },
          "locationId" : {
            "type" : "string",
            "example" : "loc_4fK9xQ2mLr8TzB1vNc7HdW"
          },
          "name" : {
            "type" : "string",
            "example" : "Bilfirman AB"
          },
          "notes" : {
            "type" : "string",
            "description" : "Staff-facing notes, at most 1000 characters."
          },
          "organisationNumber" : {
            "type" : "string",
            "description" : "The Swedish organisation number: ten digits with a valid check digit, with no hyphen.",
            "example" : "5560360793"
          },
          "otherPhoneNumber" : {
            "type" : "string",
            "example" : "+4686543210"
          },
          "phoneNumber" : {
            "type" : "string",
            "description" : "Phone number, in E.164 format when it was set through this API.",
            "example" : "+46701234567"
          },
          "updatedAt" : {
            "type" : "string",
            "format" : "date-time"
          }
        },
        "required" : [ "id", "locationId" ]
      },
      "CompanyCreate" : {
        "type" : "object",
        "properties" : {
          "address" : {
            "$ref" : "#/components/schemas/Address"
          },
          "department" : {
            "type" : "string",
            "description" : "A part of the company, when the location serves several of them.",
            "example" : "Used cars"
          },
          "email" : {
            "type" : "string",
            "example" : "info@example.com"
          },
          "externalReference" : {
            "type" : "string",
            "description" : "Your own id for it, at most 100 characters, such as its id in your system. It makes the create safe to repeat and lets you find it again. Compared exactly, letter case included.",
            "example" : "crm-88121"
          },
          "name" : {
            "type" : "string",
            "example" : "Bilfirman AB"
          },
          "notes" : {
            "type" : "string",
            "description" : "Staff-facing notes, at most 1000 characters."
          },
          "organisationNumber" : {
            "type" : "string",
            "description" : "The Swedish organisation number: ten digits with a valid check digit, with no hyphen.",
            "example" : "5560360793"
          },
          "otherPhoneNumber" : {
            "type" : "string",
            "example" : "+4686543210"
          },
          "phoneNumber" : {
            "type" : "string",
            "description" : "Phone number in E.164 format: a plus sign, the country code and the number, at most 15 digits.",
            "example" : "+46701234567"
          }
        },
        "required" : [ "name" ]
      },
      "CompanyList" : {
        "type" : "object",
        "properties" : {
          "data" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/Company"
            }
          },
          "nextCursor" : {
            "type" : "string",
            "description" : "Pass as `after` to fetch the next page. Absent on the last page.",
            "example" : "com_6Tn3wRd8Kq1Vb5Xs9LmPyA"
          }
        },
        "required" : [ "data" ]
      },
      "CompanyUpdate" : {
        "type" : "object",
        "description" : "A JSON merge patch: send only what changes. A field you leave out keeps its value; a field set to `null` is cleared, except `name`, which cannot be cleared. `address` merges field by field.",
        "properties" : {
          "address" : {
            "anyOf" : [ {
              "$ref" : "#/components/schemas/Address"
            }, {
              "type" : "null"
            } ]
          },
          "department" : {
            "type" : [ "string", "null" ],
            "description" : "A part of the company, when the location serves several of them.",
            "example" : "Used cars"
          },
          "email" : {
            "type" : [ "string", "null" ],
            "example" : "info@example.com"
          },
          "name" : {
            "type" : "string",
            "example" : "Bilfirman AB"
          },
          "notes" : {
            "type" : [ "string", "null" ],
            "description" : "Staff-facing notes, at most 1000 characters."
          },
          "organisationNumber" : {
            "type" : [ "string", "null" ],
            "description" : "The Swedish organisation number: ten digits with a valid check digit, with no hyphen.",
            "example" : "5560360793"
          },
          "otherPhoneNumber" : {
            "type" : [ "string", "null" ],
            "example" : "+4686543210"
          },
          "phoneNumber" : {
            "type" : [ "string", "null" ],
            "description" : "Phone number in E.164 format: a plus sign, the country code and the number, at most 15 digits.",
            "example" : "+46701234567"
          }
        }
      },
      "CustomField" : {
        "type" : "object",
        "description" : "A field a location has added of its own, with the value given for one booking or one customer on it.",
        "properties" : {
          "key" : {
            "type" : "string",
            "example" : "allergies"
          },
          "label" : {
            "type" : "string",
            "example" : "Allergies"
          },
          "type" : {
            "type" : "string",
            "description" : "The kind of field, in lower case, such as `text` or `checkbox`. The list is open.",
            "example" : "text"
          },
          "value" : {
            "description" : "The value, of the JSON type the field holds. Left out when not given.",
            "example" : "None"
          }
        },
        "required" : [ "key" ]
      },
      "Customer" : {
        "type" : "object",
        "description" : "The short entry for a person, a company or a vehicle. The id's prefix says which type, and that type's API has the rest.",
        "properties" : {
          "id" : {
            "type" : "string",
            "example" : "per_2QCtWVSaVO7yCRYBj1zsbA"
          },
          "locationId" : {
            "type" : "string",
            "example" : "loc_4fK9xQ2mLr8TzB1vNc7HdW"
          },
          "name" : {
            "type" : "string",
            "example" : "Anna Lindqvist"
          },
          "number" : {
            "type" : "string",
            "description" : "The number that identifies it, when it has one: a person's identity number, a company's organisation number or a vehicle's registration number.",
            "example" : "198604122385"
          },
          "type" : {
            "type" : "string",
            "description" : "`person`, `company` or `vehicle`. It also says where to read the rest: Persons, Companies or Vehicles. More values may be added.",
            "example" : "person"
          }
        },
        "required" : [ "id", "locationId", "type" ]
      },
      "CustomerList" : {
        "type" : "object",
        "properties" : {
          "data" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/Customer"
            }
          },
          "nextCursor" : {
            "type" : "string",
            "description" : "Pass as `after` to fetch the next page. Absent on the last page.",
            "example" : "per_2QCtWVSaVO7yCRYBj1zsbA"
          }
        },
        "required" : [ "data" ]
      },
      "CustomerOwner" : {
        "type" : "object",
        "description" : "Whose customer a person is within its location: the location's own, or that of one of the location's resources. A salon that rents out chairs uses it: a person served by a hairdresser who rents a chair is that resource's customer, and a person served by an employee is the location's. Many locations never record it, so `unassigned` is common and says nothing either way.",
        "properties" : {
          "resourceId" : {
            "type" : "string",
            "description" : "The resource (see Resources) whose customer the person is. Present when `type` is `resource`.",
            "example" : "res_2QCtWVSaVO7yCRYBj1zsbA"
          },
          "type" : {
            "type" : "string",
            "description" : "`location`: the location's own customer. `resource`: the customer of one resource, named by `resourceId`. `unassigned`: not recorded. More values may be added.",
            "enum" : [ "location", "resource", "unassigned" ]
          }
        },
        "required" : [ "type" ]
      },
      "Error" : {
        "type" : "object",
        "description" : "The body of every non-2xx response.",
        "properties" : {
          "code" : {
            "type" : "string",
            "description" : "Stable, machine-readable error code.",
            "enum" : [ "invalid_request", "validation_failed", "entity_not_found", "duplicate", "merchant_required", "code_already_used", "gift_card_already_used", "pin_not_recognised", "insufficient_stock", "not_allowed", "request_in_progress", "idempotency_key_reused", "unauthenticated", "forbidden", "account_blocked", "rate_limited", "route_not_found", "method_not_allowed", "internal_error" ]
          },
          "details" : {
            "type" : "object",
            "additionalProperties" : { },
            "description" : "Extra detail. For `validation_failed`, `fieldErrors` maps each rejected field to the reason."
          },
          "message" : {
            "type" : "string",
            "description" : "Human-readable explanation. Not stable; do not parse it."
          }
        },
        "required" : [ "code", "message" ]
      },
      "GiftCard" : {
        "type" : "object",
        "description" : "A gift card. It belongs to one merchant and is redeemed at that merchant's point of sale, or at another merchant's when the location allows it. Amounts are decimal strings in the location's currency.",
        "properties" : {
          "accountingType" : {
            "type" : "string",
            "description" : "How VAT is handled. `single_purpose`: VAT is charged when the card is sold. `multi_purpose`: VAT is charged when the card is redeemed.",
            "example" : "single_purpose"
          },
          "beneficiary" : {
            "type" : "string",
            "description" : "Who the card is for, as free text.",
            "example" : "Anna Andersson"
          },
          "code" : {
            "type" : "string",
            "description" : "What the holder shows or types when paying.",
            "example" : "K7QX9M2D"
          },
          "createdAt" : {
            "type" : "string",
            "format" : "date-time",
            "example" : "2026-11-17T09:14:03Z"
          },
          "currency" : {
            "type" : "string",
            "description" : "ISO 4217 currency code.",
            "example" : "SEK"
          },
          "customerId" : {
            "type" : "string",
            "description" : "The person, company or vehicle the card is linked to, when it is linked to one. The id's prefix says which.",
            "example" : "per_2QCtWVSaVO7yCRYBj1zsbA"
          },
          "description" : {
            "type" : "string",
            "example" : "Presentkort"
          },
          "externalReference" : {
            "type" : "string",
            "description" : "Your own id for the card, as sent when it was imported.",
            "example" : "shopify-5512340001-1"
          },
          "id" : {
            "type" : "string",
            "description" : "Gift card id.",
            "example" : "gft_7Qm2xN0cT1kPz4Wb9LrEaD"
          },
          "locationId" : {
            "type" : "string",
            "example" : "loc_4fK9xQ2mLr8TzB1vNc7HdW"
          },
          "merchantId" : {
            "type" : "string",
            "description" : "The merchant the card belongs to.",
            "example" : "mer_2Rk8sVh3Yq6Nf1Tz0JwUcM"
          },
          "note" : {
            "type" : "string",
            "description" : "A note for staff, shown on the card in Cliento.",
            "example" : "Web shop order 10042"
          },
          "remainingValue" : {
            "type" : "string",
            "description" : "What is left to spend.",
            "example" : "500.00"
          },
          "soldDate" : {
            "type" : "string",
            "format" : "date",
            "description" : "The day the card was sold, in the location's time zone.",
            "example" : "2026-11-17"
          },
          "status" : {
            "type" : "string",
            "description" : "`active`: usable, nothing spent. `partially_redeemed`: usable, some spent. `redeemed`: nothing left. `expired`: past `validUntil`. `void`: cancelled. More values may be added.",
            "example" : "active"
          },
          "updatedAt" : {
            "type" : "string",
            "format" : "date-time",
            "description" : "When it last changed, in UTC.",
            "example" : "2026-10-12T09:24:31.512Z"
          },
          "validUntil" : {
            "type" : "string",
            "format" : "date",
            "description" : "The last day the card can be used.",
            "example" : "2027-11-17"
          },
          "value" : {
            "type" : "string",
            "description" : "The card's full value.",
            "example" : "500.00"
          },
          "vatPercent" : {
            "type" : "string",
            "description" : "The VAT rate of a single-purpose card.",
            "example" : "25"
          }
        },
        "required" : [ "code", "createdAt", "currency", "id", "locationId", "remainingValue", "status", "updatedAt", "value" ]
      },
      "GiftCardImport" : {
        "type" : "object",
        "description" : "A gift card that was sold somewhere else, such as a web shop. `value` is required, and `merchantId` at a location with several merchants. A field that is left out takes its default, as each field says.",
        "properties" : {
          "accountingType" : {
            "type" : "string",
            "description" : "`single_purpose` or `multi_purpose`. Defaults to the merchant's setting.",
            "example" : "single_purpose"
          },
          "beneficiary" : {
            "type" : "string",
            "example" : "Anna Andersson"
          },
          "code" : {
            "type" : "string",
            "description" : "The code the holder uses when paying. Leave out to have one generated. Stored in upper case. Must not already be used at the location.",
            "example" : "NH-4711"
          },
          "customerId" : {
            "type" : "string",
            "description" : "A person, company or vehicle of this location to link the card to.",
            "example" : "per_2QCtWVSaVO7yCRYBj1zsbA"
          },
          "description" : {
            "type" : "string",
            "description" : "Defaults to \"Presentkort\" (Swedish for gift card).",
            "example" : "Presentkort"
          },
          "externalReference" : {
            "type" : "string",
            "description" : "Your own id for this card, at most 100 characters, such as an order id and line number. Sending the same reference again returns the card created the first time, so a retry never makes a second card.",
            "example" : "shopify-5512340001-1"
          },
          "merchantId" : {
            "type" : "string",
            "description" : "Which merchant the card belongs to. Needed only when the location has more than one; see `GET /v1/locations/{locationId}/merchants`.",
            "example" : "mer_2Rk8sVh3Yq6Nf1Tz0JwUcM"
          },
          "note" : {
            "type" : "string",
            "description" : "A note for staff.",
            "example" : "Web shop order 10042"
          },
          "remainingValue" : {
            "type" : "string",
            "description" : "What is left on a card that was already partly used. Between 0 and `value`. Defaults to `value`. Cannot be changed afterwards.",
            "example" : "500.00"
          },
          "soldDate" : {
            "type" : "string",
            "format" : "date",
            "description" : "Defaults to today in the location's time zone.",
            "example" : "2026-11-17"
          },
          "validUntil" : {
            "type" : "string",
            "format" : "date",
            "description" : "The last day the card can be used. Defaults to `soldDate` plus the merchant's standard validity.",
            "example" : "2027-11-17"
          },
          "value" : {
            "type" : "string",
            "description" : "The card's full value, as a decimal string with at most two decimals. Positive.",
            "example" : "500.00"
          },
          "vatPercent" : {
            "type" : "string",
            "description" : "The VAT rate of a single-purpose card, 0 to 100. Defaults to the merchant's setting. Leave out for a multi-purpose card.",
            "example" : "25"
          }
        },
        "required" : [ "value" ]
      },
      "GiftCardList" : {
        "type" : "object",
        "properties" : {
          "data" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/GiftCard"
            }
          },
          "nextCursor" : {
            "type" : "string",
            "description" : "Pass as `after` to fetch the next page. Absent on the last page.",
            "example" : "gft_7Qm2xN0cT1kPz4Wb9LrEaD"
          }
        },
        "required" : [ "data" ]
      },
      "IdentityNumber" : {
        "type" : "object",
        "description" : "A number issued by an authority that identifies a person, such as a personnummer. The accepted format of each type is listed under Persons.",
        "properties" : {
          "number" : {
            "type" : "string",
            "description" : "In the format of its type: twelve digits for a Swedish personnummer.",
            "example" : "198604122385"
          },
          "type" : {
            "type" : "string",
            "description" : "The kind of number, in lower case, such as `se_personnummer` or `foreign`. More values may be added.",
            "example" : "se_personnummer"
          }
        },
        "required" : [ "number", "type" ]
      },
      "Location" : {
        "type" : "object",
        "description" : "One place of business, such as one salon or one branch of a car wash. Everything else in the API belongs to a location.",
        "properties" : {
          "currency" : {
            "type" : "string",
            "description" : "ISO 4217 currency code.",
            "example" : "SEK"
          },
          "id" : {
            "type" : "string",
            "description" : "Location id.",
            "example" : "loc_4fK9xQ2mLr8TzB1vNc7HdW"
          },
          "name" : {
            "type" : "string"
          },
          "timeZone" : {
            "type" : "string",
            "description" : "IANA time zone.",
            "example" : "Europe/Stockholm"
          }
        },
        "required" : [ "currency", "id", "name", "timeZone" ]
      },
      "LocationList" : {
        "type" : "object",
        "properties" : {
          "data" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/Location"
            }
          }
        },
        "required" : [ "data" ]
      },
      "Merchant" : {
        "type" : "object",
        "description" : "The company that sells at a location: the company whose name is on the receipt. A merchant belongs to one location, and a location usually has one. What is sold, such as a gift card, belongs to a merchant.",
        "properties" : {
          "id" : {
            "type" : "string",
            "description" : "Merchant id.",
            "example" : "mer_2Rk8sVh3Yq6Nf1Tz0JwUcM"
          },
          "name" : {
            "type" : "string",
            "description" : "The company name.",
            "example" : "Salong Exempel AB"
          },
          "organisationNumber" : {
            "type" : "string",
            "description" : "The company's registration number, as entered in Cliento. Left out when none is entered. It can be written with or without a hyphen.",
            "example" : "556677-8899"
          }
        },
        "required" : [ "id", "name" ]
      },
      "MerchantList" : {
        "type" : "object",
        "properties" : {
          "data" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/Merchant"
            }
          }
        },
        "required" : [ "data" ]
      },
      "Person" : {
        "type" : "object",
        "description" : "Represents an individual a location serves. Belongs to one location.",
        "properties" : {
          "address" : {
            "$ref" : "#/components/schemas/Address"
          },
          "communication" : {
            "$ref" : "#/components/schemas/Communication"
          },
          "createdAt" : {
            "type" : "string",
            "format" : "date-time"
          },
          "dateOfBirth" : {
            "type" : "string",
            "description" : "ISO 8601 date.",
            "example" : "1986-04-12"
          },
          "email" : {
            "type" : "string",
            "example" : "anna.lindqvist@example.com"
          },
          "externalReference" : {
            "type" : "string",
            "description" : "Your own id for it, as sent when it was created.",
            "example" : "crm-88121"
          },
          "gender" : {
            "type" : "string",
            "description" : "One lower-case word such as `female`. More values may be added.",
            "example" : "female"
          },
          "id" : {
            "type" : "string",
            "example" : "per_2QCtWVSaVO7yCRYBj1zsbA"
          },
          "identityNumber" : {
            "$ref" : "#/components/schemas/IdentityNumber"
          },
          "locationId" : {
            "type" : "string",
            "example" : "loc_4fK9xQ2mLr8TzB1vNc7HdW"
          },
          "name" : {
            "type" : "string",
            "example" : "Anna Lindqvist"
          },
          "notes" : {
            "type" : "string",
            "description" : "Staff-facing notes, at most 1000 characters."
          },
          "onlineBookingBlocked" : {
            "type" : "boolean",
            "description" : "True when the person may not book online."
          },
          "otherPhoneNumber" : {
            "type" : "string",
            "example" : "+4686543210"
          },
          "owner" : {
            "$ref" : "#/components/schemas/CustomerOwner",
            "description" : "Whose customer the person is within the location. See CustomerOwner."
          },
          "phoneNumber" : {
            "type" : "string",
            "description" : "Mobile number, in E.164 format when it was set through this API.",
            "example" : "+46701234567"
          },
          "updatedAt" : {
            "type" : "string",
            "format" : "date-time"
          }
        },
        "required" : [ "communication", "id", "locationId", "onlineBookingBlocked", "owner" ]
      },
      "PersonCreate" : {
        "type" : "object",
        "properties" : {
          "address" : {
            "$ref" : "#/components/schemas/Address"
          },
          "communication" : {
            "$ref" : "#/components/schemas/Communication"
          },
          "dateOfBirth" : {
            "type" : "string",
            "description" : "ISO 8601 date.",
            "example" : "1986-04-12"
          },
          "email" : {
            "type" : "string",
            "example" : "anna.lindqvist@example.com"
          },
          "externalReference" : {
            "type" : "string",
            "description" : "Your own id for it, at most 100 characters, such as its id in your system. It makes the create safe to repeat and lets you find it again. Compared exactly, letter case included.",
            "example" : "crm-88121"
          },
          "gender" : {
            "type" : "string",
            "enum" : [ "male", "female", "other" ]
          },
          "identityNumber" : {
            "$ref" : "#/components/schemas/IdentityNumber"
          },
          "name" : {
            "type" : "string",
            "example" : "Anna Lindqvist"
          },
          "notes" : {
            "type" : "string",
            "description" : "Staff-facing notes, at most 1000 characters."
          },
          "onlineBookingBlocked" : {
            "type" : "boolean",
            "description" : "Set to true to stop the person booking online."
          },
          "otherPhoneNumber" : {
            "type" : "string",
            "example" : "+4686543210"
          },
          "phoneNumber" : {
            "type" : "string",
            "description" : "Mobile number in E.164 format: a plus sign, the country code and the number, at most 15 digits.",
            "example" : "+46701234567"
          }
        },
        "required" : [ "name" ]
      },
      "PersonList" : {
        "type" : "object",
        "properties" : {
          "data" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/Person"
            }
          },
          "nextCursor" : {
            "type" : "string",
            "description" : "Pass as `after` to fetch the next page. Absent on the last page.",
            "example" : "per_2QCtWVSaVO7yCRYBj1zsbA"
          }
        },
        "required" : [ "data" ]
      },
      "PersonUpdate" : {
        "type" : "object",
        "description" : "A JSON merge patch: send only what changes. A field you leave out keeps its value; a text field, `identityNumber` or `address` set to `null` is cleared, except `name`, which cannot be cleared. `address` merges field by field; `identityNumber` is replaced as a whole. The booleans (`communication.*`, `onlineBookingBlocked`) are true or false and cannot be null.",
        "properties" : {
          "address" : {
            "anyOf" : [ {
              "$ref" : "#/components/schemas/Address"
            }, {
              "type" : "null"
            } ]
          },
          "communication" : {
            "$ref" : "#/components/schemas/Communication"
          },
          "dateOfBirth" : {
            "type" : [ "string", "null" ],
            "description" : "ISO 8601 date.",
            "example" : "1986-04-12"
          },
          "email" : {
            "type" : [ "string", "null" ],
            "example" : "anna.lindqvist@example.com"
          },
          "gender" : {
            "type" : [ "string", "null" ],
            "enum" : [ "male", "female", "other", null ]
          },
          "identityNumber" : {
            "anyOf" : [ {
              "$ref" : "#/components/schemas/IdentityNumber"
            }, {
              "type" : "null"
            } ]
          },
          "name" : {
            "type" : "string",
            "example" : "Anna Lindqvist"
          },
          "notes" : {
            "type" : [ "string", "null" ],
            "description" : "Staff-facing notes, at most 1000 characters."
          },
          "onlineBookingBlocked" : {
            "type" : "boolean",
            "description" : "Set to true to stop the person booking online."
          },
          "otherPhoneNumber" : {
            "type" : [ "string", "null" ],
            "example" : "+4686543210"
          },
          "phoneNumber" : {
            "type" : [ "string", "null" ],
            "description" : "Mobile number in E.164 format: a plus sign, the country code and the number, at most 15 digits.",
            "example" : "+46701234567"
          }
        }
      },
      "PinCheck" : {
        "type" : "object",
        "description" : "A PIN typed by a person at your screen.",
        "properties" : {
          "pin" : {
            "type" : "string",
            "description" : "The PIN, digits only.",
            "example" : "4711"
          }
        },
        "required" : [ "pin" ]
      },
      "PinCheckResult" : {
        "type" : "object",
        "description" : "Whose PIN it is.",
        "properties" : {
          "name" : {
            "type" : "string",
            "description" : "The person's name. Left out when none is registered.",
            "example" : "Clara Nyberg"
          },
          "staffId" : {
            "type" : "string",
            "description" : "Staff id.",
            "example" : "stf_7Qm2xLd9Kc4Vb1Ts8HpWzE"
          }
        },
        "required" : [ "staffId" ]
      },
      "Product" : {
        "type" : "object",
        "description" : "Represents an article a location sells or uses: an entry in its product list. Belongs to that location. Amounts are decimal strings in the location's currency. Quantities are whole numbers of the product.",
        "properties" : {
          "articleNumber" : {
            "type" : "string",
            "description" : "The supplier's article number, as read from a delivery note. Not unique: two suppliers can use the same one.",
            "example" : "81650673"
          },
          "averageCost" : {
            "type" : "string",
            "description" : "The average cost of those in stock, VAT excluded. Only with the scope `stock:cost`.",
            "example" : "61.2311"
          },
          "brand" : {
            "type" : "string",
            "example" : "Wella"
          },
          "createdAt" : {
            "type" : "string",
            "format" : "date-time",
            "description" : "When it was created, in UTC. Left out when that is not known.",
            "example" : "2026-03-02T08:15:00Z"
          },
          "eans" : {
            "type" : "array",
            "description" : "The product's barcodes. A product can be linked to an article in a supplier's catalogue, and then that article's barcodes are included. Can be empty.",
            "example" : [ "4064666230412" ],
            "items" : {
              "type" : "string"
            }
          },
          "id" : {
            "type" : "string",
            "description" : "Product id.",
            "example" : "prd_5Tg8mPq1Xc7Jd2Ls9WbNrK"
          },
          "latestPurchasePrice" : {
            "type" : "string",
            "description" : "What one cost at the latest delivery, VAT excluded. Only with the scope `stock:cost`.",
            "example" : "62.4500"
          },
          "locationId" : {
            "type" : "string",
            "example" : "loc_4fK9xQ2mLr8TzB1vNc7HdW"
          },
          "name" : {
            "type" : "string",
            "example" : "Color Touch 7/0"
          },
          "notForSale" : {
            "type" : "boolean",
            "description" : "Marked as not sold to customers, such as colour used in a treatment. It is a mark only: the point of sale does not refuse to sell the product.",
            "example" : true
          },
          "price" : {
            "type" : "string",
            "description" : "The sale price, VAT included. Left out when none is set.",
            "example" : "149.00"
          },
          "stockQuantity" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "The stock balance. Never negative. Left out when the product is not stock-tracked.",
            "example" : 12
          },
          "stockTracked" : {
            "type" : "boolean",
            "description" : "Whether the location keeps a stock balance for the product.",
            "example" : true
          },
          "updatedAt" : {
            "type" : "string",
            "format" : "date-time",
            "description" : "When it last changed, in UTC.",
            "example" : "2026-10-12T09:24:31.512Z"
          },
          "vatPercent" : {
            "type" : "string",
            "description" : "VAT rate in per cent.",
            "example" : "25"
          }
        },
        "required" : [ "eans", "id", "locationId", "name", "notForSale", "stockTracked", "updatedAt" ]
      },
      "ProductList" : {
        "type" : "object",
        "properties" : {
          "data" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/Product"
            }
          },
          "nextCursor" : {
            "type" : "string",
            "description" : "Pass as `after` to fetch the next page. Absent on the last page.",
            "example" : "prd_5Tg8mPq1Xc7Jd2Ls9WbNrK"
          }
        },
        "required" : [ "data" ]
      },
      "Recipe" : {
        "type" : "object",
        "description" : "A record of what was mixed for a person at one visit. Belongs to that person.",
        "properties" : {
          "appData" : {
            "type" : "object",
            "additionalProperties" : { },
            "description" : "The JSON object of your own that is stored with the recipe, returned as it is.",
            "example" : {
              "formulaId" : "F-2291"
            }
          },
          "bowls" : {
            "type" : "array",
            "description" : "The bowls mixed, in order.",
            "items" : {
              "$ref" : "#/components/schemas/RecipeBowl"
            }
          },
          "createdAt" : {
            "type" : "string",
            "format" : "date-time",
            "example" : "2026-10-11T09:24:31Z"
          },
          "id" : {
            "type" : "string",
            "example" : "rcp_4Kd8sWn2Qy6Hb9Tf3LmVxA"
          },
          "note" : {
            "type" : "string",
            "example" : "Slightly warmer than last time."
          },
          "personId" : {
            "type" : "string",
            "example" : "per_6Jm2tRx8Lc4Nq7Bv5ZgWkD"
          },
          "properties" : {
            "type" : "object",
            "additionalProperties" : {
              "type" : "string"
            },
            "example" : {
              "processingTime" : "35 min"
            }
          },
          "stockReference" : {
            "type" : "string",
            "example" : "treatment-2026-10-11-0042"
          },
          "title" : {
            "type" : "string",
            "example" : "Colour, roots and lengths"
          },
          "updatedAt" : {
            "type" : "string",
            "format" : "date-time",
            "description" : "When staff last changed the recipe in Cliento. Left out when they never have.",
            "example" : "2026-10-12T14:02:10Z"
          }
        },
        "required" : [ "bowls", "createdAt", "id", "personId" ]
      },
      "RecipeBowl" : {
        "type" : "object",
        "description" : "One bowl: what was mixed together, usually colour and a developer.",
        "properties" : {
          "components" : {
            "type" : "array",
            "description" : "What went into the bowl, 1 to 20.",
            "items" : {
              "$ref" : "#/components/schemas/RecipeComponent"
            }
          },
          "properties" : {
            "type" : "object",
            "additionalProperties" : {
              "type" : "string"
            },
            "description" : "Your own names and values for this bowl, with the same limits as a component's `properties`.",
            "example" : {
              "processingTime" : "35 min"
            }
          },
          "ratio" : {
            "type" : "string",
            "description" : "Parts of colour to parts of developer.",
            "example" : "1:2"
          },
          "title" : {
            "type" : "string",
            "description" : "What the bowl is for. At most 200 characters.",
            "example" : "Roots"
          }
        },
        "required" : [ "components" ]
      },
      "RecipeComponent" : {
        "type" : "object",
        "description" : "One thing that went into a bowl.",
        "properties" : {
          "amount" : {
            "type" : "string",
            "description" : "How much, in `unit`, as a decimal string greater than 0 with at most three decimals.",
            "example" : "30"
          },
          "name" : {
            "type" : "string",
            "description" : "What it is, as the hairdresser reads it. At most 200 characters.",
            "example" : "Koleston Perfect 7/0"
          },
          "productId" : {
            "type" : "string",
            "description" : "The product the component was taken from, when it is one of the location's.",
            "example" : "prd_5Tg8mPq1Xc7Jd2Ls9WbNrK"
          },
          "properties" : {
            "type" : "object",
            "additionalProperties" : {
              "type" : "string"
            },
            "description" : "Your own names and values for this component, at most 30. A name is at most 100 characters and a value at most 500.",
            "example" : {
              "brand" : "Wella"
            }
          },
          "role" : {
            "type" : "string",
            "description" : "What the component is in the mix, as one lower-case word such as `colour` or `developer`. The list is open, so accept a value you do not know.",
            "example" : "colour"
          },
          "shade" : {
            "type" : "string",
            "description" : "The shade code of a colour. At most 50 characters.",
            "example" : "7/0"
          },
          "strengthPercent" : {
            "type" : "string",
            "description" : "The strength of a developer in per cent, as a decimal string greater than 0 and at most 100, with at most two decimals.",
            "example" : "6"
          },
          "unit" : {
            "type" : "string",
            "description" : "The unit of `amount`, as the hairdresser reads it. At most 20 characters.",
            "example" : "g"
          }
        },
        "required" : [ "amount", "name", "unit" ]
      },
      "RecipeCreate" : {
        "type" : "object",
        "description" : "What was mixed for a person at one visit.",
        "properties" : {
          "appData" : {
            "type" : "object",
            "additionalProperties" : { },
            "description" : "A JSON object of your own, for what the recipe has no field for. It is stored and returned as it is, and never shown in Cliento. At most 16 kB. A number keeps the precision of a double; send a longer one as a string.",
            "example" : {
              "formulaId" : "F-2291"
            }
          },
          "bowls" : {
            "type" : "array",
            "description" : "The bowls mixed, 1 to 20, in order.",
            "items" : {
              "$ref" : "#/components/schemas/RecipeBowl"
            }
          },
          "note" : {
            "type" : "string",
            "description" : "Free text. At most 4000 characters.",
            "example" : "Slightly warmer than last time."
          },
          "properties" : {
            "type" : "object",
            "additionalProperties" : {
              "type" : "string"
            },
            "description" : "Your own names and values for the whole recipe, with the same limits as a component's `properties`.",
            "example" : {
              "processingTime" : "35 min"
            }
          },
          "staffId" : {
            "type" : "string",
            "description" : "The staff member who made the recipe, shown as its author in the person's log. Without it the author is the API key.",
            "example" : "stf_7Hn3kQw9Zr4Tb6Vy1MxPdE"
          },
          "stockReference" : {
            "type" : "string",
            "description" : "The `externalReference` of the stock document that took the recipe's products out of stock, when you recorded one. At most 100 characters.",
            "example" : "treatment-2026-10-11-0042"
          },
          "title" : {
            "type" : "string",
            "description" : "What the recipe is for. At most 200 characters.",
            "example" : "Colour, roots and lengths"
          }
        },
        "required" : [ "bowls" ]
      },
      "RecipeList" : {
        "type" : "object",
        "properties" : {
          "data" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/Recipe"
            }
          },
          "nextCursor" : {
            "type" : "string",
            "description" : "Pass as `after` to fetch the next page. Absent on the last page.",
            "example" : "rcp_4Kd8sWn2Qy6Hb9Tf3LmVxA"
          }
        },
        "required" : [ "data" ]
      },
      "Resource" : {
        "type" : "object",
        "description" : "What gets booked: most often a person, sometimes a room or a piece of equipment. A resource belongs to one location.",
        "properties" : {
          "createdAt" : {
            "type" : "string",
            "format" : "date-time",
            "description" : "When it was created, in UTC. Left out when that is not known.",
            "example" : "2026-03-02T08:15:00Z"
          },
          "groupId" : {
            "type" : "string",
            "description" : "The group the resource is listed under. Left out for a deleted resource.",
            "example" : "rgr_5Jk9wQe2Ty6Nb3Vx8MzLcA"
          },
          "id" : {
            "type" : "string",
            "example" : "res_4Hn7pQw2Zt9Lk3Vd6XyRbA"
          },
          "locationId" : {
            "type" : "string",
            "example" : "loc_4fK9xQ2mLr8TzB1vNc7HdW"
          },
          "name" : {
            "type" : "string",
            "description" : "The name staff see in the calendar.",
            "example" : "Anna"
          },
          "onlineBookingEnabled" : {
            "type" : "boolean",
            "description" : "Whether the resource is open for online booking at all. Which services customers may book with it is on the resource service.",
            "example" : true
          },
          "onlineName" : {
            "type" : "string",
            "description" : "The name customers see in online booking, when it differs.",
            "example" : "Anna L."
          },
          "pausedUntil" : {
            "type" : "string",
            "description" : "For a paused resource: the last day of the pause.",
            "example" : "2026-12-31"
          },
          "status" : {
            "type" : "string",
            "description" : "`active`, `paused` or `deleted`. A paused resource is paused for billing: it keeps its bookings but takes no new online bookings. A deleted resource is left out of the list but still answers by id, since earlier bookings refer to it. The status is not changed through the API. More values may be added.",
            "example" : "active"
          },
          "type" : {
            "type" : "string",
            "description" : "What kind of thing it is, as one lower-case word such as `person` or `room`. Set when the resource is created. The list is open, so accept a value you do not know.",
            "example" : "person"
          },
          "updatedAt" : {
            "type" : "string",
            "format" : "date-time",
            "description" : "When it last changed, in UTC.",
            "example" : "2026-10-12T09:24:31.512Z"
          }
        },
        "required" : [ "id", "locationId", "name", "onlineBookingEnabled", "status", "type", "updatedAt" ]
      },
      "ResourceGroup" : {
        "type" : "object",
        "description" : "A group of resources: a heading the location lists them under, such as \"Stylists\" or \"Rooms\".",
        "properties" : {
          "id" : {
            "type" : "string",
            "example" : "rgr_5Jk9wQe2Ty6Nb3Vx8MzLcA"
          },
          "locationId" : {
            "type" : "string",
            "example" : "loc_4fK9xQ2mLr8TzB1vNc7HdW"
          },
          "name" : {
            "type" : "string",
            "example" : "Stylists"
          }
        },
        "required" : [ "id", "locationId", "name" ]
      },
      "ResourceGroupList" : {
        "type" : "object",
        "properties" : {
          "data" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/ResourceGroup"
            }
          },
          "nextCursor" : {
            "type" : "string",
            "description" : "Pass as `after` to fetch the next page. Absent on the last page.",
            "example" : "rgr_5Jk9wQe2Ty6Nb3Vx8MzLcA"
          }
        },
        "required" : [ "data" ]
      },
      "ResourceList" : {
        "type" : "object",
        "properties" : {
          "data" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/Resource"
            }
          },
          "nextCursor" : {
            "type" : "string",
            "description" : "Pass as `after` to fetch the next page. Absent on the last page.",
            "example" : "res_4Hn7pQw2Zt9Lk3Vd6XyRbA"
          }
        },
        "required" : [ "data" ]
      },
      "ResourceService" : {
        "type" : "object",
        "description" : "A service as one resource offers it: what is booked. It is identified by its resource and its service. The name, description, group, type and VAT are the service's, and are changed on the service. The duration, after-time, price and online booking setting are the service's defaults, unless the resource overrides them with its own, as `ownValues` says.",
        "properties" : {
          "addOn" : {
            "type" : "boolean",
            "description" : "An add-on is booked together with another service, never on its own.",
            "example" : false
          },
          "afterTimeMinutes" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "Time kept free after the booking, for cleaning up or resting.",
            "example" : 15
          },
          "description" : {
            "type" : "string",
            "description" : "The service's description.",
            "example" : "Wash, cut and styling."
          },
          "durationMinutes" : {
            "type" : "integer",
            "format" : "int32",
            "example" : 45
          },
          "groupId" : {
            "type" : "string",
            "description" : "The group the service is listed under.",
            "example" : "sgr_8Vc2nXd5Rt1Kq7Lm4HwYpZ"
          },
          "locationId" : {
            "type" : "string",
            "example" : "loc_4fK9xQ2mLr8TzB1vNc7HdW"
          },
          "name" : {
            "type" : "string",
            "description" : "The service's name.",
            "example" : "Haircut"
          },
          "onlineBookingAllowed" : {
            "type" : "boolean",
            "description" : "Whether customers may book the service with this resource in online booking. Online booking also requires that the service is shown online (`shownOnline`) and that the resource is open for online booking (`onlineBookingEnabled`).",
            "example" : true
          },
          "ownValues" : {
            "type" : "boolean",
            "description" : "False: the resource service follows the service, and its duration, after-time, price and online booking setting are the service's defaults and change with them. True: it overrides them with its own, which stay when the service changes.",
            "example" : false
          },
          "price" : {
            "type" : "string",
            "example" : "650.00"
          },
          "priceFrom" : {
            "type" : "boolean",
            "description" : "True when the price is a starting price, shown to customers as \"from\".",
            "example" : false
          },
          "resourceId" : {
            "type" : "string",
            "example" : "res_4Hn7pQw2Zt9Lk3Vd6XyRbA"
          },
          "serviceId" : {
            "type" : "string",
            "example" : "svc_3Gk8rTn5Ym1Wc7Zp2LdQxB"
          },
          "shownOnline" : {
            "type" : "boolean",
            "description" : "Whether the service is shown to customers in online booking.",
            "example" : true
          },
          "type" : {
            "type" : "string",
            "description" : "How the service is booked, such as `appointment` or `class`. The same value as the service's `type`. The list is open, so accept a value you do not know.",
            "example" : "appointment"
          },
          "updatedAt" : {
            "type" : "string",
            "format" : "date-time",
            "description" : "When it last changed, in UTC.",
            "example" : "2026-10-12T09:24:31.512Z"
          },
          "vatParts" : {
            "type" : "array",
            "description" : "For a service that consists of parts accounted for at different VAT rates, such as a vaccine with no VAT and a certificate at 25 %: the service's parts, at the service's default prices. Left out for every other service.",
            "items" : {
              "$ref" : "#/components/schemas/ServiceVatPart"
            }
          },
          "vatPercent" : {
            "type" : "string",
            "description" : "The service's VAT rate, included in the price. A service with `vatParts` is sold with each part at its own rate when it is paid for at the point of sale or online. This is then the single rate used where the service is not split into its parts.",
            "example" : "25"
          }
        },
        "required" : [ "addOn", "afterTimeMinutes", "durationMinutes", "locationId", "name", "onlineBookingAllowed", "ownValues", "price", "priceFrom", "resourceId", "serviceId", "shownOnline", "type", "updatedAt", "vatPercent" ]
      },
      "ResourceServiceList" : {
        "type" : "object",
        "properties" : {
          "data" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/ResourceService"
            }
          },
          "nextCursor" : {
            "type" : "string",
            "description" : "Pass as `after` to fetch the next page. Absent on the last page.",
            "example" : "svc_3Gk8rTn5Ym1Wc7Zp2LdQxB"
          }
        },
        "required" : [ "data" ]
      },
      "Service" : {
        "type" : "object",
        "description" : "A service as the location defines it: something the location offers, with its name and the default duration, price and settings. Belongs to a location. What is booked is a resource service, which follows these defaults unless the resource overrides them.",
        "properties" : {
          "addOn" : {
            "type" : "boolean",
            "description" : "An add-on is booked together with another service, never on its own.",
            "example" : false
          },
          "afterTimeMinutes" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "Time kept free after the booking, for cleaning up or resting.",
            "example" : 15
          },
          "createdAt" : {
            "type" : "string",
            "format" : "date-time",
            "description" : "When it was created, in UTC. Left out when that is not known.",
            "example" : "2026-03-02T08:15:00Z"
          },
          "description" : {
            "type" : "string",
            "example" : "Wash, cut and styling."
          },
          "durationMinutes" : {
            "type" : "integer",
            "format" : "int32",
            "example" : 45
          },
          "groupId" : {
            "type" : "string",
            "description" : "The group the service is listed under. Left out for a deleted service.",
            "example" : "sgr_8Vc2nXd5Rt1Kq7Lm4HwYpZ"
          },
          "id" : {
            "type" : "string",
            "example" : "svc_3Gk8rTn5Ym1Wc7Zp2LdQxB"
          },
          "locationId" : {
            "type" : "string",
            "example" : "loc_4fK9xQ2mLr8TzB1vNc7HdW"
          },
          "name" : {
            "type" : "string",
            "example" : "Haircut"
          },
          "onlineBookingAllowed" : {
            "type" : "boolean",
            "description" : "The default for the service's resource services: whether customers may book the service themselves in online booking. A resource can override it. A service can be shown without being bookable, so that customers see it and call.",
            "example" : true
          },
          "price" : {
            "type" : "string",
            "example" : "595.00"
          },
          "priceFrom" : {
            "type" : "boolean",
            "description" : "True when the price is a starting price, shown to customers as \"from\".",
            "example" : false
          },
          "resourceServices" : {
            "type" : "array",
            "description" : "The service's resource services: one for each resource the service can be booked for. The same resource services are read per resource under `/resources/{resourceId}/services`.",
            "items" : {
              "$ref" : "#/components/schemas/ResourceService"
            }
          },
          "shownOnline" : {
            "type" : "boolean",
            "description" : "Whether the service is shown to customers in online booking.",
            "example" : true
          },
          "status" : {
            "type" : "string",
            "description" : "`active`, or `deleted` for a service that has been removed: it is left out of the list but still answers by id, since earlier bookings refer to it. More values may be added.",
            "example" : "active"
          },
          "type" : {
            "type" : "string",
            "description" : "How it is booked, as one lower-case word such as `appointment` or `class`. Set when the service is created. The list is open, so accept a value you do not know.",
            "example" : "appointment"
          },
          "updatedAt" : {
            "type" : "string",
            "format" : "date-time",
            "description" : "When it last changed, in UTC.",
            "example" : "2026-10-12T09:24:31.512Z"
          },
          "vatParts" : {
            "type" : "array",
            "description" : "For a service that consists of parts accounted for at different VAT rates, such as a vaccine with no VAT and a certificate at 25 %: the parts. Left out for every other service.",
            "items" : {
              "$ref" : "#/components/schemas/ServiceVatPart"
            }
          },
          "vatPercent" : {
            "type" : "string",
            "description" : "The VAT rate included in the price. A service with `vatParts` is sold with each part at its own rate when it is paid for at the point of sale or online. This is then the single rate used where the service is not split into its parts.",
            "example" : "25"
          }
        },
        "required" : [ "addOn", "afterTimeMinutes", "durationMinutes", "id", "locationId", "name", "onlineBookingAllowed", "price", "priceFrom", "resourceServices", "shownOnline", "status", "type", "updatedAt", "vatPercent" ]
      },
      "ServiceGroup" : {
        "type" : "object",
        "description" : "A group of services: a heading the location lists them under, such as \"Hair\" or \"Colour\".",
        "properties" : {
          "addOns" : {
            "type" : "boolean",
            "description" : "True for the group that holds the location's add-ons.",
            "example" : false
          },
          "id" : {
            "type" : "string",
            "example" : "sgr_8Vc2nXd5Rt1Kq7Lm4HwYpZ"
          },
          "locationId" : {
            "type" : "string",
            "example" : "loc_4fK9xQ2mLr8TzB1vNc7HdW"
          },
          "name" : {
            "type" : "string",
            "example" : "Hair"
          }
        },
        "required" : [ "addOns", "id", "locationId", "name" ]
      },
      "ServiceGroupList" : {
        "type" : "object",
        "properties" : {
          "data" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/ServiceGroup"
            }
          },
          "nextCursor" : {
            "type" : "string",
            "description" : "Pass as `after` to fetch the next page. Absent on the last page.",
            "example" : "sgr_8Vc2nXd5Rt1Kq7Lm4HwYpZ"
          }
        },
        "required" : [ "data" ]
      },
      "ServiceList" : {
        "type" : "object",
        "properties" : {
          "data" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/Service"
            }
          },
          "nextCursor" : {
            "type" : "string",
            "description" : "Pass as `after` to fetch the next page. Absent on the last page.",
            "example" : "svc_3Gk8rTn5Ym1Wc7Zp2LdQxB"
          }
        },
        "required" : [ "data" ]
      },
      "ServiceVatPart" : {
        "type" : "object",
        "description" : "One part of a service that consists of several, with its share of the price and the VAT rate that part is booked at.",
        "properties" : {
          "description" : {
            "type" : "string",
            "example" : "Treatment"
          },
          "price" : {
            "type" : "string",
            "example" : "600.00"
          },
          "vatPercent" : {
            "type" : "string",
            "example" : "25"
          }
        },
        "required" : [ "description", "price", "vatPercent" ]
      },
      "Staff" : {
        "type" : "object",
        "description" : "Represents a person who works at a location, and belongs to that location. Other data refers to a staff id to record who made a sale or a stock withdrawal. A staff member is not a resource; a resource is what gets booked, and a staff member can be linked to one or more resources, or to none.",
        "properties" : {
          "hasPin" : {
            "type" : "boolean",
            "description" : "Whether the person has a PIN for the point of sale. The PIN itself is never returned.",
            "example" : true
          },
          "id" : {
            "type" : "string",
            "description" : "Staff id.",
            "example" : "stf_7Qm2xLd9Kc4Vb1Ts8HpWzE"
          },
          "name" : {
            "type" : "string",
            "description" : "The person's name. Left out when none is registered.",
            "example" : "Clara Nyberg"
          },
          "resourceIds" : {
            "type" : "array",
            "description" : "The resources the person is linked to. Often one, and can be empty.",
            "example" : [ "res_4Hn7pQw2Zt9Lk3Vd6XyRbA" ],
            "items" : {
              "type" : "string"
            }
          }
        },
        "required" : [ "hasPin", "id", "resourceIds" ]
      },
      "StaffList" : {
        "type" : "object",
        "properties" : {
          "data" : {
            "type" : "array",
            "description" : "The staff of the location, sorted by name. At most 500.",
            "items" : {
              "$ref" : "#/components/schemas/Staff"
            }
          },
          "pinsInUse" : {
            "type" : "boolean",
            "description" : "Whether the location has switched PINs on at its point of sale. When false, staff pick their name from a list there, and a PIN a person happens to have is not asked for.",
            "example" : true
          }
        },
        "required" : [ "data", "pinsInUse" ]
      },
      "StockDocument" : {
        "type" : "object",
        "description" : "Stock changes that belong together, such as the rows of a delivery note or what one treatment used. Every line is applied, or none.",
        "properties" : {
          "comment" : {
            "type" : "string",
            "description" : "A note, at most 500 characters. Stored on every movement.",
            "example" : "Colour for Anna, 14:00"
          },
          "externalReference" : {
            "type" : "string",
            "description" : "Your own reference for the document, such as a delivery note number, 1 to 100 characters. A document whose reference has been used at the location changes nothing and is answered with the movements made the first time, so it is safe to send again at any time.",
            "example" : "DN-2026-10412"
          },
          "lines" : {
            "type" : "array",
            "description" : "1 to 100 lines, applied in order.",
            "items" : {
              "$ref" : "#/components/schemas/StockMovementLine"
            }
          },
          "staffId" : {
            "type" : "string",
            "description" : "The staff member who did it. Shown as who made the movements.",
            "example" : "stf_7Qm2xLd9Kc4Vb1Ts8HpWzE"
          }
        },
        "required" : [ "lines" ]
      },
      "StockDocumentResult" : {
        "type" : "object",
        "properties" : {
          "data" : {
            "type" : "array",
            "description" : "The movements made, in the order of the lines.",
            "items" : {
              "$ref" : "#/components/schemas/StockMovement"
            }
          }
        },
        "required" : [ "data" ]
      },
      "StockMovement" : {
        "type" : "object",
        "description" : "One change of the stock balance of a product or of a colour bar item. A movement is never changed or removed: a mistake is undone by a new movement that corrects it.",
        "properties" : {
          "balanceAfter" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "The balance after this movement.",
            "example" : 505
          },
          "colourBarItemId" : {
            "type" : "string",
            "description" : "The colour bar item whose balance changed, in packs. Left out when the movement is a product's.",
            "example" : "cbi_3Vn8Kq5TzL1xWc7PdM2RbJ"
          },
          "colourBarMixId" : {
            "type" : "string",
            "description" : "The colour bar mix that made the movement, by finishing a pack or by being undone. Such a movement is not corrected on its own: the mix is undone.",
            "example" : "cbm_8Wq3Nd6HtY1mKx4LcP9RvB"
          },
          "comment" : {
            "type" : "string",
            "example" : "Colour for Anna, 14:00"
          },
          "correctedByMovementId" : {
            "type" : "string",
            "description" : "On a movement that has been corrected: its correction.",
            "example" : "mov_6Wr3tYu8Io1Pa4Sd7FgHjK"
          },
          "correctsMovementId" : {
            "type" : "string",
            "description" : "On a correction: the movement it undoes.",
            "example" : "mov_9Bd2fHk4Mn7Pq1Sv6XzCtA"
          },
          "createdAt" : {
            "type" : "string",
            "format" : "date-time",
            "example" : "2026-10-11T09:24:31.512Z"
          },
          "externalReference" : {
            "type" : "string",
            "description" : "The caller's own reference for what the movement belongs to.",
            "example" : "visit-88213"
          },
          "id" : {
            "type" : "string",
            "description" : "Movement id.",
            "example" : "mov_3Lp6vRt9Yx2Kc5Ng8QwEzH"
          },
          "listValue" : {
            "type" : "string",
            "description" : "On a withdrawal: what was taken, at the list price then of the article in a supplier's catalogue that it is linked to, VAT excluded, as a decimal string. Left out when there is no such list price. It is kept as it was: a later change of the list price does not change it.",
            "example" : "304.00"
          },
          "productId" : {
            "type" : "string",
            "description" : "The product whose balance changed. Left out when the movement is a colour bar item's.",
            "example" : "prd_5Tg8mPq1Xc7Jd2Ls9WbNrK"
          },
          "quantity" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "The change applied to the balance: negative when stock left.",
            "example" : -2
          },
          "reason" : {
            "type" : "string",
            "description" : "Why stock was taken out: a lower-case word such as `consumption` or `damaged`. The list is open, so accept a value you do not know.",
            "example" : "consumption"
          },
          "residue" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "On a withdrawal made by a colour bar mix: the grams that were never taken from the pack it finished.",
            "example" : 3
          },
          "shortfall" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "How much of what was asked for was not in stock; the balance never goes below 0. A sale of 3 with 1 in stock has `quantity` -1 and `shortfall` 2. 0 when everything was in stock.",
            "example" : 0
          },
          "staffId" : {
            "type" : "string",
            "description" : "Who made it, when a staff member did: look the name up in the staff list. Left out for a movement made without one, such as by an administrator in Cliento.",
            "example" : "stf_7Qm2xLd9Kc4Vb1Ts8HpWzE"
          },
          "type" : {
            "type" : "string",
            "description" : "`sale` and `refund`: at the point of sale. `delivery`: goods received, or the adjustment made when a delivery is changed afterwards. `withdrawal`: taken out by hand. `correction`: undoes the movement in `correctsMovementId`. `stocktake`: the difference found by a count. `opening_balance`: the balance when movements began to be recorded for the product. More values may be added.",
            "example" : "withdrawal"
          }
        },
        "required" : [ "balanceAfter", "createdAt", "id", "quantity", "shortfall", "type" ]
      },
      "StockMovementLine" : {
        "type" : "object",
        "description" : "One line of a stock document.",
        "properties" : {
          "colourBarItemId" : {
            "type" : "string",
            "description" : "The colour bar item, in place of `productId`. Its `quantity` is in whole packs.",
            "example" : "cbi_3Vn8Kq5TzL1xWc7PdM2RbJ"
          },
          "movementId" : {
            "type" : "string",
            "description" : "For a `correction`: the withdrawal to undo.",
            "example" : "mov_9Bd2fHk4Mn7Pq1Sv6XzCtA"
          },
          "productId" : {
            "type" : "string",
            "description" : "The product. A `delivery` or `withdrawal` line names a product or a colour bar item, never both.",
            "example" : "prd_5Tg8mPq1Xc7Jd2Ls9WbNrK"
          },
          "quantity" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "How many, as a whole number greater than 0. Required for all types but `correction`.",
            "example" : 2
          },
          "reason" : {
            "type" : "string",
            "description" : "For a `withdrawal`: why, as one lower-case word such as `consumption` or `damaged`. The list is open.",
            "example" : "consumption"
          },
          "type" : {
            "type" : "string",
            "description" : "`delivery`: goods received. `withdrawal`: stock taken out. `correction`: undoes the withdrawal in `movementId`.",
            "example" : "withdrawal"
          },
          "unitCost" : {
            "type" : "string",
            "description" : "For a `delivery`: what one cost, VAT excluded, as a decimal string with at most four decimals. 0 when left out.",
            "example" : "62.4500"
          }
        },
        "required" : [ "type" ]
      },
      "StockMovementList" : {
        "type" : "object",
        "properties" : {
          "data" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/StockMovement"
            }
          },
          "nextCursor" : {
            "type" : "string",
            "description" : "Pass as `after` to fetch the next page. Absent when there is nothing more yet; to follow new movements, call again later with the id of the last movement you have read.",
            "example" : "mov_3Lp6vRt9Yx2Kc5Ng8QwEzH"
          }
        },
        "required" : [ "data" ]
      },
      "Vehicle" : {
        "type" : "object",
        "description" : "Represents a vehicle a location works on. Belongs to one location.",
        "properties" : {
          "colour" : {
            "type" : "string",
            "description" : "From the vehicle register.",
            "example" : "Red"
          },
          "createdAt" : {
            "type" : "string",
            "format" : "date-time"
          },
          "description" : {
            "type" : "string",
            "description" : "What the vehicle is called in Cliento. A lookup in the vehicle register fills it in with the make, model, colour and year; staff can change it.",
            "example" : "The red van"
          },
          "externalReference" : {
            "type" : "string",
            "description" : "Your own id for it, as sent when it was created.",
            "example" : "crm-88121"
          },
          "id" : {
            "type" : "string",
            "example" : "veh_3Ks7pQx2Wn9Tb4Vd6HyRcE"
          },
          "locationId" : {
            "type" : "string",
            "example" : "loc_4fK9xQ2mLr8TzB1vNc7HdW"
          },
          "make" : {
            "type" : "string",
            "description" : "From the Swedish vehicle register, when staff have looked the vehicle up in Cliento. Not set through the API.",
            "example" : "Volvo"
          },
          "model" : {
            "type" : "string",
            "description" : "From the vehicle register.",
            "example" : "V60"
          },
          "modelYear" : {
            "type" : "integer",
            "format" : "int32",
            "description" : "From the vehicle register.",
            "example" : 2021
          },
          "notes" : {
            "type" : "string",
            "description" : "Staff-facing notes, at most 1000 characters."
          },
          "registrationNumber" : {
            "type" : "string",
            "description" : "The registration number, one to seven characters.",
            "example" : "ABC123"
          },
          "updatedAt" : {
            "type" : "string",
            "format" : "date-time"
          }
        },
        "required" : [ "id", "locationId" ]
      },
      "VehicleCreate" : {
        "type" : "object",
        "description" : "A vehicle needs a `registrationNumber` or a `description`.",
        "properties" : {
          "description" : {
            "type" : "string",
            "description" : "What the vehicle is called in Cliento, such as `The red van`.",
            "example" : "The red van"
          },
          "externalReference" : {
            "type" : "string",
            "description" : "Your own id for it, at most 100 characters, such as its id in your system. It makes the create safe to repeat and lets you find it again. Compared exactly, letter case included.",
            "example" : "crm-88121"
          },
          "notes" : {
            "type" : "string",
            "description" : "Staff-facing notes, at most 1000 characters."
          },
          "registrationNumber" : {
            "type" : "string",
            "description" : "The registration number, one to seven characters.",
            "example" : "ABC123"
          }
        }
      },
      "VehicleList" : {
        "type" : "object",
        "properties" : {
          "data" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/Vehicle"
            }
          },
          "nextCursor" : {
            "type" : "string",
            "description" : "Pass as `after` to fetch the next page. Absent on the last page.",
            "example" : "veh_3Ks7pQx2Wn9Tb4Vd6HyRcE"
          }
        },
        "required" : [ "data" ]
      },
      "VehicleUpdate" : {
        "type" : "object",
        "description" : "A JSON merge patch: send only what changes. A field you leave out keeps its value; a field set to `null` is cleared, but a vehicle must keep a `registrationNumber` or a `description`.",
        "properties" : {
          "description" : {
            "type" : [ "string", "null" ],
            "description" : "What the vehicle is called in Cliento, such as `The red van`.",
            "example" : "The red van"
          },
          "notes" : {
            "type" : [ "string", "null" ],
            "description" : "Staff-facing notes, at most 1000 characters."
          },
          "registrationNumber" : {
            "type" : [ "string", "null" ],
            "description" : "The registration number, one to seven characters.",
            "example" : "ABC123"
          }
        }
      }
    },
    "securitySchemes" : {
      "apiKey" : {
        "description" : "An API key created in Cliento, sent as a bearer token.",
        "scheme" : "bearer",
        "type" : "http"
      }
    }
  },
  "x-tagGroups" : [ {
    "description" : "A location is one place of business, such as one salon or one branch of a car wash. An\norganisation with one salon has one location. A chain has one per salon.\n\nEverything else in the API belongs to a location: customers, services, bookings, staff, points of\nsale and stock.",
    "name" : "Locations",
    "tags" : [ "Locations" ]
  }, {
    "description" : "Staff are the people who work at a location. Other calls refer to a staff member to say who did\nsomething, such as who took stock out.\n\nA staff member is not the same as a resource. A resource is what gets booked in the calendar. A\nstaff member can be linked to one or more resources, or to none.",
    "name" : "Staff",
    "tags" : [ "Staff", "Staff hours" ]
  }, {
    "description" : "The Customers APIs let an integration keep a location's customer register in step with another\nsystem: find customers, and create, change and delete them.\n\nA customer is one of three types, each with its own API:\n\n- **Persons.** A person is an individual: the one who books, and the one the location sends\n  reminders to.\n- **Companies.** A company owns a vehicle or pays the invoice.\n- **Vehicles.** A vehicle is what a garage or a car wash works on.\n\nThe Customers API searches all three types at once. Use it when you have a phone number or a\nreference and do not know which type it belongs to.\n\nEach customer belongs to one location. The links between them, such as a vehicle's owner, are not\navailable through the API yet.\n\nWhat staff record about a person over time, such as a recipe, belongs to that person.",
    "name" : "Customers",
    "tags" : [ "Persons", "Recipes", "Companies", "Vehicles", "Customers", "Customer log" ]
  }, {
    "description" : "The Booking APIs let an integration read a location's bookings. They also list its resources and\nits services. Creating and changing bookings through the API is coming soon.\n\nA booking is built from these:\n\n- **Resources.** A resource is what gets booked. It is most often a person, sometimes a room or a\n  piece of equipment.\n- **Services.** A service is something the location offers, such as a haircut. It has a name and\n  a default duration and price.\n- **Resource services.** A resource service is a service as one resource offers it. It uses the\n  service's duration and price unless the resource has its own.\n- **Schedules.** A schedule says when a resource works.\n- **Availability.** Availability says when a service can be booked. It is worked out from the\n  resource services, the schedules and the bookings already made.\n- **Bookings.** A booking is a time on a resource, usually for a customer and one or more\n  services.\n\n**Getting started with bookings**\n\nA booking needs other things to exist first. Set them up in this order:\n\n1. **Add a resource.** A booking is always made on a resource, so the location needs at least\n   one. Resources are added in Cliento today. List them with\n   `GET /v1/locations/{locationId}/resources`.\n2. **Add the services the location offers.** Services are added in Cliento today. List them with\n   `GET /v1/locations/{locationId}/services`.\n3. **Enable each service for the resources that offer it.** That creates a resource service, with\n   the duration and price that apply to that resource. It is done in Cliento today. Read a\n   resource's resource services with\n   `GET /v1/locations/{locationId}/resources/{resourceId}/services`.\n4. **Create the customer the booking is for.** Create a person with\n   `POST /v1/locations/{locationId}/persons`, a company with\n   `POST /v1/locations/{locationId}/companies`, or a vehicle with\n   `POST /v1/locations/{locationId}/vehicles`.\n5. **Create the booking.** A booking is a time on the resource, for the customer, with one or\n   more of the resource's services. Bookings are made in Cliento or by customers online today.\n   Read them with `GET /v1/locations/{locationId}/bookings`.\n\nAdding resources and services, enabling a service for a resource and creating a booking through\nthe API are coming soon.\n\nA reservation skips steps 2 to 4. It only blocks time on a resource, such as a lunch break or a\nstaff meeting, and needs no service and no customer.\n\nOnly the resource is required. Staff can make a booking in Cliento without a customer, without a\nservice, or with a service the resource is not enabled for. Expect to read bookings like that. A\nschedule is not required either: it decides when a service can be booked online, not whether a\nbooking can exist.",
    "name" : "Booking",
    "tags" : [ "Resources", "Services", "Schedules", "Availability", "Bookings" ]
  }, {
    "description" : "The Point of sale APIs let an integration list a location's merchants, and read, import and void\ngift cards. Points of sale, sales, receipts, payments and invoices are coming soon.\n\nA merchant is the company that sells at a location. Most locations have one. A location has\nseveral when independent businesses share it, such as a salon with rented chairs.\n\nSales, receipts, payments and gift cards belong to a merchant, not directly to the location. The\nmoney and the bookkeeping are the merchant's.",
    "name" : "Point of sale",
    "tags" : [ "Merchants", "Gift cards", "Points of sale", "Sales", "Receipts", "Payments", "Invoices" ]
  }, {
    "description" : "The Products and stock APIs are for an integration that keeps track of a location's stock. It\ncan read the products and their balances, and record deliveries and withdrawals.\n\nA location has one product list and one stock. All the location's merchants share them.\n\n- **Products.** A product is an article the location sells or uses. A product with stock tracking\n  has a balance: the number of units the location has.\n- **Stock movements.** A stock movement is one change to a balance. The movements are the history\n  behind every balance.\n\nA balance changes in several ways. These are the common ones:\n\n- A sale at a point of sale takes out what is sold, whichever merchant sells it.\n- A delivery adds to it.\n- Staff take out what they use.\n- A stocktake counts the stock and corrects the balance to what was counted.\n\nA hair salon's colours, developers and bleaches are not products. They are colour bar items,\ndescribed under Colour bar. Their balances are in the same stock, so a stock movement names\neither a product or a colour bar item.\n\nThe Products API reads the product list and the balances. The Stock movements API reads the\nhistory and records deliveries and withdrawals. The APIs for points of sale, sales and stocktakes\nare coming soon.",
    "name" : "Products and stock",
    "tags" : [ "Products", "Stock movements", "Stocktakes" ]
  }, {
    "description" : "The Colour bar APIs are for hair salons. They let an integration manage a salon's stock of hair\ncolour and keep it correct as the colour is used.\n\nHair colour is not sold to the customer. It is used a little at a time: a tube is opened, and each\nmix takes some grams from it. The colour bar keeps track of both the packs in stock and how much\nis left in the open one.\n\nThere are three APIs:\n- **Colour bar ranges.** A range is a supplier's series, such as one brand's permanent colours.\n- **Colour bar items.** An item is one shade in a range. Each item has its own stock.\n- **Colour bar mixes.** A mix is what a staff member mixed for one customer. Registering a mix\n  takes the grams used from each item's stock.\n\nDeliveries are recorded through the Stock movements API.",
    "name" : "Colour bar",
    "tags" : [ "Colour bar ranges", "Colour bar items", "Colour bar mixes" ]
  }, {
    "description" : "The Marketing APIs will let an integration read the campaigns a location has sent to its\ncustomers, and the reviews customers have left after their visits. They are coming soon.",
    "name" : "Marketing",
    "tags" : [ "Campaigns", "Reviews" ]
  }, {
    "description" : "The Reports API will let an integration read a location's figures over a period, such as\nbookings, sales and staff hours. It is coming soon.",
    "name" : "Reports",
    "tags" : [ "Reports" ]
  } ]
}
