API Design Conventions
These conventions apply to every endpoint of the SmaCH API: artworks, monitorings, events and inventory, as well as organizations, users and members. A client meets the same vocabulary whichever resource it calls. The detail of each endpoint, its parameters and schemas, is in the API reference.
| Environment | API host | Reference |
|---|---|---|
| Production | https://api.smach.science |
api.smach.science/reference |
| Development | https://api.dev.smach.science |
api.dev.smach.science/reference |
1. Requests
Section titled “1. Requests”Authentication and organization
Section titled “Authentication and organization”Every call carries an OAuth2 Bearer token from the SmaCH Keycloak, realm smach in
production and smach-dev in development. Each API accepts the tokens of its own realm
only. A person signs in with the authorization code flow and PKCE; a system uses client
credentials.
On the artworks, monitorings, events and inventory endpoints, the X-Organization header
names the organization a request applies to, by its alias:
- optional for an account that belongs to one organization;
- required for an account that belongs to several (400 without it);
- a super-admin may omit it to read across every organization, and must send it to write (400 without it);
- an organization the account does not belong to answers 403.
Identifiers and dates
Section titled “Identifiers and dates”- Resources are named in paths by their business key: the
referenceof an artwork, theidentityof an equipment item, thealiasof an organization. Monitorings and events have no other key than theirid, a UUID. Treat everyidas an opaque string. - Dates are ISO 8601 strings in UTC, such as
2026-09-23T10:00:00Z, in requests and responses.
Fields set by the API
Section titled “Fields set by the API”Fields marked read-only in the reference, such as id, created, updated or the status
of a monitoring, are set by the API. The organization of a resource is always the one the
request is made for.
2. Collections
Section titled “2. Collections”Request parameters
Section titled “Request parameters”| Parameter | Type | Default | Notes |
|---|---|---|---|
page |
integer, from 0 |
0 |
0-indexed |
size |
integer, 1 to 200 |
50 |
never the whole collection |
search |
string | none | free text; the fields it matches are documented per endpoint |
sort |
string | per endpoint | where the endpoint lists sortable fields, see below |
Filters are named, typed parameters declared per endpoint, such as status, tags,
type or inService, next to search. A filter that accepts several values is repeated:
status=ready&status=draft.
search matches anywhere in the documented fields, without regard to case. It is a
filter, not a ranking.
Sorting
Section titled “Sorting”sort takes a field name for ascending order, prefixed with - for descending order.
Several fields are separated by commas:
GET /artworks?sort=-updated,nameEach endpoint lists its sortable fields in its description in the reference, along with
its default order. For example, artworks sort on name, reference, status, created
and updated, newest first by default; the events of a monitoring sort on date, type,
severity and created, oldest first by default. A field that is not sortable answers
400.
The directory endpoints (/organizations, /users, and the members of an organization)
list no sortable field. They return their own order, and a caller that needs another
sorts on its side. Do not send sort to them.
Response
Section titled “Response”A list answers 200 with a JSON array at the root, without an envelope. Navigation is
in two headers.
Response to GET /artworks?status=ready&page=1&size=50, on 137 matching artworks:
HTTP/1.1 200 OKContent-Type: application/jsonX-Total-Count: 137Link: <?status=ready&page=0&size=50>; rel="first", <?status=ready&page=0&size=50>; rel="prev", <?status=ready&page=2&size=50>; rel="next", <?status=ready&page=2&size=50>; rel="last"
[ { "reference": "bayeux-tapestry", "...": "..." } ]Link(RFC 8288) is present on every list, withfirstalways,prevafter the first page,nextwhen a next page exists, andlastwhen the total is known. Every filter of the request is carried over.- The references are relative and carry only the query. Resolve them against the request
URL, for example with
new URL(ref, response.url). - The absence of
rel="next"marks the end of the collection. Do not infer the end from the length of the array. X-Total-Countis the number of items matching the filters. It is absent where counting is too costly, for instance on the members of an organization narrowed by a search;rel="last"is then absent too, and navigation rests onrel="next".
Counting
Section titled “Counting”GET <resource>/count takes the same search and filters as the list and answers an
object:
{ "count": 137 }It answers a business question, such as how many monitorings are under way. It is not needed to page through a list.
3. Writes
Section titled “3. Writes”- A creation answers 201 Created with a
Locationheader pointing at the new resource. Two exceptions keep 200 for now: the setup of a monitoring and the push of an event. They are listed in the migration guide. - An update (
PUT) cannot rename an artwork or an equipment item: areferenceor anidentityin the body that differs from the one in the path answers 400. - A deletion answers 204. Deleting something still in use answers 409 with a message
naming what holds it: an equipment item that appears in a monitoring, a packaging
assigned to an artwork, an artwork whose monitoring is set up or recording, or an
organization that still has members unless
forceis set. - A start or stop event sent twice is accepted and changes nothing. Other creations are not deduplicated: after a timeout, read the resource back before sending it again.
4. Errors
Section titled “4. Errors”Every error is an RFC 9457 problem detail, with
the application/problem+json content type. The human-readable message is in detail.
A rejected parameter carries a stable type and a parameter member naming the
offender:
{ "type": "https://api.smach.science/problems/invalid-parameter", "title": "Invalid request parameter", "status": 400, "detail": "size must be less than or equal to 200", "parameter": "size"}Rejected parameters include a negative page, a size out of bounds, a sort on a
field that is not sortable, and a value of the wrong type, such as an unknown status or an
id that is not a UUID.
The type URIs under https://api.smach.science/problems/ are part of the contract: a
caller may branch on them, and they do not change once published.
The artworks, monitorings, events and inventory endpoints also copy detail into a
message member, for the mobile application. Read detail: message will be withdrawn,
announced in the release notes beforehand.
| Status | Meaning |
|---|---|
| 400 | the request is invalid: a parameter, the body, or a missing X-Organization |
| 401 | no token, or a token the API does not accept |
| 403 | the account may not do this, or may not reach this organization |
| 404 | the resource does not exist in this organization |
| 409 | the request conflicts with the state of the data, such as an artwork that is not ready for a setup or an item in use |
| 502, 504 | sending an invitation or a password reset email failed or timed out; worth retrying |
5. Versions
Section titled “5. Versions”The address carries no version: https://api.smach.science/artworks. The contract
evolves compatibly, so a client keeps working as fields and endpoints are added. Should a
release ever need an incompatible change, a client will choose its version with an
optional Api-Version request header. Without it, a request is served as version 1, the
contract described here. There is nothing to send today.
6. One host
Section titled “6. One host”api.smach.science serves every resource, each path routed to the service that owns it,
and the API portal at its root. A client never needs to know which service answers. The
web application and external clients call the same endpoints, and roles and
organizations are enforced by the API itself.