Skip to main content

Player External API

The Player External JavaScript IFrame API provides third-party developers the ability to deeply integrate Seenic Tours to their solutions.

Using this API provides ability to make custom filtering, use your own website components to control Seenic Tours and make it look like part of your website/application.

What you can do

  • Send method calls from the parent window to the iframe
  • Subscribe to state and receive responses from the iframe
  • Use a simple, promise-based API from your application code

How it works

The library establishes a messaging channel between the parent page and the iframe. All communication uses the standard browser postMessage API.

Getting Started

Include the script

Add the API script to your page next to the iframe. Prefer loading it after the iframe so the target is available.

<iframe
id="seenic-player"
src="https://tour.seenic.io?locationId=5772"
></iframe>
<!-- Production (minified) -->
<script src="https://cdn.seenic.io/player-external-api.min.js"></script>
<!-- Or non-minified (debug) -->
<!-- <script src="https://cdn.seenic.io/player-external-api.js"></script> -->

Or load it as an ES module:

<iframe
id="seenic-player"
src="https://tour.seenic.io?locationId=5772"
></iframe>
<script
type="module"
src="https://cdn.seenic.io/player-external-api.js"
></script>
Download the script

Prefer to self-host? Download the embeddable script and serve it from your own domain:

You can also grab both files interactively from the Sandbox page.

Get the API instance

Obtain the client by passing the iframe element (or its selector):

const iframe = document.getElementById("seenic-player");
const api = PlayerExternalAPI(iframe);

// Or with a selector
const api = PlayerExternalAPI("#seenic-player");

Optionally enable strict origin validation (recommended only if your player origin is fixed):

const api = PlayerExternalAPI("#seenic-player", {
targetOrigin: "https://tour.seenic.io",
});

Wait for the player

The player may take a moment to initialize after the iframe loads. Subscribe to on_ready before calling methods:

api.subscribe("on_ready", () => {
console.log("Player is ready");
// Safe to call api.send(), etc.
});

Call a method

Methods are sent to the iframe with a type and optional payload:

// With a response
const result = await api.send("set_point", { pointId: 123 });
console.log(result);

Subscribe to events

Listen for changes emitted from the iframe:

api.subscribe("subscribe_to_app_state", (data) => {
console.log("State received:", data);
});

// Unsubscribe when done
const unsubscribe = api.subscribe("subscribe_to_app_state", handler);
unsubscribe();

API Overview

The Player External API object is created by calling PlayerExternalAPI(iframe, options?) with the target iframe element or a CSS selector string. Options may include targetOrigin, timeoutMs, and debug.

Client methods

MethodDescription
send(type, payload?)Sends a method call to the iframe. Returns a Promise that resolves with the response payload.
subscribe(type, callback)Subscribes to an event from the iframe. Returns an unsubscribe function.
destroy()Tears down listeners, rejects pending requests, and unsubscribes from all events.

Wire format

Outbound requests use kind: "PlayerExternalAPI:request" with { id, type, payload }. Subscriptions use kind: "PlayerExternalAPI:subscribe" / "PlayerExternalAPI:unsubscribe".

Inbound responses and events include a type string and optional payload. Failed requests reject with a PlayerExternalAPIError that may include data, responsePayload, and responseType from the player.

Naming

api.send() and api.subscribe() types use snake_case (for example get_tour_data, on_marker_click, subscribe_to_app_state).

Full method list

TypeKindDescription
on_readysubscribePlayer initialized and is ready to accept commands
get_markers_listsendAll markers in the player
get_tour_datasendTour data (cycles, views, points)
get_app_statesendCurrent app state snapshot
on_marker_clicksubscribeUser clicked a marker
subscribe_to_app_statesubscribeApp state stream
set_filtered_unitssendFilter which units are shown
locate_markersendLocate marker by Seenic marker ID (with blink)
set_pointsendMove camera to a tour point (optional pitch/yaw)
set_cycle_viewsendSet active cycle and view
set_locationsendSwitch location by ID
set_camera_posesendSet camera pitch and yaw in degrees
set_fovsendSet field of view (zoom) in degrees
get_screenshotsendScreenshot as base64 / data URL
set_fullscreen_modesendEnter/exit fullscreen
set_dollhouse_modesendEnable/disable dollhouse mode
show_spinnersendShow interactive floorplan spinner
hide_spinnersendHide interactive floorplan spinner
set_spinner_statesendSet spinner floor, frame, zoom

Example

const api = PlayerExternalAPI("#seenic-player", {
targetOrigin: "https://tour.seenic.io",
});

