Skip to content

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.

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.

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>

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.

{
"id": "5f3c2b1a-0e4d-4a7b-9c8d-1e2f3a4b5c6d",
"email": "curator@example.org",
"role": "administrator",
"organizations": { "bayeux": "Bayeux Museum" },
"roles": { "bayeux": "administrator" }
}
  • organizations maps each alias to its display name. Offer a choice when it holds more than one.
  • roles gives the role held in each organization. An organization where the account holds no role is in organizations and not in roles.
  • role is 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.

Terminal window
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, with first, prev, next and last relations. Follow next until 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:

Terminal window
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 (stationary or transport), config and at least one datalogger are required.
  • config is the configuration of the session, usually copied from the artwork and adjusted on site. A datalogger may carry its own config, which replaces the session’s for that datalogger alone.
  • A datalogger names a logger and a sensor by their identity, and optionally the gateway it reaches. Its position is relative to the artwork: latitude is the Y position, longitude the X position.
  • gateways applies to transports. The position of a gateway is optional. The labels Cabin or Cabine give cabin, trailer or Remorque give trailer, and any other value records the gateway without a position.
  • Do not send id, artwork nor status: 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 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.

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.

{
"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"
}
  • sensor is the captor that crossed its threshold: temperature, humidity or accelerometer. It is absent when the alert reports the state of the device itself, such as its battery or storage.
  • values holds one value for a temperature or a humidity, three axes for an accelerometer.
  • The web application reads message in two forms. For a temperature or a humidity: 25.5 > 22 or 12.3 < 15. For the accelerometer, raw values per axis, separated by commas: Y : -20000 < -16384 , Z : 20000 > 16384.

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.

  • A monitoring receives events while it is pending or in_progress.
  • A start or a stop sent twice is accepted and changes nothing, so a retry never loses the event it carries.
  • An ended monitoring 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)

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.

StaRT does not need these two endpoints, which the web application uses.

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: type and severity, which may be repeated (type=alert&type=position), sourceType, source, and sensor for alerts.
  • search matches the message of an alert.
  • sort accepts date, type, severity and created. A - prefix sorts descending, and fields are separated by commas.
GET /monitorings/0198c5a0-7c3e-7b1a-9d2f-4e6a8b0c1d2e/events?type=alert&sort=-date

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-Organization rules as the other endpoints.