Cliento API reference
Base URL: https://api.cliento.com
The Cliento API gives your own systems server-to-server access to your organisation's data in Cliento.
Everything in the API belongs to a location. A location is one place of business, such as one
salon or one branch of a car wash. Start with GET /v1/locations to list the locations your key
can use. Almost every other call takes a location id in its path. An item can also be read by its
id, without its location; see "Identifiers".
This introduction covers what applies to every call. Each part of the reference describes its own API.
Industry-specific APIs
Most APIs are used by every organisation, whatever its industry. A few are made for one industry, such as the Recipes API for hair and beauty and the Vehicles API for automotive. The reference labels such an API with its industry. It can label a single field or value in the same way. The label is information only: the API does not check an organisation's industry, and no call is refused because of it.
Authentication
Create an API key for your organisation and send it on every request:
Authorization: Bearer cl_live_...A key belongs to one organisation and is shown only once, when it is created. Keep it on your server; never ship it in a browser or mobile app. If a key leaks, revoke it and create a new one.
Each key carries scopes that decide what it may do, for example customers:read and
customers:write. A call outside the key's scopes is answered with 403 and code forbidden.
Identifiers
Every id is an opaque string with a prefix that says what it identifies. Store ids as strings and do not parse them: their length and the characters after the prefix may change. An id from one host, such as our staging host, does not exist on another.
An id is enough to read the item it identifies. Every item has a path under its location, such as
/v1/locations/{locationId}/persons/{personId}, and one without it, /v1/persons/{personId}.
Use the second when you have an id and not its location: an id you stored, or one that another
item refers to. It works for an item at any location your key can use, and the answer includes
the item's locationId. An id at a location your key cannot use is answered like an id that does
not exist, with entity_not_found.
| Prefix | Identifies | Example |
|---|---|---|
loc_ | A location | loc_4fK9xQ2mLr8TzB1vNc7HdW |
per_ | A person | per_2QCtWVSaVO7yCRYBj1zsbA |
com_ | A company | com_6Tn3wRd8Kq1Vb5Xs9LmPyA |
veh_ | A vehicle | veh_3Ks7pQx2Wn9Tb4Vd6HyRcE |
stf_ | A staff member | stf_7Qm2xLd9Kc4Vb1Ts8HpWzE |
mer_ | A merchant | mer_2Rk8sVh3Yq6Nf1Tz0JwUcM |
gft_ | A gift card | gft_7Qm2xN0cT1kPz4Wb9LrEaD |
prd_ | A product | prd_5Tg8mPq1Xc7Jd2Ls9WbNrK |
mov_ | A stock movement | mov_9Bd2fHk4Mn7Pq1Sv6XzCtA |
res_ | A resource | res_2QCtWVSaVO7yCRYBj1zsbA |
rgr_ | A resource group | rgr_5Jk9wQe2Ty6Nb3Vx8MzLcA |
svc_ | A service | svc_3Gk8rTn5Ym1Wc7Zp2LdQxB |
sgr_ | A service group | sgr_8Vc2nXd5Rt1Kq7Lm4HwYpZ |
rcp_ | A recipe | rcp_4Kd8sWn2Qy6Hb9Tf3LmVxA |
bkg_ | A booking | bkg_5Rt8mKq2Wn7Xb4Vd9HyLcA |
bks_ | A series of bookings | bks_6Wq2nYd8Rk4Tc9Xb3HmLzP |
sal_ | A sale | sal_8Hd3kWq5Zn2Tb7Xc4LmPyR |
Created and updated times
Persons, companies, vehicles, resources, services, products, gift cards and recipes have
createdAt and updatedAt, both in UTC.
updatedAt is the time the stored item last changed. Use it to tell whether your copy of an item
is current. createdAt is the time the item was created. It is left out where it is not known,
such as for a service created before these times were kept.
A service's updatedAt also moves when one of its resource services is added, changed or
removed, because a service is read with them.
Three things do not move updatedAt:
- A value that changes with time alone, such as a gift card's
statuswhen the card expires. - A change to the location's own settings, such as its time zone.
- A barcode that a supplier adds to the catalogue article a product is linked to.
Live and test keys
A key that starts with cl_live_ belongs to an organisation with real data, in production. Every
other key starts with cl_test_. In production a cl_test_ key belongs to a sandbox: an
organisation of its own on the same API and the same host, with data that is yours to create and
throw away. A sandbox sends no SMS or email to customers
and takes no payments. Outside production, such as on our staging host, all keys are cl_test_.
Updating
Updates use PATCH and change only what you send. A field you leave out keeps its value. To
clear a field, send it as null. One request is applied as a whole or not at all.
Paging
Most lists are paged. Pass the nextCursor of one page as after to get the next, and go on
until a page has none. Follow nextCursor even on a list that fits in one page today. Locations,
staff and merchants are returned in one response.
A cursor names the last item you read. Some lists refuse a cursor whose item has been removed
since, with validation_failed on after. Read the list again from the start when that
happens.
Errors
Every non-2xx response has the same JSON body:
{ "code": "validation_failed", "message": "...", "details": { "fieldErrors": { "email": "..." } } }Branch on code; message is for humans and may change. An id that does not exist, or that your
key may not access, is answered with 400 and code entity_not_found. 404 is reserved for a
URL that is not an endpoint at all.
Requests are validated strictly: an unknown JSON field is rejected with invalid_request rather
than ignored.
Rate limits
Requests are limited per API key. When the limit is exceeded the answer is 429 with code
rate_limited and a Retry-After header giving the number of seconds to wait.
A request body may be at most 1 MB, far more than any call needs. A larger one is answered with
413 and code invalid_request.
Versioning
The version is in the URL (/v1). Every operation in the reference is marked stable or
preview.
- A stable operation only changes in backwards-compatible ways: new optional request fields, new response fields and new values in open-ended enumerations. Your client must tolerate response fields and enumeration values it does not know. A breaking change to a stable operation means a new version, and the previous version keeps working during a published deprecation period.
- A preview operation works and can be built against, but its fields, paths and answers may still change without a new version. Tell us when you build against one, so that we know to make it stable.
New operations can be added at any time.
Amounts
Money is a decimal string in the location's currency, such as "500.00", never a JSON number.
Send at most two decimals.
Retrying safely
A timeout or a dropped connection can hide the answer to a request that did succeed. Every call that changes something can be made safe to send again.
Idempotency-Key is the general way. It is a header that every POST, PATCH and DELETE
that changes something accepts, and it holds a value you make up for one request, such as a UUID.
Send the same value when you retry. The first request runs. For 24 hours every repeat is answered
with the first request's answer, marked with the header Idempotent-Replayed: true, and does not
run again. A value belongs to the API key it was sent with: the same value sent with another key
is a different request. Send one on every write.
- Using an
Idempotency-Keyfor a different request (another path or body) is refused with422andidempotency_key_reused. - A repeat that arrives while the first request is still running is refused with
409andrequest_in_progress; wait a moment and send it again. - An answer of
500or above is not kept, so a retry runs the request again. - A refusal such as
validation_failedis kept like any other answer: correct the request and send it with a newIdempotency-Key.
externalReference is a second way, for a call that creates something. It is your own id for
what you create, such as an order number or the id in your own system, sent in the body. When a
call arrives with a reference that is already in use, nothing is created or changed: the existing
item is returned as it is, with 200 instead of 201. A reference that belongs to a customer
of another type is refused with duplicate instead. Unlike an Idempotency-Key, a reference
lasts as long as the item does, and you can find the item by it later. Persons, companies,
vehicles, gift cards and stock documents (the stock changes recorded in one call) take one. When
you have an id of your own, send it as well as the header.