Embeds
Put a dashboard, chat, skill or notebook on another site.
Contents
What is an embed?
An embed shows a dashboard, chat, skill or notebook inside a frame on another site. It always shows the whole object, view-only. You can't embed one chart on its own, so a dashboard embed shows every chart on the dashboard. Embeds work on every plan.
You turn one on in the object's Settings → Embed. Flip the switch to Enabled, then pick who
sees it. The frame's address is the object's own address under /embed/, such as
/embed/dashboards/{id}.
Who sees an embed?
The Embed card offers three audiences, and one is on at a time.
| Audience | Who sees it | What you set up |
|---|---|---|
| Anyone | Anyone with the address, in the frame or outside it | Nothing. This is the object's public access |
| Anyone who can access your website | Whoever your site lets in, with no Vistazo account | A key, and a few lines on your server that sign each page load |
| Anyone who can access this dashboard (or chat, skill, notebook) | Only people who can already open the object in Vistazo | Nothing, or a key if your own sign-in names the reader |
Flipping the switch on picks Anyone who can access this dashboard, so enabling an embed never makes anything public. Anyone is the same setting as Anyone with the link in the Sharing pane (How sharing works). Turn public access on there and the embed moves to Anyone. Turn it off there and an embed at Anyone goes off with it.
Warning
While an embed is at Anyone, the object is public. Anyone who finds its address can open it, with or without your site.
You need edit access to manage an embed. On a chat, only its author can. Guests never see the Embed
pane. Your agent sets the same choice with the set_sharing tool's embed.
What does the frame show?
The frame has no filters, no refresh and no built-in assistant. A reader can't change the values a dashboard shows.
A frame whose object isn't public asks its reader to sign in, with Sign in to see this dashboard if you have access. Someone signed in who can't open it reads You don't have access to this, or it doesn't exist. They get Open in Vistazo and Sign in again, which signs the frame in afresh for a workspace that asks for a recent sign-in. Neither message says whether the object exists, so you can hand out the code for a private object safely.
A page your server signed that the embed won't serve shows This embed isn't available, with no link out.
What is in the frame's header?
The frame opens with a header. It carries the Vistazo logo, the object's name, when it last refreshed and a button that opens it in Vistazo. On a chat or skill the time reads as last updated.
To hide the header, turn off Show header in the Code card. Or add header=0 to the frame's
address yourself, as in /embed/dashboards/{id}?header=0 or …?t={token}&header=0. The frame
shrinks to fit and reports its new height to your page.
How does a website embed work?
A website embed shows the object to whoever your site lets in, while its public access stays off. Your server signs a short-lived token on each page load and adds it to the frame's address. Anyone who loads that page sees the object, with no Vistazo account.
Embed keys
Your server signs with an embed key. In the object's Settings → Embed, press New key in the
Keys card and give it a name. Vistazo shows the secret once. Copy it into your server's
environment as VISTAZO_EMBED_SECRET straight away, because it never appears again.
Each key's row then shows the last four characters of its secret. That lets you check which secret your server holds. The row's menu rotates a key, which issues a new secret while the old one keeps working for 24 hours. Your server can switch over in that window without a gap.
Deleting a key stops every page signed with it. Switching the embed to Anyone who can access this dashboard also stops these pages. Switching doesn't delete the keys, so switching back to Anyone who can access your website makes the same pages show again.
Signing a token
The Code card writes the code for the object you're on. Pick Node or Python for your server and
HTML or React for the frame. In Node, with jose:
import { SignJWT } from 'jose'
const token = await new SignJWT({ dashboardId: '{dashboard id}' })
.setProtectedHeader({ alg: 'HS256', kid: '{key id}' })
.setExpirationTime('10m')
.sign(new TextEncoder().encode(process.env.VISTAZO_EMBED_SECRET))
const src = `https://app.vistazo.ai/embed/dashboards/{dashboard id}?t=${token}`
In Python, with PyJWT:
import os, time, jwt
token = jwt.encode(
{"dashboardId": "{dashboard id}", "exp": int(time.time()) + 600},
os.environ["VISTAZO_EMBED_SECRET"],
algorithm="HS256",
headers={"kid": "{key id}"},
)
src = "https://app.vistazo.ai/embed/dashboards/{dashboard id}?t=" + token
Set src as the iframe's address. Any JWT library works. The token is signed with HS256, its header's
kid is the key's ID, and its payload names the object and an expiry. The claim that names the
object depends on its kind.
| Object | Claim | Frame address |
|---|---|---|
| Dashboard | dashboardId |
/embed/dashboards/{id} |
| Chat | chatId |
/embed/chats/{id} |
| Skill | skillId |
/embed/skills/{id} |
| Notebook | notebookId |
/embed/notebooks/{id} |
Vistazo refuses a token that expires more than 10 minutes out rather than shortening it. The frame loads the object once when it opens and keeps showing it after the token expires. To show newer data, your page loads again with a fresh token.
Filter values
A dashboard with filters opens at its published defaults. To show something else, such as each
customer's own region, add params to the token. Each entry is a filter's key and a value of that
filter's type. Your server signs them, so the reader can't change them.
const token = await new SignJWT({
dashboardId: '{dashboard id}',
params: { region: 'EMEA', countries: ['US', 'DE'], window: { start: 'today-30', end: 'today' } },
})
{"dashboardId": "{dashboard id}", "params": {"region": "EMEA"}, "exp": int(time.time()) + 600}
For a dashboard with filters, the Code card adds a params line set to its first filter's
default. Only dashboards take params. A chat, skill or notebook token that carries them shows
This embed isn't available.
Vistazo checks every value against the dashboard's filters, by type and, for a list, against its options. One unknown key or bad value shows This embed isn't available. Vistazo never drops the value and shows a wider view instead. To test a set of values, open the dashboard in Vistazo and pick the same ones in its filters.
A value equal to its filter's default counts as the default. Relative dates resolve in UTC, so
today turns over at midnight UTC. A text value that would land inside a warehouse query is refused,
because the query runs as the dashboard's owner. Text that only fills a title is fine.
The token rides in the frame's address. Anyone who can see your page's source, the browser's history or a log of addresses can read the values, so never put a secret in them.
Who pays for filter values
The defaults, and any combination of values already shown, cost nothing and run no new queries. Each new combination runs the dashboard's queries on its owner's connections, as the owner, and lands on the owner's warehouse bill.
A dashboard runs up to 500 new combinations a day. After that, a new combination shows This embed isn't available until the day is up, while the defaults and earlier combinations keep showing. The limit counts runs rather than their cost. To cap spend, use your warehouse's own quotas, such as BigQuery's custom query quotas.
How do you embed for people with access?
This audience shows the object on any site, to the people who can already open it in Vistazo. Anyone else sees nothing of it, including a member it isn't shared with. The Sign-in choice in the Code card sets how the frame learns who is reading.
Vistazo sign-in
Vistazo is the default. The code is the object's address, as a link, an HTML snippet or a React snippet. You need no key and no server code.
A reader who isn't signed in presses Sign in in the frame and signs in to Vistazo in a small window. The first time, Vistazo asks whether to show itself on that site. The answer covers every page on the site, because browsers keep one sign-in per site. Each reader sees the sites they allowed under Settings → Security → Authorized apps, and can revoke any of them there.
Your workspace's single sign-on and two-factor rules apply in the frame, as in the app. If the site blocks pop-up windows, the frame offers Open in Vistazo in a new tab. With no token there are no filter values, so a dashboard shows its published defaults.
SSO
With SSO, your server signs a token that names the object and the person reading. It can be picked only in a workspace with single sign-on available, and elsewhere it is greyed out. Pages show once the workspace has verified at least one domain.
Sign as in Signing a token and add the person as viewer. One set of keys
serves both website embeds and these.
const token = await new SignJWT({ dashboardId: '{dashboard id}', viewer: { email: user.email } })
.setProtectedHeader({ alg: 'HS256', kid: '{key id}' })
.setExpirationTime('10m')
.sign(new TextEncoder().encode(process.env.VISTAZO_EMBED_SECRET))
token = jwt.encode(
{"dashboardId": "{dashboard id}", "viewer": {"email": user_email}, "exp": int(time.time()) + 600},
os.environ["VISTAZO_EMBED_SECRET"],
algorithm="HS256",
headers={"kid": "{key id}"},
)
An email must be on one of the workspace's verified domains. To name someone by your identity
provider's user ID instead, use viewer: { sub: '{user id}' }. Read the email from the session your
server verified, never from the page or the request. The Code card shows that line for Okta,
Microsoft Entra, Google Workspace and WorkOS.
These tokens take params under the same rules, and the queries still run as the dashboard's owner.
viewer decides who may open the frame and has no say in which rows they see.
Your workspace's single sign-on and two-factor rules don't apply in this frame, because your site has already signed the person in. If the workspace loses single sign-on or its last verified domain, these pages stop showing. Vistazo sign-in keeps working.
Who manages embed keys?
Whoever can edit an object manages its keys, and on a chat that is its author. When someone else creates, rotates, renames or deletes a key, the object's owner gets a notification. Deleting an object deletes its keys.
Workspace admins find every embed key in the workspace under Settings → General → Authentication, in the Embed keys card. Each row shows the object, the key's name, who created it and when it was last used. Revoke there stops the key.
What happens when public links are off?
An admin can turn off the workspace's Public links switch. Embeds at Anyone and Anyone who can access your website then stop showing, and both audiences read Disabled for this workspace. Each object keeps its choice, so turning public links back on brings its embed back. Embeds for people with access keep working, because their readers can already open the object.
How does a pasted link become an embed?
Some pages turn a pasted link into an embedded view using oEmbed, an open standard for exactly that. Paste an object's address into any page that accepts an oEmbed link and the page shows the object's frame, as the audience you picked in Settings → Embed allows. Pasting the address of one chart on a dashboard shows the whole dashboard. On a page that accepts an iframe instead, paste the HTML snippet from the Code card.
The endpoint those pages call is public:
GET https://api.vistazo.ai/api/oembed?url={address}&format=json
url is the object's address as it appears in the browser, such as
https://app.vistazo.ai/dashboards/{id}. The answer is the rich type, and its html is the
object's /embed/ frame. An object with public access also returns its title. Every other address
gets the same answer without one, so the endpoint gives nothing away about a private object.
Every object's page also advertises the endpoint, so a site that looks for it finds it from the pasted address alone.
What are an embed's limits?
| Limit | Value |
|---|---|
| Keys per object | 5 |
| Token lifetime | 10 minutes at most |
| Token length | 8,192 characters, room for about 200 short codes. @vistazo/embed won't sign a longer one |
| Old secret after a rotation | Keeps working for 24 hours |
| New filter combinations per dashboard | 500 a day |