await api.send("set_filtered_units", {
units: [{ id: "Q1B1F1U0001" }],
});
await api.send("set_point", { pointId: 3 });

// Listen for events
api.subscribe("on_ready", () => console.log("Player is ready"));
api.subscribe("on_marker_click", (payload) =>
console.log("Marker clicked:", payload),
);
api.subscribe("subscribe_to_app_state", (data) =>
console.log("App state:", data),
);

Methods

Use the Sandbox page to run the player in an iframe and try send and subscriptions against the bundled player-external-api.js.

  • Methods are sent via api.send(type, payload) and return a Promise when the iframe responds. Use await api.send(...) to wait until the method has completed.
  • Events are subscribed via api.subscribe(type, callback), which returns an unsubscribe function.

Method and event type strings use snake_case (for example get_tour_data, on_marker_click).

Data & lists

get_markers_list

Returns all markers currently in the player as a JSON array.

Example:

const response = await api.send("get_markers_list");
console.log("Markers in player:", response.payload.markers);

Example response:

api.send() resolves with the full response envelope; the data is under payload.

{
"kind": "PlayerExternalAPI:response",
"type": "get_markers_list",
"status": "success",
"id": "1717000000000:1",
"payload": {
"locationId": 5772,
"markers": [
{
"markerId": "Q1B5S1F1U5111",
"text": "Unit 5111",
"locationId": 5772,
"type": "Unit Preview",
"unitId": "Q1B5S1F1U5111",
"actionType": "Go To Location and Point",
"actionPayload": {
"hotspotId": null,
"actionLocationId": null,
"actionPointId": 3,
"actionCycleId": null,
"actionViewId": null,
"actionEventData": null
}
}
]
}
}

get_tour_data

Returns tour data: cycles, views, points, and related structures. Use the result to pass IDs into set_point and set_cycle_view.

Example:

const response = await api.send("get_tour_data");
const tourData = response.payload;
console.log("Cycles:", tourData?.cycles, "Start point:", tourData?.startPoint);

Example response:

payload is null when the tour config has not loaded yet.

{
"kind": "PlayerExternalAPI:response",
"type": "get_tour_data",
"status": "success",
"id": "1717000000000:2",
"payload": {
"locationId": 5772,
"name": "Aventon Soraya — Building 5",
"companyId": 12,
"gAnalyticsId": null,
"status": "active",
"startPoint": 1,
"navigationMode": "points",
"units": { "scene": "m", "ruler": "ft" },
"pointsCount": 24,
"markersCount": 8,
"explorer3DId": 42,
"floors": [
{
"id": 1,
"minimapUrl": "https://media.seenic.io/locations/5772/maps/Floor1_minimap.webp?t=1743180088777"
}
],
"cycles": [],
"ui": {
"arrowsNavigation": true,
"panoSectorsNavigation": true,
"floorPointsNavigation": true,
"dollhouseButton": true,
"measureButton": true,
"minimap": true,
"locationbar": true,
"leaseButton": { "enabled": false, "link": null }
},
"camera": { "defaultFov": 75 },
"variants": [],
"spinner": null
}
}

get_app_state

Returns the current global app state snapshot.

Example:

const response = await api.send("get_app_state");
console.log("Current app state:", response.payload);

Example response:

{
"kind": "PlayerExternalAPI:response",
"type": "get_app_state",
"status": "success",
"id": "1717000000000:3",
"payload": {
"locationId": 5772,
"navigationMode": "points",
"viewMode": "panorama",
"floor": 1,
"current": { "pointId": 3, "cycleId": null, "cycleViewId": null },
"visibleMarkers": ["Q1B5S1F1U5111", "Q1B5S1F1U5112"],
"motion": {
"active": false,
"targetType": null,
"targetPointId": null,
"targetCycleId": null,
"targetCycleViewId": null,
"targetLocationId": null,
"progress": null
},
"camera": { "yaw": 1.5708, "pitch": 0, "fov": 75 },
"minimap": { "state": "minimized" },
"units": [
{
"seenicId": "Q1B5S1F1U5111",
"filteredOut": false,
"availabilityStatus": "available",
"floorPlan": {
"name": "A2",
"tourLink": "https://tour.seenic.io/?locationId=521",
"spinnerLink": "https://tour.seenic.io/?spinnerId=2069"
}
}
]
}
}

Events from iframe

Subscribe with api.subscribe(type, callback). The returned function unsubscribes.

on_ready

Fired once when the external API plugin inside the player has finished initializing. Subscribe as early as possible so you do not miss the event if the plugin is already ready when you attach.

