StaRT Mobile API - Complete Workflow Guide
For Mobile Developers (Jay)
This guide documents the complete API workflow for the StaRT mobile app for the January 8, 2025 beta test with the fake Bayeux Tapestry.
All examples use the v0.3.0 artwork-centric model with real API endpoints.
Base URL (Development): https://api.dev.smach.scienceOpenAPI Documentation: https://api.smach.science/swagger-ui/index.html
Overview
The StaRT mobile app manages the complete monitoring lifecycle for artwork transport:
Setup → Start → Monitoring → Stop → Upload → Analysis
📦 🚚 📍 🏁 📊 ✅Monitoring Lifecycle:
- Setup (Phase 1): Create monitoring →
pendingstatus - Start (Phase 2): Begin recording →
in_progressstatus - Monitoring (Phase 3): Push events (alerts, positions)
- Stop (Phase 4): End recording →
endedstatus - Upload (Phase 5): Upload datalogger data →
uploadedstatus (v1.0.0) - Analysis (Phase 6): Expert analysis →
completedstatus (v1.0.0)
Date/Time Format: Epoch Milliseconds
All date/time fields in the API (date, created, updated) are serialized as epoch milliseconds (Unix timestamp in ms), not ISO 8601 strings.
Example: 2025-01-08T10:00:00Z → 1736330400000
This applies to both request payloads and response bodies. The OpenAPI spec documents these fields as type: number, format: epoch-milliseconds.
Authentication
Get JWT Token from Keycloak
Endpoint: https://id.smach.science/realms/smach/protocol/openid-connect/token
Client ID: start (StaRT mobile app)
Organization Scopes
StaRT uses OAuth2 scopes to control organization information in JWT tokens:
organization: User selects one organization at login (Keycloak displays selector if multi-org)organization:*: Token contains all user organizations (no login selector)
For StaRT mobile, organization scope is recommended (explicit organization selection).
⚠️ Important:
- You cannot use both scopes simultaneously (choose one)
- Without a scope, no organization information in token
- With
organizationscope, user can only access resources from the selected organization (even if they belong to multiple orgs)
Request with organization scope (Recommended for StaRT):
curl -X POST "https://id.smach.science/realms/smach/protocol/openid-connect/token" \
-d "client_id=start" \
-d "username=admin@bayeux.test" \
-d "password=PASSWORD" \
-d "grant_type=password" \
-d "scope=openid organization"Response:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI...",
"expires_in": 300,
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI...",
"token_type": "Bearer",
"scope": "openid organization"
}Decoded JWT Token (with organization scope):
{
"sub": "52673b28-ee1f-4d34-bb70-85137d4de6bd",
"email": "admin@bayeux.test",
"organization": {
"bayeux": {
"name": ["Bayeux Museum"]
}
},
"realm_access": {
"roles": ["administrator"]
}
}Alternative: Request with organization:* scope (All Organizations):
curl -X POST "https://id.smach.science/realms/smach/protocol/openid-connect/token" \
-d "client_id=start" \
-d "username=admin@bayeux.test" \
-d "password=PASSWORD" \
-d "grant_type=password" \
-d "scope=openid organization:*"Decoded JWT Token (with organization:* scope):
{
"sub": "52673b28-ee1f-4d34-bb70-85137d4de6bd",
"email": "admin@bayeux.test",
"organizations": {
"bayeux": {
"name": ["Bayeux Museum"]
}
},
"realm_access": {
"roles": ["administrator"]
}
}Scope Differences
| Scope | Login UX | Token Claim | Use Case |
|---|---|---|---|
organization | Selector at login (if multi-org) | "organization": {"bayeux": {...}} | Mobile apps - explicit org selection |
organization:* | No selector | "organizations": {"bayeux": {...}} | Web apps - dynamic org switching |
See Organization Management for complete documentation.
Use token in all API requests:
export TOKEN="eyJhbGciOiJSUzI1NiIsInR5cCI..."Organization Header Required
All API requests (except authentication) must include:
-H "X-Organization: bayeux"For single-organization users, this header is optional (auto-resolved from JWT). For super-admins and multi-org users, this header is mandatory.
Phase 0: Authentication & Organization Selection
0.1 Get User's Organizations (v1.0.0 - multi-org support)
For super-admin users who can access multiple organizations:
curl -X GET "https://api.dev.smach.science/me/organizations" \
-H "Authorization: Bearer $TOKEN"Response:
["bayeux", "louvre", "smach"]For v0.3.0
In the current beta, focus on single organization users (admin@bayeux.test). Multi-organization selection will be needed for super.admin@smach.science in v1.0.0.
Phase 1: Setup - Create Monitoring
1.1 List Artworks Ready for Setup
Get artworks with status ready (no active monitoring):
curl -X GET "https://api.dev.smach.science/artworks?status=ready" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Organization: bayeux"Response:
[
{
"id": "674ebf66cf6f5c456e1e27a8",
"organization": "bayeux",
"tags": ["Medieval Art", "Tapestry", "11th Century", "Norman Conquest"],
"reference": "bayeux-tapestry-replica",
"name": "Bayeux Tapestry (Test Replica)",
"status": "ready",
"inclosure": "INC001",
"crate": "CRT001",
"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": -16384,
"zMax": 16384
}
},
"logger": {
"battery": 15,
"storage": 100,
"frequency": 12
}
},
"checklist": [
{
"title": "Verify environmental seals",
"description": "Check all seals are intact"
},
{
"title": "Activate dataloggers",
"description": "Ensure all loggers are powered"
},
{
"title": "Test sensor connectivity",
"description": "Verify sensors are reporting"
},
{
"title": "Document initial conditions",
"description": "Record temp/humidity before packaging"
}
]
}
]1.2 Get Artwork Details
Using Reference Instead of ID
The API uses the artwork reference (URL-safe slug like bayeux-tapestry-replica) in path parameters, not the MongoDB ObjectId.
curl -X GET "https://api.dev.smach.science/artworks/bayeux-tapestry-replica" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Organization: bayeux"1.3 Get Available Inventory Items
List available loggers:
curl -X GET "https://api.dev.smach.science/inventories/loggers" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Organization: bayeux"Response:
[
{
"id": "...",
"identity": "IMEI-LOGGER-001",
"organization": "bayeux",
"name": "Datalogger 001",
"model": "AES Techno v1.0.0"
},
{
"id": "...",
"identity": "IMEI-LOGGER-002",
"organization": "bayeux",
"name": "Datalogger 002",
"model": "AES Techno v1.1.2"
}
]List available sensors:
curl -X GET "https://api.dev.smach.science/inventories/sensors" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Organization: bayeux"Response:
[
{
"id": "...",
"identity": "IMEI-SENSOR-001",
"organization": "bayeux",
"kinds": ["temperature", "humidity", "accelerometer"],
"name": "3-sensor probe (T+H+A)",
"model": "HDC3021 + IIS3DWB"
}
]List available gateways (v0.4.0):
curl -X GET "https://api.dev.smach.science/inventories/gateways" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Organization: bayeux"Response:
[
{
"id": "...",
"identity": "GW-001",
"organization": "bayeux",
"name": "Gateway Cabin",
"description": "AES Techno GW v1.0"
},
{
"id": "...",
"identity": "GW-002",
"organization": "bayeux",
"name": "Gateway Trailer",
"description": "AES Techno GW v1.0"
}
]1.4 Create Monitoring (Setup)
Pre-Setup Tasks in App
Before calling this endpoint:
- ✅ User completes checklist items in UI
- ✅ App auto-detects dataloggers via Bluetooth
- ✅ App displays detected Logger+Sensor pairs
- ✅ (Optional) User adjusts configuration thresholds
- ✅ Get current GPS coordinates
Endpoint: POST /artworks/{reference}/monitorings
Per-Datalogger Configuration Override
Each datalogger can optionally have its own config that overrides Monitoring.config. If config is null, the datalogger inherits the monitoring-level base configuration.
Configuration inheritance chain: Artwork.config → Monitoring.config → Datalogger.config
Request Body:
{
"organization": "bayeux",
"artwork": "bayeux-tapestry-replica",
"type": "transport",
"status": "pending",
"gateways": [
{
"gateway": "GW-001",
"position": "cabin"
},
{
"gateway": "GW-002",
"position": "trailer"
}
],
"dataloggers": [
{
"logger": "IMEI-LOGGER-001",
"sensor": "IMEI-SENSOR-001",
"note": "Top right",
"position": {
"latitude": 3.23,
"longitude": 2.12
},
"gateway": "GW-001"
},
{
"logger": "IMEI-LOGGER-002",
"sensor": "IMEI-SENSOR-002",
"note": "Center - sensitive zone",
"position": {
"latitude": 0.0,
"longitude": 0.0
},
"gateway": "GW-002",
"config": {
"sensors": {
"temperature": {
"interval": 60,
"min": 19,
"max": 21
},
"humidity": {
"interval": 60,
"min": 48,
"max": 52
},
"accelerometer": {
"range": "2g",
"xMin": -8192,
"xMax": 8192,
"yMin": -8192,
"yMax": 8192,
"zMin": -8192,
"zMax": 8192
}
},
"logger": {
"battery": 15,
"storage": 100,
"frequency": 60
}
}
},
{
"logger": "IMEI-LOGGER-003",
"sensor": "IMEI-SENSOR-003",
"note": "Bottom left",
"position": {
"latitude": -3.23,
"longitude": -2.12
},
"gateway": "GW-002"
}
],
"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": -16384,
"zMax": 16384
}
},
"logger": {
"battery": 15,
"storage": 100,
"frequency": 12
}
}
}cURL Example:
curl -X POST "https://api.dev.smach.science/artworks/bayeux-tapestry-replica/monitorings" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Organization: bayeux" \
-H "Content-Type: application/json" \
-d '{
"organization": "bayeux",
"artwork": "bayeux-tapestry-replica",
"type": "transport",
"status": "pending",
"gateways": [
{"gateway": "GW-001", "position": "cabin"},
{"gateway": "GW-002", "position": "trailer"}
],
"dataloggers": [
{
"logger": "IMEI-LOGGER-001",
"sensor": "IMEI-SENSOR-001",
"note": "Top right",
"position": {"latitude": 3.23, "longitude": 2.12},
"gateway": "GW-001"
}
],
"config": {...}
}'Response:
{
"id": "674ec123cf6f5c456e1e27b9",
"organization": "bayeux",
"artwork": "bayeux-tapestry-replica",
"type": "transport",
"status": "pending",
"gateways": [
{"gateway": "GW-001", "position": "cabin"},
{"gateway": "GW-002", "position": "trailer"}
],
"dataloggers": [
{
"logger": "IMEI-LOGGER-001",
"sensor": "IMEI-SENSOR-001",
"note": "Top right",
"position": {"latitude": 3.23, "longitude": 2.12},
"config": null,
"gateway": "GW-001"
},
{
"logger": "IMEI-LOGGER-002",
"sensor": "IMEI-SENSOR-002",
"note": "Center - sensitive zone",
"position": {"latitude": 0.0, "longitude": 0.0},
"config": {"sensors": {"temperature": {"interval": 60, "min": 19, "max": 21}, "...": "..."}, "logger": {"battery": 15, "storage": 100, "frequency": 60}},
"gateway": "GW-002"
}
],
"config": {...},
"created": 1736323200000,
"updated": 1736323200000
}What happens:
- ✅ Monitoring created with
pendingstatus - ✅ Artwork status updated:
ready→standby - ✅ Dataloggers marked as
IN_USEin inventory - ✅ Configuration copied from
artwork.baseConfig - ✅ StaRT stores
monitoring.idin session for next steps
Phase 2: Start - Begin Recording
When to Start
The START event should be triggered when:
- ✅ All checklist items completed
- ✅ Dataloggers installed and configured via Bluetooth
- ✅ Carrier is ready to take charge of the artwork
2.1 Start Recording - POST Start Event
Endpoint: POST /artworks/{reference}/monitorings/events
Request Body:
{
"type": "start",
"severity": "info",
"date": 1736330400000,
"location": {
"latitude": 49.276,
"longitude": -0.702
},
"sourceType": "monitoring",
"source": "674ec123cf6f5c456e1e27b9"
}cURL Example:
curl -X POST "https://api.dev.smach.science/artworks/bayeux-tapestry-replica/monitorings/events" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Organization: bayeux" \
-H "Content-Type: application/json" \
-d '{
"type": "start",
"severity": "info",
"date": 1736330400000,
"location": {
"latitude": 49.276,
"longitude": -0.702
},
"sourceType": "monitoring",
"source": "674ec123cf6f5c456e1e27b9"
}'Response:
{
"id": "674ec200...",
"organization": "bayeux",
"artwork": "bayeux-tapestry-replica",
"monitoring": "674ec123cf6f5c456e1e27b9",
"type": "start",
"severity": "info",
"date": 1736330400000,
"location": {
"latitude": 49.276,
"longitude": -0.702
},
"sourceType": "monitoring",
"source": "674ec123cf6f5c456e1e27b9"
}What happens:
- ✅ Event saved with
starttype - ✅ Monitoring status updated:
pending→in_progress - ✅ Artwork status updated:
standby→monitoring - ✅ Recording officially started
- ✅ SSE notification sent to web app users
- ✅ StaRT sends Bluetooth command to start dataloggers
Phase 3: Monitoring - Push Events During Journey
While the monitoring is in_progress, push events for tracking:
3.1 Position Event (GPS Tracking)
Frequency: Every 5-15 minutes (configurable via loggerConfig.frequency)
Source-Based Routing
StaRT doesn't need to track monitoring IDs for position events! Backend automatically finds the active monitoring using the logger IMEI.
Request Body:
{
"type": "position",
"severity": "info",
"date": 1736337600000,
"location": {
"latitude": 49.443,
"longitude": -1.266
},
"sourceType": "logger",
"source": "IMEI-LOGGER-001",
"gyroscope": {"x": 0.01, "y": -0.02, "z": 0.005},
"accelerometer": {"x": 0.12, "y": -0.34, "z": 9.81},
"speed": 22.5
}Telemetry Fields (v0.4.0)
Position events can optionally include real-time telemetry from StaRT:
gyroscope: 3-axis rotation rate in degrees/s ({x, y, z})accelerometer: 3-axis acceleration in m/s² ({x, y, z})speed: Current speed in m/s
These fields are optional and only sent when the StaRT app has access to device sensors.
cURL Example:
curl -X POST "https://api.dev.smach.science/artworks/bayeux-tapestry-replica/monitorings/events" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Organization: bayeux" \
-H "Content-Type: application/json" \
-d '{
"type": "position",
"severity": "info",
"date": 1736337600000,
"location": {
"latitude": 49.443,
"longitude": -1.266
},
"sourceType": "logger",
"source": "IMEI-LOGGER-001"
}'Response: 200 OK (event saved, monitoring unchanged)
Position Events NOT Streamed via SSE
Position events are NOT included in SSE streaming (too frequent). Use REST polling (GET /monitorings/{id}/events) to fetch position history.
3.2 Alert Event (Threshold Violation)
When a sensor detects a threshold breach:
Source-Based Routing
Backend automatically finds the active monitoring using the sensor IMEI.
Request Body:
{
"type": "alert",
"severity": "warning",
"date": 1736350200000,
"location": {
"latitude": 48.856,
"longitude": 2.352
},
"sourceType": "sensor",
"source": "IMEI-SENSOR-002",
"sensor": "temperature",
"values": [25.5],
"message": "25.5 > 22"
}Alert Decomposition (v0.4.0)
Alert events now include structured telemetry fields:
sensor: Sensor type that triggered the alert (temperature,humidity,accelerometer)values: Sensor values as a flexible-size list (e.g.,[25.5]for temperature,[-5000, 0, 5000]for 3-axis accelerometer in raw int16)- The legacy
valuefield is replaced bysensor+valuesfor richer alert data.
cURL Example:
curl -X POST "https://api.dev.smach.science/artworks/bayeux-tapestry-replica/monitorings/events" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Organization: bayeux" \
-H "Content-Type: application/json" \
-d '{
"type": "alert",
"severity": "warning",
"date": 1736350200000,
"location": {
"latitude": 48.856,
"longitude": 2.352
},
"sourceType": "sensor",
"source": "IMEI-SENSOR-002",
"sensor": "temperature",
"values": [25.5],
"message": "25.5 > 22"
}'Response: 200 OK (alert saved, SSE notification sent to web app)
Alert Message Convention (agreed with Jay)
Alert event message follows a standardized format:
Temperature / Humidity (single value):
[measured_value] [> | <] [configured_threshold]Examples: 25.5 > 22 (temperature exceeded max), 12.3 < 15 (temperature below min), 85.97 > 81 (humidity exceeded max)
Accelerometer (multi-axis, raw int16 values):
[X | Y | Z] : [measured_value] [< | >] [configured_threshold][, repeated if multiple axes]Examples: Y : -20000 < -16384 (Y-axis below threshold), Y : -20000 < -16384 , Z : 20000 > 16384 (two axes)
The frontend parses these raw messages and displays translated, human-readable text with converted m/s² values (see Sensor Data Reference).
Accelerometer Alert Example:
{
"type": "alert",
"severity": "warning",
"date": 1736351000000,
"location": {
"latitude": 48.900,
"longitude": 2.100
},
"sourceType": "sensor",
"source": "IMEI-SENSOR-002",
"sensor": "accelerometer",
"values": [-20000, 0, 20000],
"message": "Y : -20000 < -16384 , Z : 20000 > 16384"
}3.3 Accelerometer Data Format
Raw int16 Storage
Accelerometer thresholds and values are stored as raw int16 in the API (range: -32768 to 32767). This is the same format exchanged between StaRT, the gateway, and the dataloggers.
The frontend converts raw values to m/s² for display using:
value_ms2 = raw_value × scaleFactor × 9.81 × 10⁻³| Range | Scale Factor (mg/LSB) | Max Amplitude (m/s²) |
|---|---|---|
| ±2g | 0.061 | ±19 m/s² |
| ±4g | 0.122 | ±39 m/s² |
| ±8g | 0.244 | ±78 m/s² |
| ±16g | 0.488 | ±156 m/s² |
Default configuration (±2g range): thresholds at ±16384 raw ≈ ±10 m/s²
Axis Disabled State
An axis can be disabled (no alerts triggered) by setting its thresholds to the maximum range values:
min = -32767,max = 32767
This is useful when an axis is subject to Earth's gravity (e.g., vertical axis) to avoid continuous alerts. The frontend detects this state and displays the axis as "disabled" rather than showing extreme threshold values.
Configuration example (±2g range, ~10 m/s² thresholds, Z-axis disabled):
{
"accelerometer": {
"range": "2g",
"xMin": -16384,
"xMax": 16384,
"yMin": -16384,
"yMax": 16384,
"zMin": -32767,
"zMax": 32767
}
}For full conversion formulas and display rules, see the Sensor Data Reference.
3.4 Notification Event (Manual Message) (v1.0.0)
For manual messages from the carrier/operator:
Request Body:
{
"type": "notification",
"severity": "info",
"date": 1736347500000,
"location": {
"latitude": 50.718,
"longitude": -1.881
},
"sourceType": "user",
"source": "admin@bayeux.test",
"message": "Rest stop for lunch - All systems nominal",
"tag": "Road conditions"
}Response: 200 OK (notification saved, SSE broadcast to organization)
Phase 4: Stop - End Recording
4.1 Stop Recording - POST Stop Event
Cannot Restart After Stop
Once a monitoring is stopped (ended status), it CANNOT be restarted. This is a business rule to ensure monitoring session immutability.
Endpoint: POST /artworks/{reference}/monitorings/events
Request Body:
{
"type": "stop",
"severity": "info",
"date": 1736373600000,
"location": {
"latitude": 51.507,
"longitude": -0.127
},
"sourceType": "monitoring",
"source": "674ec123cf6f5c456e1e27b9"
}cURL Example:
curl -X POST "https://api.dev.smach.science/artworks/bayeux-tapestry-replica/monitorings/events" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Organization: bayeux" \
-H "Content-Type: application/json" \
-d '{
"type": "stop",
"severity": "info",
"date": 1736373600000,
"location": {
"latitude": 51.507,
"longitude": -0.127
},
"sourceType": "monitoring",
"source": "674ec123cf6f5c456e1e27b9"
}'Response:
{
"id": "674ec204...",
"organization": "bayeux",
"artwork": "bayeux-tapestry-replica",
"monitoring": "674ec123cf6f5c456e1e27b9",
"type": "stop",
"severity": "info",
"date": 1736373600000,
"location": {
"latitude": 51.507,
"longitude": -0.127
},
"sourceType": "monitoring",
"source": "674ec123cf6f5c456e1e27b9"
}What happens:
- ✅ Event saved with
stoptype - ✅ Monitoring status updated:
in_progress→ended - ✅ Artwork status updated:
monitoring→ready - ✅ Monitoring session closed (immutable, cannot restart)
- ✅ Dataloggers marked as
AVAILABLEin inventory - ✅ SSE notification sent to web app users
- ✅ StaRT sends Bluetooth command to stop dataloggers
- ✅ Artwork ready for new monitoring session
Post-Stop Actions
- Take photos AFTER pressing STOP
- Upload photos to S3 separately (v1.0.0)
- Later: Upload datalogger data via USB (v1.0.0)
Phase 5: Query Events (Timeline)
5.1 Get All Events for Monitoring
curl -X GET "https://api.dev.smach.science/monitorings/674ec123cf6f5c456e1e27b9/events" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Organization: bayeux"Event Filters & Pagination (v0.4.0)
The events endpoint supports advanced filtering and pagination:
Filters:
?type=alertor?type=alert&type=position— Filter by event type(s)?severity=warning&severity=critical— Filter by severity level(s)?sourceType=sensor— Filter by source type?source=IMEI-SENSOR-002— Filter by source identifier?sensor=temperature— Filter by sensor type (for alert events)
Pagination & Sorting:
?page=0&size=100— Paginate results (default: page 0, size 100)?sort=date,desc— Sort by field (default:date,ascfor timeline order)
Examples:
# Only alerts, newest first
GET /monitorings/{id}/events?type=alert&sort=date,desc
# Temperature alerts only
GET /monitorings/{id}/events?type=alert&sensor=temperature
# Paginated timeline (first 50 events)
GET /monitorings/{id}/events?page=0&size=50Response:
[
{
"id": "674ec200...",
"type": "start",
"date": 1736330400000,
"location": {"latitude": 49.276, "longitude": -0.702}
},
{
"id": "674ec201...",
"type": "position",
"date": 1736337600000,
"location": {"latitude": 49.443, "longitude": -1.266}
},
{
"id": "674ec202...",
"type": "alert",
"severity": "warning",
"date": 1736350200000,
"location": {"latitude": 48.856, "longitude": 2.352},
"sensor": "temperature",
"values": [25.5],
"message": "25.5 > 22"
},
{
"id": "674ec203...",
"type": "stop",
"date": 1736373600000,
"location": {"latitude": 51.507, "longitude": -0.127}
}
]5.2 Real-Time Event Stream (SSE)
For real-time monitoring in the web app (and optionally StaRT):
curl -X GET "https://api.dev.smach.science/monitorings/events/stream" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Organization: bayeux" \
-H "Accept: text/event-stream"Event Types Streamed:
- ✅
start- Monitoring started - ✅
alert- Threshold violation - ✅
notification- User message (v1.0.0) - ✅
stop- Monitoring stopped - ❌
position- NOT streamed (too frequent, use REST polling)
Example SSE Message:
data: {"type":"alert","severity":"warning","date":1736350200000, ...}
data: {"type":"stop","severity":"info","date":1736373600000, ...}Heartbeat: Empty SSE every 30s to keep connection alive
Error Handling
Common Errors
1. No Active Monitoring Found
{
"status": 404,
"error": "No active monitoring found for artwork bayeux-tapestry-replica"
}Solution: Create monitoring with POST /artworks/{reference}/monitorings first
2. Artwork Already Has Active Monitoring
{
"status": 400,
"error": "Artwork bayeux-tapestry-replica already has an active monitoring"
}Solution: Complete current monitoring before creating a new one
3. Cannot Restart Ended Monitoring
{
"status": 400,
"error": "Monitoring 674ec123cf6f5c456e1e27b9 has status Ended and cannot be restarted"
}Solution: Create a new monitoring session
4. Invalid Source for Event Type
{
"status": 400,
"error": "alert events must have sourceType=sensor"
}Solution: Use correct sourceType for event type (see table below)
5. Sensor Not Associated with Active Monitoring
{
"status": 404,
"error": "No active monitoring found for sensor IMEI-SENSOR-002"
}Solution: Verify sensor IMEI is correctly associated with monitoring during Setup
6. Missing X-Organization Header
{
"status": 400,
"error": "X-Organization header required for multi-organization users"
}Solution: Include X-Organization: bayeux header
Event Types & Source Routing
| Event Type | sourceType | source | When to Send |
|---|---|---|---|
start | monitoring | monitoring-uuid | Once: Begin recording |
alert | sensor | IMEI-SENSOR-xxx | On threshold violation |
position | logger | IMEI-LOGGER-xxx | Every 5-15 min during journey |
notification | user | email@domain.test | Manual user message (v1.0.0) |
stop | monitoring | monitoring-uuid | Once: End recording |
Source-Based Routing Benefits:
- StaRT doesn't need to track monitoring IDs for sensor/logger events
- Backend automatically finds active monitoring using device IMEI
- Simpler mobile app logic
Status Summary
Monitoring Status
| Status | Description | Artwork Status | Can Send Events | Next Actions |
|---|---|---|---|---|
pending | Setup complete, awaiting START | standby | ✅ Only start | Press START button |
in_progress | Recording in progress | monitoring | ✅ position, alert, notification, stop | Continue journey or press STOP |
ended | Recording stopped | ready | ❌ None | Upload data (v1.0.0) |
uploaded (v1.0.0) | Data uploaded to S3 | analysing | ❌ None | Expert analysis |
completed (v1.0.0) | Expert analysis done | ready/archived | ❌ None | Create new monitoring |
Artwork Status
| Status | Description | Monitoring Status | Next Actions |
|---|---|---|---|
draft | Being configured | None | Configure artwork |
ready | Configured, no active monitoring | None | Create monitoring (Setup) |
standby | Monitoring created, awaiting START | pending | Press START |
monitoring | Active monitoring session | in_progress | Continue or press STOP |
analysing (v1.0.0) | Data uploaded, awaiting analysis | uploaded | Expert analysis |
archived | No longer monitored | completed | Archive |
Complete Example Workflow
# 0. Authentication (with organization scope)
TOKEN=$(curl -X POST "https://id.smach.science/realms/smach/protocol/openid-connect/token" \
-d "client_id=start" \
-d "username=admin@bayeux.test" \
-d "password=PASSWORD" \
-d "grant_type=password" \
-d "scope=openid organization" | jq -r '.access_token')
# 1. List artworks ready for setup
curl -X GET "https://api.dev.smach.science/artworks?status=ready" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Organization: bayeux"
# 2. Setup - Create monitoring (with gateways v0.4.0)
curl -X POST "https://api.dev.smach.science/artworks/bayeux-tapestry-replica/monitorings" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Organization: bayeux" \
-H "Content-Type: application/json" \
-d '{
"organization": "bayeux",
"artwork": "bayeux-tapestry-replica",
"type": "transport",
"status": "pending",
"gateways": [{"gateway": "GW-001", "position": "cabin"}],
"dataloggers": [...],
"config": {...}
}'
# Store monitoring ID
MONITORING_ID="674ec123cf6f5c456e1e27b9"
# 3. Start - Begin recording
curl -X POST "https://api.dev.smach.science/artworks/bayeux-tapestry-replica/monitorings/events" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Organization: bayeux" \
-H "Content-Type: application/json" \
-d '{
"type": "start",
"severity": "info",
"date": 1736330400000,
"location": {"latitude": 49.276, "longitude": -0.702},
"sourceType": "monitoring",
"source": "'"$MONITORING_ID"'"
}'
# 4. Position - During journey
curl -X POST "https://api.dev.smach.science/artworks/bayeux-tapestry-replica/monitorings/events" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Organization: bayeux" \
-H "Content-Type: application/json" \
-d '{
"type": "position",
"severity": "info",
"date": 1736337600000,
"location": {"latitude": 49.443, "longitude": -1.266},
"sourceType": "logger",
"source": "IMEI-LOGGER-001"
}'
# 5. Alert - Threshold exceeded
curl -X POST "https://api.dev.smach.science/artworks/bayeux-tapestry-replica/monitorings/events" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Organization: bayeux" \
-H "Content-Type: application/json" \
-d '{
"type": "alert",
"severity": "warning",
"date": 1736350200000,
"location": {"latitude": 48.856, "longitude": 2.352},
"sourceType": "sensor",
"source": "IMEI-SENSOR-002",
"sensor": "temperature",
"values": [25.5],
"message": "25.5 > 22"
}'
# 6. Stop - Arrival
curl -X POST "https://api.dev.smach.science/artworks/bayeux-tapestry-replica/monitorings/events" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Organization: bayeux" \
-H "Content-Type: application/json" \
-d '{
"type": "stop",
"severity": "info",
"date": 1736373600000,
"location": {"latitude": 51.507, "longitude": -0.127},
"sourceType": "monitoring",
"source": "'"$MONITORING_ID"'"
}'
# 7. Query events timeline
curl -X GET "https://api.dev.smach.science/monitorings/$MONITORING_ID/events" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Organization: bayeux"Next Steps for Jay
- ✅ Review this workflow and validate against StaRT mobile app architecture
- ✅ Test authentication with
admin@bayeux.testcredentials - ✅ Implement Bluetooth detection for Logger+Sensor pairs
- ✅ Test Setup endpoint (
POST /artworks/{reference}/monitorings) - ✅ Test event endpoints (Start, Position, Alert, Stop)
- ✅ Integrate SSE streaming for real-time alerts (optional for mobile)
- ✅ Test with Bayeux fixture data (see bayeux-test-dataset)
Meeting: Monday, December 9, 2024 - API review with Jay
Beta Test: January 8, 2025 - Fake Bayeux Tapestry transport to London
Additional Resources
- OpenAPI Documentation: https://api.smach.science/swagger-ui/index.html
- Test Dataset: Bayeux Test Dataset
- User Roles: User Roles & Permissions
- Organization Management: Organization Management