# Embed dashboards with embed tokens

> Show a business's Message24 dashboard or call-center analytics inside an ERP, intranet or website with an iframe and a read-only embed token.

Source: https://docs.message24.net/developers/embed/embed-dashboards/

An embed token lets you show a business's live Message24 analytics inside another system, such as an ERP or an internal portal, without anyone logging in to Message24. You place an iframe on your page; the token in its URL decides which business and which pages it can show.

Two pages can be embedded:

| Page | Path | Scope | Shows |
|---|---|---|---|
| Business dashboard | `/embed/dashboard` | `dashboard` | The business dashboard, including conversation drill-downs. |
| Call center | `/embed/cdr` | `cdr` | Call-center analytics: KPIs, trends, heatmap, agent performance and the call log. |

Embedded pages are read-only. Export buttons and links that open conversations in the app are hidden, and nothing can be changed.

## What a token exposes

Treat an embed token like a password. Anyone who has it can load the pages it was created for, from any browser, and see:

- The analytics on those pages.
- Drill-down lists of conversations with contact names (dashboard scope).
- The full call log, including caller phone numbers (call center scope).

A token never exposes message content, CSV exports, any way to change data, or anything belonging to another business.

:::caution
The token is part of the iframe's URL, so anyone who can view the source of the page hosting the iframe can copy it. Only embed on pages that are themselves behind your own login.
:::

## Create a token

Embed tokens are managed by business admins. If you are the developer, ask an admin of the business to do this and send you the token.

1. In Message24, open **Settings > Embed Tokens**.
2. Under **New token**, enter a **Token name** that says where it will be used, for example "ERP production".
3. Under **Pages this token can embed**, tick **Business dashboard**, **Call center**, or both. Choose only the call center to share call analytics without revenue or order figures.
4. Optionally fill in **Allowed embedding origins** (see [origin allowlist](#origin-allowlist)).
5. Press **Create**.

The token is shown **once**, together with a ready-made embed URL and a **Copy \<iframe\> snippet** button for each page it can embed. Message24 stores only a hash of the token, so it cannot be shown again. If it is lost, create a new token and revoke the old one.

## URL format

```text
https://message24.net/embed/dashboard#embedToken=YOUR_TOKEN
https://message24.net/embed/cdr#embedToken=YOUR_TOKEN
```

The token goes in the URL fragment, after `#`. Browsers never send the fragment to a server, so the token stays out of access logs and `Referer` headers. There are no other URL parameters.

:::note
`?embedToken=YOUR_TOKEN` as a query parameter also works, for older hand-built links, but it sends the token to the server with every page load. Use the fragment form.
:::

## Embed code

The snippet Message24 generates is a full-width iframe plus a small listener that resizes it to fit its content, so there is no inner scrollbar:

```html
<iframe src="https://message24.net/embed/dashboard#embedToken=YOUR_TOKEN" style="width:100%;border:0" height="900" title="Message24"></iframe>
<script>
  window.addEventListener('message', function (e) {
    if (e.origin !== 'https://message24.net' || !e.data || e.data.type !== 'm24-embed-height') return;
    document.querySelectorAll('iframe').forEach(function (f) {
      if (f.contentWindow === e.source) f.style.height = e.data.height + 'px';
    });
  });
</script>
```

The embedded page posts `{ type: "m24-embed-height", height: <pixels> }` to its parent whenever its layout changes. The height is the only data it sends. `height="900"` is the size used until the first message arrives, or if your page does not add the listener.

To embed both pages on one host page, add one iframe per page. A single listener handles all of them, since it matches each message to the iframe that sent it.

## Origin allowlist

**Allowed embedding origins** restricts which sites may frame a token. Enter origins separated by commas, spaces or new lines:

```text
https://erp.example.com, https://intranet.example.com
```

- An origin is the scheme, host and port, with no path. A trailing slash is ignored. Matching is exact: `https://example.com` does not allow `https://www.example.com`.
- **Leave it empty to allow any site.** The token list then shows "Embeddable on any origin".
- Opening the embed URL directly in a browser tab, not inside an iframe, is always allowed, so you can check a token by pasting its URL.
- When the page is framed by a site that is not on the list, it shows "Not authorized here".

The check runs in the browser. The embedded page reads its parent's origin from `window.location.ancestorOrigins` in Chromium and WebKit browsers, and from `document.referrer` elsewhere. If it cannot confirm the parent, it refuses to render. In browsers without `ancestorOrigins`, such as Firefox, a host page that sends `Referrer-Policy: no-referrer` therefore always shows "Not authorized here"; use a policy that sends at least the origin, such as the default `strict-origin-when-cross-origin`.

:::note
The allowlist stops other websites from framing a token in a normal browser. It does not make a leaked token safe: the token itself is the access boundary. Revoke any token that leaks.
:::

## Editing and scopes

In the token list, the edit button lets an admin change a token's name and allowed origins. The token value stays the same, so live embeds keep working.

Scopes cannot be changed after a token is created. To give an existing embed access to another page, create a new token with the pages you need, switch your iframes to it, and revoke the old one. A token loaded on a page it does not cover shows "Not available for this token".

## Revoking a token

Press the revoke button next to the token and confirm. The token stops working on the next request: embedded pages show "Invalid or revoked token". Revoked tokens stay in the list, greyed out with the date they were revoked, so there is a record of them. Each token also shows its creator, creation date and when it was last used.

## Messages the embed can show

| Message | Cause |
|---|---|
| Missing embed token | The URL has no `embedToken`. |
| Invalid or revoked token | The token is wrong, was revoked, or could not be verified. |
| Not available for this token | The token was not created with this page's scope. |
| Not authorized here | The host site is not in the token's allowed origins, or its origin could not be confirmed. |

## The API behind the embed

The embedded pages load their data from `https://message24.net/api/embed/`, sending the token in an `X-Embed-Token` header. They are listed here so you know exactly what a token can reach.

| Endpoint | Scope |
|---|---|
| `GET /api/embed/context` | any: the business's ID, name and currency, its channels, the token's scopes and allowed origins |
| `GET /api/embed/dashboard/stats` | `dashboard` |
| `GET /api/embed/dashboard/session-stats` | `dashboard` |
| `GET /api/embed/dashboard/session-drilldown` | `dashboard` |
| `GET /api/embed/cdr/kpis` | `cdr` |
| `GET /api/embed/cdr/call-trends` | `cdr` |
| `GET /api/embed/cdr/call-trends/by-agent` | `cdr` |
| `GET /api/embed/cdr/heatmap` | `cdr` |
| `GET /api/embed/cdr/agent-performance` | `cdr` |
| `GET /api/embed/cdr/duration-distribution` | `cdr` |
| `GET /api/embed/cdr/language-breakdown` | `cdr` |
| `GET /api/embed/cdr/caller-segmentation` | `cdr` |
| `GET /api/embed/cdr/caller-segmentation/callers` | `cdr` |
| `GET /api/embed/cdr/events` | `cdr` |

A missing or unknown token returns `401`; a token without the route's scope returns `403`. Requests for CSV output (`format=csv`) are refused with `403`. Every query is limited to the token's business, whatever parameters are sent.
