docs
/
AppEngine API

Device integrations

The server side of Device Hub — hub keys and live state, remote peripheral config, device calls, and the event stream back to screens.

DeviceIntegrationsModule is the server half of the Device Hub. On-site hub agents hold a WebSocket open to it; staff screens (POS, KDS, kiosk, the Hubs admin) call its HTTP routes to send work to hardware and subscribe to what the hardware reports. Installing and pairing a hub is covered in setting up a hub; this page is the API.

Datatypes: device_hub (one per hub box, keyed by data.name) and device_config (one per peripheral).

Hubs

POST/device-integrations/hubs/:sk/api-keyJWT
GET/device-integrations/hubs/live-statesJWT
GET/device-integrations/hubs/:hubName/stateJWT
POST/device-integrations/hubs/:hubName/pingJWT
GET/device-integrations/hubs/:hubName/discover-pathsJWT
POST/device-integrations/hubs/:hubName/endpointsJWT
  • api-key — mints a new hub_… key, stores only its SHA-256 hash with apiKeyMintedAt, and returns the cleartext once. Re-minting invalidates the old key, so the agent is locked out until it is reconfigured.
  • live-states — { hubName: { online, connectedAt, version } } for every hub with an open socket, read from the gateway's in-memory map rather than the database.
  • state — one round trip for a Diagnose panel: the hub record (status, lastSeen, lastError, ipAddress, apiKeyMintedAt, endpointsReported), the live view (online, connected since, version, endpoints) and the devices configured on that hub.
  • ping — times a round trip to the agent: { ok, online, latencyMs, error? }.
  • discover-paths — asks the agent to list candidate device files (HID readers, USB printers, USB-serial) so the UI can offer a picker. Linux only; other platforms return none.
  • endpoints — pushes peripheral config to the agent, which saves it and reloads. Body { action: 'merge', endpoints: [{ name, kind, capabilities, config }] } upserts by name; { action: 'remove', names } removes; replace is also accepted.

Calling a device

GET/device-integrations/adaptersJWT
GET/device-integrations/adapters/configuredJWT
POST/device-integrations/callJWT
POST/device-integrations/pageJWT

call takes { capability, data }, where capability is page · print · drawer · charge · weigh · display · kds. The target is resolved as:

  1. ?adapter=console or ?adapter=sms-pager — sent straight to that adapter, no lookup.
  2. ?device=<name> — that device_config.
  3. Otherwise the first active device_config with that capability, narrowed by ?servicePoint= and ?location=.

A device with connection: 'remote' is sent to its hubName / endpointName over the hub socket; adapterName: 'sms-pager' sends a Twilio SMS; console just logs and succeeds (for testing). With no matching device, a page falls back to the SMS pager when Twilio is configured; anything else answers 503. Each call stamps lastUsedAt on the device, and lastError on failure. The result is { ok, ref?, data?, error? }.

page is the shortcut for paging a guest: body { phone, pagerNumber, message, alertType }, capability implied. The SMS pager needs a phone number.

Events from hubs

GET/device-integrations/eventsJWT

Hub agents push events — scanner reads, drawer opened, printer errors — and the server fans them out per org. Each message is { kind, hubName, endpointName, data, ts }; a hub-status event is emitted whenever a hub connects or drops. Filter with ?hub= and ?kinds= (comma-separated); orgid may be passed as a query parameter because EventSource cannot set headers. The stream pings every 25 seconds.

Browsers can instead subscribe over socket.io on the /device-events namespace with auth: { token, orgId, hub? } and listen for event. The token must be a user of that org; sockets are dropped if the account is locked.

The hub socket

Agents connect to /ws/hub (a raw WebSocket) with x-hub-name, x-hub-key and x-orgid headers, or hubName, apiKey and orgid query parameters. The key is checked against the stored hash; a bad key closes the socket with code 4001. One connection per hub — a reconnect closes the previous socket.

On connect the hub is marked online. Its hello reports its endpoints and version, and the server creates a device_config for each endpoint it has not seen, leaving operator edits alone. heartbeat refreshes lastSeen and per-endpoint status. On disconnect the hub goes offline and its endpoint statuses are cleared.