lytics

Guide

Embed a web analytics dashboard in your app

Put an app's visits, visitors, pageviews and top pages inside another application — here, the analytics page of a fictional internal team portal. You track the portal with lytics, make an embed for it, and paste one iframe. Then you decide, with the access model in front of you, who should hold the link.

Checked against the current lytics embed: the steps below were run on a disposable copy of lytics, where a page on the allowed origin showed the dashboard, a page on another origin was refused, the link opened on its own with no sign-in, and a revoked link stopped serving. An embed shows aggregate numbers — never the ids of your users.

The plan

The fictional company runs Example team portal at https://portal.example.com, a site its staff sign in to. It wants a page there, /analytics, that shows how the portal itself is used. Four steps:

  1. An app in lytics for the portal, with its domain.
  2. The app's snippet in the portal, until lytics confirms a real pageview.
  3. An embed: a label, the exact origin that will show it, and a range.
  4. The iframe on /analytics, sized for phones as well as laptops.

The dashboard can be any app's — a product's numbers shown in an internal tool works the same way. Swap in your own names as you go.

1. Create an account and an app

Create an account, or sign in. On Your apps, under Add an app:

FieldWhat to enter
NameExample team portal. The embedded dashboard shows it as its heading, so pick a name you are happy for its viewers to read.
Domainsportal.example.com — every hostname the tracked site is served from. Events from other hosts are dropped.

Press Add. lytics opens the app's page with Set up this app at the top.

Create your dashboard

2. Install the snippet and receive a pageview

Copy the snippet from the app's setup into the <head> every page of the portal shares. It looks like this, with your app's key where this one says lyt_REPLACE_WITH_YOUR_SITE_KEY — so copy it from lytics, not from here:

<script>window.lytics=window.lytics||function(){(lytics.q=lytics.q||[]).push(arguments)};</script>
<script defer src="https://lytics.lp-data.cloud/api/t/lyt_REPLACE_WITH_YOUR_SITE_KEY.js"></script>

Deploy, open the portal in another tab, and press Check for pageview. Carry on once it confirms one. The installation reference covers the details — domains, single-page apps, signed-in users — and Add analytics to an Astro site covers Astro.

An embed can be made before any data arrives, but it would show zeros: a real pageview first means you see the dashboard working in the next step.

3. Make the embed

On the app's page, go to Embeds and fill in the form:

FieldWhat to enter
LabelTeam portal — analytics page. Only you see it, in the list of embeds. Name where the link lives, so you know which one to revoke.
Shown onhttps://portal.example.com — the exact origin of the page that will show it: https://, the host and any port, no path. Up to ten, comma-separated.
Range30 days. 7, 30 or 90 days, fixed for this embed. Viewers cannot change it.

Press Make an embed. lytics shows the iframe for it, once:

<iframe src="https://lytics.lp-data.cloud/api/embed/REPLACE_WITH_YOUR_EMBED_TOKEN" title="Team portal — analytics page" style="width:100%;height:900px;border:0" loading="lazy"></iframe>
Copy it now. lytics keeps only a hash of the link, so it cannot show it again. A lost link is not recovered — you make a new embed and revoke the old one. Paste it straight into the portal's code or a secret store, not into chat or a ticket.

Use Open it next to the iframe to check the dashboard on its own. The embed then appears in the list with its range, its origins and when it was last viewed — updated at most once an hour.

4. Add the iframe to the portal

The iframe lytics gives you works as it is. For the portal, give it a title that tells screen-reader users what the frame contains, and a height that suits phones:

The portal's /analytics page
<!-- /analytics in the team portal. Paste your own link from lytics in
     place of the src below — and keep that link out of public repositories. -->
<iframe
  class="lytics-embed"
  src="https://lytics.lp-data.cloud/api/embed/REPLACE_WITH_YOUR_EMBED_TOKEN"
  title="Team portal usage, last 30 days (lytics)"
  loading="lazy"
></iframe>
The portal's stylesheet
.lytics-embed {
  display: block;
  width: 100%;
  height: 1300px;
  border: 0;
}

/* On a phone the tiles and tables stack, so the page gets taller. */
@media (max-width: 640px) {
  .lytics-embed { height: 1800px; }
}

Replace the whole src with the link from step 3. REPLACE_WITH_YOUR_EMBED_TOKEN is not a token: left in, the frame says the embed does not exist.

The embedded page has no script, so it cannot tell the portal how tall it is: the frame's height is yours to set, and when the page inside is taller, the frame scrolls. The page reflows to the frame's width — tiles wrap and the tables stack on a phone — which makes it taller. Measured with every list full and no database panels:

FramePage height
1280 px wide (a laptop)about 1,280 px
390 px wide (a phone)about 1,780 px

