# Embedding the live view

> Show the venue's live 3D floor inside your app: mint a short-lived embed address, frame it, renew it when it expires, or frame the plan alone.

There are two embeds. The live embed shows the floor with live status and a replay of stored days. The plan embed shows the plan alone. Both are view-only, and only your origins may frame them.

## Get a live embed address

Your server asks for an address with a signed request, as in [Signing requests](https://floorplancraft.com/docs/signing-requests.md):

```http
POST /api/live/embed-tokens HTTP/1.1
Host: floorplancraft.com
Content-Type: application/json
X-FPC-Connection: lc_8sKq2mXvT0bN4yR7wE1uZa
X-FPC-Timestamp: 1794054600
X-FPC-Signature: v1=<64 lowercase hex characters>

{"ttlSeconds":43200}
```

The body is empty, `{}`, or `{ "ttlSeconds": n }` with n a whole number from 60 to 43200. The default and the longest is 43200 seconds, 12 hours. The answer is `200 OK`:

```json
{ "url": "https://floorplancraft.com/live/embed/<token>", "expiresAt": "2026-11-08T00:30:00.000Z" }
```

- Mint on your server and hand only `url` to the browser. The token in it opens this one connection's live view until `expiresAt`.
- Limits: a 4 KB body; 10 addresses at once, then 30 an hour per connection.
- Any other field, or a `ttlSeconds` out of range, answers `422` with `"error": "invalid_request"`.
- Like a push, it answers `402` while the workspace does not have Live 3D.

## Frame it

```text
<iframe id="live-floor" title="Live floor"
  src="https://floorplancraft.com/live/embed/<token>?chrome=compact"
  style="width: 100%; height: 72vh; border: 0"></iframe>
```

- Give the frame a definite height, such as `72vh`. The view fills it: its heading and replay controls stay on screen, and the 3D floor takes the rest, at least 240 px. A frame without a height gets the browser's 150 px default.
- Only your connection's `embedOrigins` may frame the live embed. Every other FloorPlanCraft page refuses to be framed.
- The view uses your app's words: a booking app's view says **In use** and **Cleaning**, a play venue's says **Playing**, **Empty** and **Closed**.

## Address options

| Add | Effect |
| --- | --- |
| `?chrome=compact` | Leaves out the view's own eyebrow and large venue name, for a page that already has a header. **Live** or **Last seen** and the **Service day** picker stay. |
| `?labels=busy` | Starts with floor labels only on zones that are not free, and on stations |
| `?labels=off` | Starts with no floor labels |

Combine them as `?chrome=compact&labels=busy`. Only these exact values count; anything else gives the default, the full heading and labels on every zone. A viewer can change labels with the view's **Labels** menu; their browser remembers that choice and uses it instead of `?labels=`.

## When it expires

At `expiresAt` the view shows a notice and posts this message to the page that frames it:

```json
{ "type": "fpc:embed-expired" }
```

Mint a new address on your server and set the frame's `src`:

```ts
const frame = document.querySelector<HTMLIFrameElement>("#live-floor")!;

window.addEventListener("message", async (event: MessageEvent) => {
  if (event.origin !== "https://floorplancraft.com" || event.source !== frame.contentWindow) return;
  if (event.data?.type !== "fpc:embed-expired") return;
  // Your own server route, which calls POST /api/live/embed-tokens and returns { url }.
  const response = await fetch("/api/floorplancraft/live-embed", { method: "POST" });
  const { url } = (await response.json()) as { url: string };
  frame.src = url + "?chrome=compact";
});
```

## When it closes

Disconnecting, a lapsed Live 3D subscription, or unsharing or deleting the plan closes open live embeds within a minute: the view says it is no longer available. Addresses minted for a connection also stop working when a new connection replaces it.

## The plan embed

`planEmbedUrl` from the code exchange is the plan alone, view-only: `https://floorplancraft.com/embed/<shareToken>`. Add `?view=3d` for the 3D view; the default is `?view=2d`.

- You do not mint it, and it does not expire. Unsharing or deleting the plan closes it.
- It may be framed from the plan's allowed origins. The code exchange adds your `embedOrigins`, and the venue owner can edit the list in the plan's **Share** dialog. Disconnecting removes the origins your connection added, unless the owner or another connection added them too.
- Use it as a preview while pushes answer `402`.

> **Note:** Neither embed shows FloorPlanCraft's notice that the plan is a concept layout. Inside your frame, your app's own terms apply.

## Related

- [Table clicks and highlights](https://floorplancraft.com/docs/table-clicks-and-highlights.md)
- [Live updates](https://floorplancraft.com/docs/live-updates.md)
- [Disconnect and errors](https://floorplancraft.com/docs/disconnect-and-errors.md)

---

Source: https://floorplancraft.com/docs/embed-the-live-view · Updated 2026-11-07