Example:

api.subscribe("on_ready", (event) => {
console.log("Player is ready", event.payload);
});

Example event:

The subscription callback receives the full response envelope; read the data from payload.

{
"kind": "PlayerExternalAPI:response",
"type": "on_ready",
"status": "success",
"payload": { "ready": true }
}

on_marker_click

Fired when the user clicks a marker in the player.

Payload: { markerId, text?, locationId, type?, unitId?, actionType?, actionPayload } (fields may vary by marker type)

Example:

api.subscribe("on_marker_click", (event) => {
const marker = event.payload;
console.log("Marker clicked:", marker.markerId, marker.unitId);
});

Example event:

{
"kind": "PlayerExternalAPI:response",
"type": "on_marker_click",
"status": "success",
"payload": {
"markerId": "Q1B5S1F1U5111",
"text": "Unit 5111",
"locationId": 5772,
"type": "Unit Preview",
"unitId": "Q1B5S1F1U5111",
"actionType": "Go To Location and Point",
"actionPayload": {
"hotspotId": null,
"actionLocationId": null,
"actionPointId": 3,
"actionCycleId": null,
"actionViewId": null,
"actionEventData": null
}
}
}

subscribe_to_app_state

Streams app state updates on every heartbeat from the player. Uses camelCase for the subscription type name.

Example:

const unsubscribe = api.subscribe("subscribe_to_app_state", (event) => {
console.log("App state update:", event.payload);
});
// Call unsubscribe() when you no longer need updates

Example event:

Each heartbeat pushes the same app-state shape returned by get_app_state, under payload.

{
"kind": "PlayerExternalAPI:response",
"type": "subscribe_to_app_state",
"status": "success",
"payload": {
"locationId": 5772,
"navigationMode": "points",
"viewMode": "panorama",
"floor": 1,
"current": { "pointId": 3, "cycleId": null, "cycleViewId": null },
"visibleMarkers": ["Q1B5S1F1U5111"],
"motion": {
"active": true,
"targetType": "point",
"targetPointId": 5,
"targetCycleId": null,
"targetCycleViewId": null,
"targetLocationId": null,
"progress": 0.42
},
"camera": { "yaw": 2.1, "pitch": -0.1, "fov": 75 },
"minimap": { "state": "minimized" },
"units": []
}
}

Filtering

set_filtered_units

Accepts a list of units that should be shown inside the player. Each entry is typically { id: string } where id is a Seenic unit ID.

Example:

const response = await api.send("set_filtered_units", {
units: [{ id: "Q1B5S1F1U5111" }, { id: "Q1B1S1F2U1205" }],
});
console.log("Units hidden:", response.payload.filteredOutCount);

Example response:

{
"kind": "PlayerExternalAPI:response",
"type": "set_filtered_units",
"status": "success",
"id": "1717000000000:4",
"payload": {
"applied": true,
"filteredOutCount": 37
}
}

locate_marker

Locates a marker by Seenic marker ID with a blink highlight effect.

Payload: { markerId: string }

Example:

const { payload } = await api.send("get_markers_list");
const firstMarkerId = payload.markers?.[0]?.markerId;
if (firstMarkerId) {
const response = await api.send("locate_marker", { markerId: firstMarkerId });
console.log("Located at point:", response.payload.pointId);
}

Example response:

cycleId and viewId are only present when the location uses cycles navigation.

{
"kind": "PlayerExternalAPI:response",
"type": "locate_marker",
"status": "success",
"id": "1717000000000:5",
"payload": {
"markerId": "Q1B5S1F1U5111",
"pointId": 3
}
}

set_point

Moves the camera/view to a tour point (ID from get_tour_data). Optionally sets camera orientation in degrees when navigating to the point.

Payload: { pointId: number, pitch?: number, yaw?: number }

  • pitch and yaw are optional; when either is provided, both must be sent together.
  • pitch is in [-90, 90]; yaw is in [0, 360).

Example:

await api.send("set_point", { pointId: 3 });

// With camera angles
const response = await api.send("set_point", { pointId: 3, pitch: 0, yaw: 90 });
console.log(response.payload);

Example response:

pitch and yaw are echoed only when they were provided in the request.

{
"kind": "PlayerExternalAPI:response",
"type": "set_point",
"status": "success",
"id": "1717000000000:6",
"payload": {
"pointId": 3,
"navigationMode": "points",
"pitch": 0,
"yaw": 90
}
}

set_cycle_view

Sets the active cycle and view (IDs from get_tour_data).

