Skip to content

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

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.
  • Resources are named in paths by their business key: the reference of an artwork, the identity of an equipment item, the alias of an organization. Monitorings and events have no other key than their id, a UUID. Treat every id as an opaque string.
  • Dates are ISO 8601 strings in UTC, such as 2026-09-23T10:00:00Z, in requests and responses.

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.

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.

sort takes a field name for ascending order, prefixed with - for descending order. Several fields are separated by commas:

GET /artworks?sort=-updated,name

Each 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.

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 OK
Content-Type: application/json
X-Total-Count: 137
Link: <?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, with first always, prev after the first page, next when a next page exists, and last when 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-Count is 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 on rel="next".

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.

  • A creation answers 201 Created with a Location header 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: a reference or an identity in 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 force is 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.

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

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.

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.