Skip to content

Integration API

A versioned, read-only REST API under /integration/v1/ that lets external systems — hospital call systems, dashboards, middleware — retrieve live beacon locations and system alerts such as panic button presses.

Every SafeCall server self-hosts interactive, Swagger-UI-based documentation for this API (works offline, always matches the running server version):

  • Interactive docs: https://<your-server>/integration/docs
  • OpenAPI 3.1 spec: https://<your-server>/integration/v1/openapi.json

Source of truth: server/src/integration_http.ts (handlers), server/src/integration_openapi.ts (spec), server/src/api_access.ts (api-role allowlist).

Getting access

  1. An administrator creates an account with the api role (admin UI → Users, or safecall user --create).
  2. api accounts have no web interface and can only call the endpoints below — every other endpoint returns 403.
  3. They are system-wide: responses are not location-scoped.

Authentication

Standard JWT flow shared with the rest of the server:

bash
# 1. Log in — returns a short-lived JWT (~15 min) and a long-lived refresh token
curl -s https://<server>/login \
  -H "Content-Type: application/json" \
  -d '{"email": "integration@hospital.example", "password": "********"}'
# → payload.token, payload.refresh_token

# 2. Call the API with the JWT
curl -s https://<server>/integration/v1/beacons/locations \
  -H "Authorization: Bearer $TOKEN"

# 3. Before the JWT expires, exchange the refresh token for a fresh one
curl -s https://<server>/auth/refresh \
  -H "Content-Type: application/json" \
  -d '{"refresh_token": "'$REFRESH_TOKEN'"}'

Endpoints

MethodsPathPurpose
GET/integration/v1/beaconsActive trackable beacons (watch, panic, navigation, sos); optional ?type= filter
GET/integration/v1/beacons/locationsBulk snapshot: current zone, floor map, and last radio sighting per beacon
GET/integration/v1/beacons/:mac/locationSame for one beacon (MAC accepted in any separator format); 404 if unknown/untrackable
GET/integration/v1/alertsBeacon events incl. panic presses; filters: since, until, status, type, after_id, limit
GET/integration/v1/zonesZone directory for resolving zone ids to physical places
GET/integration/v1/openapi.jsonOpenAPI spec (no auth)
GET/integration/docsSwagger UI (no auth)

Conventions:

  • All timestamps are Unix epoch milliseconds (UTC).
  • Responses use the standard envelope { "type": "success" | "error", "message": string, "payload": { ... } }.
  • Locations are zone-based: zone is the live tracking prediction and is null until the tracking engine has observed the beacon (live state resets on server restart); last_seen (radio sighting) persists independently.

Polling for alerts

Alerts have monotonically increasing ids. Remember payload.next_after_id from each response and pass it as after_id on the next poll — results are then ordered oldest-first so the cursor only advances and no alert is missed:

bash
curl -s "https://<server>/integration/v1/alerts?after_id=$LAST_SEEN_ID" \
  -H "Authorization: Bearer $TOKEN"

Alert status semantics:

  • alert — button press (single click, double click, triple click, long push (hold)) on a panic or watch beacon
  • warning — presence/motion signals (not pushed, motion)
  • active — anything else

Extending the API

When adding an endpoint, keep three places in sync (the integration test suite enforces this):

  1. Handler in server/src/integration_http.ts + route in server/src/routes/http.ts
  2. Allowlist entry in server/src/api_access.ts (api-role accounts are denied otherwise)
  3. Path documentation in server/src/integration_openapi.ts

Tests: server/tests/api/integration.test.ts.