StaRT mobile API workflow
This page is for the developer of the StaRT mobile application. It follows a monitoring from sign-in to stop, call by call. The full schemas of every request and response are 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 |
What changed in release 0.5 is listed in the migration guide.
The sequence
Section titled “The sequence”StaRT calls five endpoints:
| Step | Call | Effect |
|---|---|---|
| Sign-in | GET /me |
who the account is, its organizations and roles |
| Choose an artwork | GET /artworks?status=ready |
the artworks ready for a monitoring |
| Artwork detail | GET /artworks/{reference} |
its configuration and checklist |
| Setup | POST /artworks/{reference}/monitorings |
monitoring pending, artwork standby |
| Start, events, stop | POST /artworks/{reference}/monitorings/events |
monitoring in_progress, then ended |
Every date is an ISO 8601 string in UTC, such as 2026-09-23T10:00:00Z, in requests and
responses alike.
Authentication
Section titled “Authentication”The API takes an OAuth2 Bearer token issued by the SmaCH Keycloak, with the client
start.
| Environment | Realm | Token endpoint |
|---|---|---|
| Production | smach |
https://id.smach.science/realms/smach/protocol/openid-connect/token |
| Development | smach-dev |
https://id.smach.science/realms/smach-dev/protocol/openid-connect/token |
The authorization endpoint is the same address ending in /auth instead of /token. A
person signs in with the authorization code flow and PKCE.
Each API accepts the tokens of its own realm only: a production token sent to the development API gets a 401. Accounts are not shared between the two realms, so a development account is asked for separately.
StaRT requests the organization scope: the token carries the organization selected at
sign-in, with the role held in it. The other scope, organization:*, carries every
organization of the account with the role held in each.
Send the token on every call:
Authorization: Bearer <access token>Organization context
Section titled “Organization context”The X-Organization header names the organization a request applies to, by its alias
(bayeux), not its display name.
| Account | Without the header | With the header |
|---|---|---|
| Member of one organization | that organization | must name it |
| Member of several organizations | 400 | must name one of them |
| Member of no organization | 403 | 403 |
| Super-admin | reads across every organization; a write answers 400 | any organization |
Naming an organization the account does not belong to answers 403. Setup and events are writes, so a super-admin always sends the header.
Who is signed in: GET /me
Section titled “Who is signed in: GET /me”{ "id": "5f3c2b1a-0e4d-4a7b-9c8d-1e2f3a4b5c6d", "email": "curator@example.org", "role": "administrator", "organizations": { "bayeux": "Bayeux Museum" }, "roles": { "bayeux": "administrator" }}organizationsmaps each alias to its display name. Offer a choice when it holds more than one.rolesgives the role held in each organization. An organization where the account holds no role is inorganizationsand not inroles.roleis the highest realm-wide role:super-admin,administrator,viewer, or empty.
A super-admin belongs to no organization, so organizations is empty and role is
super-admin. Such an account reads the list from GET /organizations, which only
super-admins may call. There is no /me/organizations endpoint.
Setup and events require the administrator role in the organization, or a super-admin
account. A viewer gets a 403.
Choose an artwork
Section titled “Choose an artwork”curl "https://api.dev.smach.science/artworks?status=ready" \ -H "Authorization: Bearer $TOKEN" \ -H "X-Organization: bayeux"The response is a JSON array of artworks. A list returns 50 items by default, 200 at
most with size. Two headers carry the navigation:
Link, withfirst,prev,nextandlastrelations. Follownextuntil it is absent to read the whole list. Do not stop on a short page.X-Total-Count, the number of items matching the filters.
Then read one artwork by its reference, not its id:
curl "https://api.dev.smach.science/artworks/bayeux-tapestry" \ -H "Authorization: Bearer $TOKEN" \ -H "X-Organization: bayeux"It carries the config (thresholds and logger settings) the setup starts from, and the
checklist to go through on site.
POST /artworks/{reference}/monitorings opens a session on an artwork in ready status.
{ "type": "transport", "config": { "sensors": { "temperature": { "interval": 300, "min": 15, "max": 25 }, "humidity": { "interval": 300, "min": 40, "max": 60 }, "accelerometer": { "range": "2g", "xMin": -16384, "xMax": 16384, "yMin": -16384, "yMax": 16384, "zMin": -32767, "zMax": 32767 } }, "logger": { "battery": 15, "storage": 100, "frequency": 12 } }, "dataloggers": [ { "logger": "18", "sensor": "s12", "gateway": "01000001", "note": "Top right", "position": { "latitude": 3.23, "longitude": 2.12 } } ], "gateways": [ { "gateway": "01000001", "position": "cabin" } ]}type(stationaryortransport),configand at least one datalogger are required.configis the configuration of the session, usually copied from the artwork and adjusted on site. A datalogger may carry its ownconfig, which replaces the session’s for that datalogger alone.- A datalogger names a
loggerand asensorby their identity, and optionally thegatewayit reaches. Itspositionis relative to the artwork:latitudeis the Y position,longitudethe X position. gatewaysapplies to transports. Thepositionof a gateway is optional. The labelsCabinorCabinegivecabin,trailerorRemorquegivetrailer, and any other value records the gateway without a position.- Do not send
id,artworknorstatus: the API sets them.
Every logger, sensor and gateway named must exist in the inventory of the organization,
under the exact identity StaRT sends: a logger in decimal (18), a sensor as s
followed by two hexadecimal digits (s12), a gateway on eight digits (01000001).
8 and 08 are two different identities.
The answer is 200 with the monitoring, whose id is a UUID. Keep that id: the start
and stop events name it.
{ "id": "0198c5a0-7c3e-7b1a-9d2f-4e6a8b0c1d2e", "artwork": "bayeux-tapestry", "type": "transport", "status": "pending", "config": { "...": "..." }, "dataloggers": [ { "logger": "18", "sensor": "s12", "gateway": "01000001", "...": "..." } ], "gateways": [ { "gateway": "01000001", "position": "cabin" } ], "created": "2026-09-23T08:00:00Z", "updated": "2026-09-23T08:00:00Z"}The artwork moves from ready to standby.
| Status | When |
|---|---|
| 400 | invalid body, or equipment the inventory does not hold (the message names it) |
| 403 | the account is not an administrator of the organization |
| 404 | unknown artwork reference |
| 409 | the artwork is not ready (a monitoring is already set up or in progress, or the artwork is a draft, archived or cancelled) |
| 409 | a logger, sensor or gateway is engaged in another monitoring that is set up or in progress |
Accelerometer values
Section titled “Accelerometer values”Accelerometer thresholds and alert values are raw signed 16-bit integers, from -32768 to
32767. The range (2g, 4g, 8g, 16g, written without a sign) sets their scale. The
web application converts them with m/s2 = raw x factor x 9.81 / 1000:
| Range | Factor (mg per unit) | Full scale |
|---|---|---|
2g |
0.061 | about 19.6 m/s2 |
4g |
0.122 | about 39 m/s2 |
8g |
0.244 | about 78 m/s2 |
16g |
0.488 | about 157 m/s2 |
An axis whose thresholds are -32767 and 32767 is shown as disabled, which is how the vertical axis is kept from alerting on gravity.
Events
Section titled “Events”Start, stop and every measurement go through one endpoint:
POST /artworks/{reference}/monitorings/events. The answer is 200 with the recorded
event, which gains an id, the monitoring it was routed to, the artwork and its
created time.
Every event carries type, severity (info, warning, critical), date,
location, sourceType and source. The location object is required; a position
resolved from a cell may carry gsm instead of a latitude.
The sourceType and source decide which session receives the event:
sourceType |
source |
Session receiving the event | Used for |
|---|---|---|---|
monitoring |
the id returned by the setup |
that session | start, stop |
logger |
a logger identity, 18 |
the session set up or in progress that engages it | position |
sensor |
a sensor identity, s12 |
the session set up or in progress that engages it | alert |
user |
an email | the session set up or in progress on the artwork | notification |
{ "type": "start", "severity": "info", "date": "2026-09-23T10:00:00Z", "location": { "latitude": 49.276, "longitude": -0.702 }, "sourceType": "monitoring", "source": "0198c5a0-7c3e-7b1a-9d2f-4e6a8b0c1d2e"}The monitoring moves from pending to in_progress and the artwork from standby to
monitoring.
Position
Section titled “Position”{ "type": "position", "severity": "info", "date": "2026-09-23T12:00:00Z", "location": { "latitude": 49.443, "longitude": -1.266, "alt": 52 }, "sourceType": "logger", "source": "18", "gyroscope": { "x": 0.01, "y": -0.02, "z": 0.005 }, "accelerometer": { "x": 0.12, "y": -0.34, "z": 9.81 }, "speed": 22.5}gyroscope, accelerometer and speed (metres per second) are optional.
{ "type": "alert", "severity": "warning", "date": "2026-09-23T15:30:00Z", "location": { "latitude": 48.856, "longitude": 2.352 }, "sourceType": "sensor", "source": "s12", "sensor": "temperature", "values": [25.5], "message": "25.5 > 22"}sensoris the captor that crossed its threshold:temperature,humidityoraccelerometer. It is absent when the alert reports the state of the device itself, such as its battery or storage.valuesholds one value for a temperature or a humidity, three axes for an accelerometer.- The web application reads
messagein two forms. For a temperature or a humidity:25.5 > 22or12.3 < 15. For the accelerometer, raw values per axis, separated by commas:Y : -20000 < -16384 , Z : 20000 > 16384.
Notification
Section titled “Notification”A free message, with an optional tag:
{ "type": "notification", "severity": "info", "date": "2026-09-23T14:45:00Z", "location": { "latitude": 50.718, "longitude": -1.881 }, "sourceType": "user", "source": "curator@example.org", "message": "Rest stop, all systems nominal", "tag": "Road conditions"}The same body as the start, with "type": "stop". The monitoring moves to ended and the
artwork back to ready, ready for a new setup.
A stop sent while the monitoring is still pending ends it as well: the setup is
cancelled and the artwork returns to ready.
Lifecycle rules
Section titled “Lifecycle rules”- A monitoring receives events while it is
pendingorin_progress. - A start or a stop sent twice is accepted and changes nothing, so a retry never loses the event it carries.
- An
endedmonitoring is never restarted and records nothing more. Only a repeated stop is accepted, and it changes nothing.
| Status | When |
|---|---|
| 400 | invalid body, or a start or stop whose source is not a UUID |
| 403 | the account is not an administrator of the organization |
| 404 | no monitoring has the id given as source |
| 409 | no monitoring set up or in progress engages that logger or sensor, or the artwork has none (user source) |
| 409 | the session belongs to another artwork than the one in the path |
| 409 | the monitoring has ended (any event other than a stop) |
Errors
Section titled “Errors”An error is returned as an RFC 9457 problem, with the application/problem+json content
type. The text is in detail, and repeated in message for now:
{ "title": "Conflict", "status": 409, "detail": "events: the monitoring 0198c5a0-7c3e-7b1a-9d2f-4e6a8b0c1d2e of bayeux-tapestry has ended and records nothing more", "message": "events: the monitoring 0198c5a0-7c3e-7b1a-9d2f-4e6a8b0c1d2e of bayeux-tapestry has ended and records nothing more"}detail is the field of the contract. message will be withdrawn once StaRT reads
detail, announced in the release notes beforehand. The general format is described in
the API conventions.
Reading the events
Section titled “Reading the events”StaRT does not need these two endpoints, which the web application uses.
Timeline
Section titled “Timeline”GET /monitorings/{id}/events returns the events of one session, oldest first, paginated
like every list (50 by default, 200 at most, Link and X-Total-Count).
- Filters:
typeandseverity, which may be repeated (type=alert&type=position),sourceType,source, andsensorfor alerts. searchmatches the message of an alert.sortacceptsdate,type,severityandcreated. A-prefix sorts descending, and fields are separated by commas.
GET /monitorings/0198c5a0-7c3e-7b1a-9d2f-4e6a8b0c1d2e/events?type=alert&sort=-dateLive stream
Section titled “Live stream”GET /monitorings/events/stream, with Accept: text/event-stream, opens a Server-Sent
Events stream of every event the organization records, positions included, in the same
JSON form as the timeline. Each event arrives as a data: line.
- Alerts are limited per captor and per session, so a burst arrives as one alert per captor rather than hundreds. The timeline holds all of them.
- A comment line is sent down a quiet stream at regular intervals, to keep it open. A client never sees it as a message.
- The stream ends on its own after a while and is meant to be reopened.
- It follows the same
X-Organizationrules as the other endpoints.
See also
Section titled “See also”- User roles, for what each role may do.
- Managing organizations and users, for how accounts and roles are granted.
- API conventions, for pagination, sorting and errors.