Payload: { cycleId: number, viewId: number }

Example:

const response = await api.send("set_cycle_view", { cycleId: 1, viewId: 2 });
console.log(response.payload);

Example response:

{
"kind": "PlayerExternalAPI:response",
"type": "set_cycle_view",
"status": "success",
"id": "1717000000000:7",
"payload": {
"cycleId": 1,
"viewId": 2
}
}

set_location

Switches to a location by ID.

Payload: { locationId: number }

Example:

const response = await api.send("set_location", { locationId: 5772 });
console.log(response.payload);

Example response:

{
"kind": "PlayerExternalAPI:response",
"type": "set_location",
"status": "success",
"id": "1717000000000:8",
"payload": {
"locationId": 5772
}
}

Camera & FOV

set_camera_pose

Sets the camera orientation.

Payload: { pitch: number, yaw: number }

Example:

await api.send("set_camera_pose", { pitch: 0, yaw: 45 });

Example response:

{
"kind": "PlayerExternalAPI:response",
"type": "set_camera_pose",
"status": "success",
"id": "1717000000000:9",
"payload": {
"applied": true,
"pitch": 0,
"yaw": 45
}
}

set_fov

Sets the field of view (zoom level). Lower values zoom in; higher values zoom out.

Payload: { value: number }

Example:

const response = await api.send("set_fov", { value: 40 });
console.log("FOV set to:", response.payload.fov);

Example response:

{
"kind": "PlayerExternalAPI:response",
"type": "set_fov",
"status": "success",
"id": "1717000000000:10",
"payload": {
"fov": 40
}
}

get_screenshot

Captures the 3D scene (without interactive UI overlays) and returns image data.

Response payload: { dataUrl: string, width: number, height: number } where dataUrl is a PNG data:image/png;base64,... URL.

Example:

const response = await api.send("get_screenshot");
const { dataUrl, width, height } = response.payload;
document.querySelector("#preview").src = dataUrl;

Example response:

{
"kind": "PlayerExternalAPI:response",
"type": "get_screenshot",
"status": "success",
"id": "1717000000000:11",
"payload": {
"dataUrl": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...",
"width": 1280,
"height": 720
}
}

Modes

set_fullscreen_mode

Enables or disables fullscreen mode.

Payload: { value: boolean }

Example:

const response = await api.send("set_fullscreen_mode", { value: true });
console.log("Fullscreen:", response.payload.fullscreen);
await api.send("set_fullscreen_mode", { value: false });

Example response:

{
"kind": "PlayerExternalAPI:response",
"type": "set_fullscreen_mode",
"status": "success",
"id": "1717000000000:12",
"payload": {
"fullscreen": true
}
}

set_dollhouse_mode

Enables or disables dollhouse view.

Payload: { value: boolean }

Example:

const response = await api.send("set_dollhouse_mode", { value: true });
console.log("Dollhouse (orbit) on:", response.payload.orbit);
await api.send("set_dollhouse_mode", { value: false });

Example response:

{
"kind": "PlayerExternalAPI:response",
"type": "set_dollhouse_mode",
"status": "success",
"id": "1717000000000:13",
"payload": {
"orbit": true
}
}

Spinner

show_spinner

Shows the interactive floorplan spinner. No payload.

Example:

const response = await api.send("show_spinner");
console.log("Spinner visible:", response.payload.visible);

Example response:

Tours without a 360° interactive floorplan spinner reject with a SPINNER_NOT_CONFIGURED error instead.

{
"kind": "PlayerExternalAPI:response",
"type": "show_spinner",
"status": "success",
"id": "1717000000000:14",
"payload": {
"visible": true,
"configured": true
}
}

hide_spinner

Hides the interactive floorplan spinner. No payload.

Example:

const response = await api.send("hide_spinner");
console.log("Spinner visible:", response.payload.visible);

Example response:

{
"kind": "PlayerExternalAPI:response",
"type": "hide_spinner",
"status": "success",
"id": "1717000000000:15",
"payload": {
"visible": false,
"configured": true
}
}

set_spinner_state

Sets interactive floorplan spinner state.

Payload: { floor: number, frame: number, zoom: number }

Example:

const response = await api.send("set_spinner_state", {
floor: 2,
frame: 27,
zoom: 1.5,
});
console.log(response.payload);

Example response:

{
"kind": "PlayerExternalAPI:response",
"type": "set_spinner_state",
"status": "success",
"id": "1717000000000:16",
"payload": {
"applied": true,
"floor": 2,
"frame": 27,
"zoom": 1.5
}
}