All API reference pages

Device tokens, public links, and ingest

Three credential surfaces that aren't user sessions: device tokens for boards and gateways, capability tokens in public URLs, and API-key-authenticated ingest.

Device tokens

Boards, kiosks, and sensor gateways are devices — non-user identities with their own long-lived token. Admins manage them:

GET/POST /orgs/{orgId}/devices                 (+ PUT /{id})     — admin
POST     /orgs/{orgId}/devices/{id}/pair       — mint a short-lived 6-char pairing code
POST     /orgs/{orgId}/devices/{id}/cast       — push content (e.g. a recipe) to the screen

The screen claims the code (rate-limited, 10/min/IP):

POST /devices/pair
{ "code": "K7M2QX" }

The response contains the device token (mdt_…) — shown once, stored hashed. The device then authenticates with the x-device-token header:

GET /device/state
x-device-token: mdt_...
{
  "device": { "id": "51f8a234-…", "name": "Hot line wallboard", "kind": "wallboard_boh", "config": {} },
  "org": { "name": "Riverbend Senior Living", "brand": { "primary": "#283040", "accent": "#F36744" } },
  "site": { "id": "9b80fa65-…", "name": "Riverbend — Columbus", "timezone": "America/New_York" },
  "date": "2026-08-08",
  "serverTime": "2026-08-08T20:39:12.413Z",
  "cast": null,
  "production": [ ... ],
  "alerts": [ { "kind": "task_missed", "severity": "warning", "message": "Missed: …" } ],
  "handoffs": []
}

The payload adapts to the device kind — production timers and alerts for kitchen wallboards, the day-part menu for menu boards, the tray queue for tray-line screens. date is computed in the site's timezone.

Realtime: devices may also open a WebSocket at GET /board/ws?token=<device token>. Any received frame means "refetch /device/state now" — frames are hints, not data, and boards must keep working on polling alone if the socket drops. Board-side images are served via GET /board/media/content/{key} with the device token (the regular media routes require a user session).

Device counts meter against the plan — pairing beyond the cap returns the 402 shape with feature: "device_limit".

Some URLs are themselves the credential — sanitized, rate-limited, no login:

GET  /survey/{token}               — a survey invite (questions + branding)
POST /survey/{token}/submit        — submit answers (validated server-side)
GET  /share/q/{token}              — a shared catering quote
GET  /share/i/{token}              — a shared catering invoice
GET  /share/insp/{token}           — a shared inspection report

Responses expose display data only — never organization ids or internal linkage. Treat these tokens as bearer capabilities: anyone with the URL can view, and reissuing a share rotates the token. Public surfaces are rate-limited per IP (see rate limits).

There is also a public support endpoint used by this site's contact form:

POST /support-inquiry              — {name, email, org?, topic, message} → 202

Printed equipment and space tags encode a web deep link, not a token:

https://miseline.keyedsystems.com/scan/{tagCode}

Unlike the public token links above, a tag code is not a credential — it identifies a target but grants nothing. Opening the link signed out bounces through sign-in with the scan target preserved; resolving it requires membership in the organization that owns the tag.

The API behind the page is:

GET /orgs/{orgId}/scan/{tagCode}

Unauthenticated calls return a machine-usable 401 carrying the scan reference, so a client can complete sign-in and resume the exact scan:

{ "error": "Authentication required", "statusCode": 401, "scan": { "tagCode": "GA-7C0A41BD", "orgId": "…" } }

The body is identical whether or not the tag exists — the endpoint never confirms a tag's existence without authentication. Authenticated responses return the resolved target with a role-aware action list and context; see the scan overview for the family.

HL7 ADT ingest

EHR/census systems feed the clinical census over HL7 v2 ADT:

POST /orgs/{orgId}/adt/hl7
x-api-key: mk_live_...
content-type: application/json
{
  "siteId": "cb35f458-366f-4aff-916c-cce0674d9421",
  "message": "MSH|^~\\&|ADT|Hospital|Miseline|Dietary|20260808120000||ADT^A01|MSG00042|P|2.5\nPID|1||MRN-9042^^^MRN||Doe^Jane||19510402|F\nPV1|1|I|3W^302^A\nAL1|1|FA|^Peanut\nODS|F|||CARDIAC^Cardiac diet"
}
{
  "processed": 1,
  "results": [
    { "type": "ADT^A01", "outcome": "applied", "detail": { "mrn": "MRN-9042", "action": "admitted", "orderCreated": true } }
  ]
}
  • Auth: an active API key for the same organization via x-api-key, or a signed-in admin session. Failure behavior, exactly: a wrong, revoked, or other-organization key returns 401 {"error": "Invalid API key"}; no key and no session returns 401 {"error": "Authentication required"}. The siteId must belong to the organization (404 Site not found otherwise).
  • Events: A01 admit, A02 transfer, A08 update (upsert demographics, unit/room, allergies, diet codes), A03 discharge. A discharge for an unknown MRN is skipped, not an error; messages without a PID-3 MRN are skipped with a reason.
  • Allergies and diets: AL1 allergies map to structured allergens; ODS diet codes map where recognized, and anything unrecognized is preserved as unmapped for the diet office to review — nothing is silently dropped.
  • A body with no parseable MSH-framed message returns 422. Every message is logged to an audit trail either way, and the clinical feature (Pro and Enterprise) must be on the plan.

Multiple messages can be batched in one message string; results reports each separately.