The CSS above leaves a little room over those. Add more for each pinned panel. If the portal sends its own Content Security Policy, its frame-src has to allow https://lytics.lp-data.cloud.

What the embedded dashboard shows

PartWhat it shows
HeadingThe app’s name, the range (“Last 30 days, from …”, in UTC) and how many visitors were on it in the last five minutes.
TilesVisits, Visitors, Pageviews, Signed-in users, Logins and Other events — totals for the range.
Visits per dayA bar chart, one bar per UTC day.
Top pagesThe ten most viewed paths, as the tracked app sent them.
ReferrersThe ten hostnames that sent the most visits.
EventsThe fifteen most frequent event names other than pageviews, logins included.
From the databaseOnly when panels are pinned to the app: each panel’s stored result and when it was last updated.

The numbers are counted when the page is requested, for the embed's fixed range ending today (UTC), and mean what they mean on your own dashboard — see the numbers. A browser may reuse a copy for up to 60 seconds. The page runs no script and sets no cookies.

Who can read the link

Before the link goes anywhere, be clear about what protects it:

Who can read the dashboard?Anyone who has the link. The URL is the credential — a bearer secret — and there is no sign-in behind it. Opened directly in a browser tab, or fetched by any program, it serves the dashboard.
What do the allowed origins do?They tell browsers which pages may show it in a frame: lytics sends them as the page's frame-ancestors. A page on any other origin gets a blocked frame. That is all they do. They do not check who is looking, and they do not apply when the link is opened on its own.
Does the portal’s sign-in protect it?It protects the portal page, not the link. Everyone who can open that page can see the iframe's src in the page source and copy it, and the copy keeps working outside the portal until you revoke it.
Can one link show different data to different people?No. Every holder of a link sees the same thing: the whole app's aggregate numbers and every panel pinned to it. There is no per-viewer or per-customer filtering, so an embed is not a way to keep one tenant's data from another.
Is it indexed by search engines?It asks not to be (noindex), but that only holds while nobody publishes the link. Treat a link in a public page, repository, ticket or screenshot as leaked, and replace it.

So: only put on an embed what you are content for everyone who could ever receive the link to read — everyone who can open the portal page, and anyone they pass it to. Never publish a real embed link, and never put one in example code, a public repository or documentation; use a placeholder, as this page does.

Review what the link will show

Open the link on its own, read it as an outsider would, and check:

Revoke and replace a link

On the app's page, under Embeds, press Revoke next to its label. From then on lytics answers the link with “This embed does not exist, or it was revoked.” — in the portal the frame shows an error. A browser that loaded it in the last 60 seconds may keep showing its copy until that runs out; after that, nothing.

To replace a link — when it may have leaked, when someone who had it should no longer, or to change its origins or range:

  1. Make a new embed with the settings you want, and copy its iframe.
  2. Put the new link in the portal and deploy.
  3. Revoke the old embed.

If the old link leaked, revoke it first and accept a moment of broken frame. Embeds cannot be edited — their origins and range are fixed — and an app can have up to 20.

Optional: database panels

Not needed for any of the above. If you connect a Postgres database on Databases and pin a panel to the app, it appears under From the database on the app's dashboard and in every one of its embeds. An embed never runs a query: it shows each panel's stored result with the time it was last updated, in UTC. Panels refresh on their own interval, 60 minutes unless you chose another. Review them as you would the rest, before you pin them. Build a Postgres signup dashboard walks through making them.

Troubleshooting

SymptomWhat to check
The frame is blank or says the page refused to connect.The page showing it is not on one of the embed's origins. An origin is the scheme, the host and the port, compared exactly: https://portal.example.com does not cover https://www.portal.example.com or https://portal.example.com:8443. The browser's console names the frame-ancestors it was refused by. Origins cannot be edited — make an embed with the right ones and revoke the old one.
“This embed does not exist, or it was revoked.”The link is incomplete, still has REPLACE_WITH_YOUR_EMBED_TOKEN in it, or was revoked. A lost link cannot be shown again; make a new embed.
The portal’s console reports its own Content Security Policy blocking the frame.The portal sends a CSP that does not allow frames from lytics. Add https://lytics.lp-data.cloud to its frame-src (or child-src) directive.
The dashboard shows zeros.No pageviews in the embed's range. Open the app's page in lytics: if it still shows Set up this app, the snippet is not sending yet — finish that first.
The dashboard has a scroll bar of its own.The iframe is shorter than the page inside it, which grows with longer lists, narrower screens and pinned panels. The embedded page has no script, so it cannot resize the frame; raise the heights in the CSS above.
“Too many requests.”Each address may load embeds 60 times a minute. An office whose people all reach the internet from one address shares that, so a page that reloads the frame on a timer can run into it.

Next