Integration · Embed Signage

Switch content in real time from the player itself.

SignIQ Measure exposes a local HTTP + WebSocket API on 127.0.0.1:8765. Embed Signage running on the same Windows or Android player calls into it to switch playlists based on live audience — without any cloud round-trip.

TL;DR

  • 1. SignIQ Measure runs as a service on the player — no UI after first pairing.
  • 2. It exposes 127.0.0.1:8765 with bearer-token auth.
  • 3. Embed Signage scripts poll or subscribe to audience data.
  • 4. Your content rules pick playlists based on viewer counts, attention, dwell.

1. Get the local token

The first time SignIQ Measure runs on a player, it generates a random 32-byte token stored locally. The pairing screen displays this token (and a QR code) — paste it into your Embed Signage device config as the env var SIGNIQ_LOCAL_TOKEN.

The token never leaves the device. SignIQ does not see it. Lose it and you can regenerate from the SignIQ Measure tray icon.

2. Detect SignIQ is running

Hit /v1/health (unauthenticated) before doing anything else. If it returns 200, you're good. If it times out, SignIQ isn't installed or hasn't started — fall back to your default playlist.

curl http://127.0.0.1:8765/v1/health
# → { "ok": true, "service": "signiq-measure", "app_version": "0.0.1" }

3. Poll current audience

For simple polling-based content switching, hit /v1/audience/current?seconds=30 on a timer. The response is a rolling-window aggregate; pick a playlist based on attention_viewers and switch.

curl -H "Authorization: Bearer $SIGNIQ_LOCAL_TOKEN" \
  "http://127.0.0.1:8765/v1/audience/current?seconds=30"
# → {
#     "from": "2026-05-11T11:30:00Z",
#     "to":   "2026-05-11T11:30:30Z",
#     "total_viewers": 4,
#     "notice_viewers": 3,
#     "attention_viewers": 2,
#     "peak_concurrent": 4,
#     "avg_attention_quality": 0.78
#   }

JS example from Embed Signage content:

const TOKEN = process.env.SIGNIQ_LOCAL_TOKEN; // paste from device pairing screen

async function pickPlaylist() {
  const r = await fetch('http://127.0.0.1:8765/v1/audience/current?seconds=30', {
    headers: { Authorization: `Bearer ${TOKEN}` },
  });
  if (!r.ok) return 'default';
  const { attention_viewers } = await r.json();
  if (attention_viewers >= 5) return 'busy-promo';
  if (attention_viewers >= 2) return 'engaged';
  return 'ambient';
}

setInterval(async () => {
  embedSignage.setActivePlaylist(await pickPlaylist());
}, 5000);

4. Or subscribe via WebSocket (sub-second)

For sub-second reactions, open a WebSocket to /v1/realtime/ws. Events stream as JSON; react when the right one arrives.

const TOKEN = process.env.SIGNIQ_LOCAL_TOKEN;
const ws = new WebSocket(`ws://127.0.0.1:8765/v1/realtime/ws?token=${TOKEN}`);

ws.onmessage = (msg) => {
  const event = JSON.parse(msg.data);
  if (event.event === 'audience.snapshot') {
    if (event.payload.attention_viewers >= 5) {
      embedSignage.setActivePlaylist('busy-promo');
    }
  }
};

Endpoint reference

MethodPathAuthDescription
GET/v1/healthnoneLiveness probe — returns 200 OK if SignIQ Measure is running on this player. Use to detect installation before negotiating a token.
GET/v1/audience/current?seconds=NbearerRolling-window aggregate over the last N seconds (default 30, clamped 5–300). The primary content-switching endpoint.
GET/v1/audience/window?from=…&to=…bearerExact-range window query (RFC3339 timestamps, max 30-minute look-back buffer). Use for historical comparisons.
GET/v1/measure/livebearerInstant snapshot: current viewers, audience composition, average attention. Updated every measurement tick.
GET/v1/realtime/wsbearer (token in query string)WebSocket stream of audience and rule events. Subscribe once, react sub-second.
POST/v1/playback/now-playingbearerTell SignIQ what Embed Signage is currently showing — content_id, campaign_id, asset name, duration. Lets SignIQ attribute audience to content for analytics.

Notes

  • Cold start. The 30-minute rolling buffer takes a few seconds to fill. During that period /v1/audience/current returns zeros with warming_up: true — treat as default-content fallback.
  • Port fallback. If 8765 is taken the service tries 8766–8774 in order. The exact port is shown on the pairing screen and stored in the device's app data directory.
  • CORS. The API accepts any origin so Embed Signage content loaded from file://, http://localhost, or an embedded WebView all work.
  • Tizen. Samsung Smart Signage cannot reliably reach a localhost server. On Tizen, use the measure-bundle JS API instead — same data shape, in-process.
  • No video leaves the device. The local API exposes only aggregates and event metadata. Raw frames stay in the SignIQ Measure process and are never persisted, never transmitted.

Need a custom integration?

We can help wire SignIQ Measure into any CMS that can run a script on the player.

Talk to engineering