This document provides complete API reference for JWT structure and configuration options used across all embedding and sharing methods.Lightdash supports three ways to share embedded content:
Shareable URL — Generate a link that anyone can open directly in their browser, no iframe or SDK needed. Ideal for sharing dashboards with external users like clients or partners.
iframe embedding — Embed dashboards inside your own web pages using a standard <iframe> tag.
React SDK — Embed dashboards and charts in React/Next.js apps with full programmatic control.
All three methods use the same JWT-based authentication described below.For method-specific implementation details, see:
Embedded Lightdash content is available to view by anyone (not just folks with a Lightdash login). Content is secured using JWT (JSON Web Tokens) with configurable expiration times.
For embedding dashboards with multiple tiles, filters, and interactive features.
All configuration options (dashboardFiltersInteractivity, canExportCsv, canExplore, etc.) must be nested inside the content object — not at the top level of the JWT payload. A common mistake is placing these properties at the root level, which will cause them to be silently ignored.
{ content: { type: 'dashboard', // Dashboard identifier (required, use one) dashboardUuid?: string, dashboardSlug?: string, // Project identifier (optional) projectUuid?: string, // Filter interactivity dashboardFiltersInteractivity?: { enabled: 'all' | 'some' | 'none', // Required allowedFilters?: string[], // Required if enabled: 'some' hidden?: boolean, // Optional: hide filter UI }, // Parameter interactivity parameterInteractivity?: { enabled: boolean, }, // Export capabilities canExportCsv?: boolean, // Allow CSV export canExportImages?: boolean, // Allow image/PNG export canExportPagePdf?: boolean, // Allow PDF export // Interactive features canDateZoom?: boolean, // Allow date granularity zoom canExplore?: boolean, // Allow "Explore from here" canViewUnderlyingData?: boolean, // Allow viewing raw data canViewDataApps?: boolean, // Allow rendering data app tiles }, // Optional: Allow embedded users to save new charts from Explore writeActions?: { spaceUuid: string, // Destination space for created content serviceAccountUserUuid?: string, // Actor for the write (use one) userUuid?: string, }, // Optional: User information for query tracking user?: { externalId?: string, email?: string, }, // Optional: User attributes for row-level filtering userAttributes?: { [attributeName: string]: string, },}
Chart tokens use contentId (the saved chart UUID) instead of dashboardUuid. Chart embeds are scoped to the specific chart and cannot access other content.
Allows users to view the raw data table behind visualizations.
canViewUnderlyingData?: boolean
When enabled, users can click on charts to open a modal showing the underlying data table. Data cannot be exported separately (use canExportCsv for that).
Allows data app tiles on an embedded dashboard to render and run their metric queries.
canViewDataApps?: boolean
Data app tiles run arbitrary metric queries against your semantic layer, so they need broader access than a standard chart tile. Enabling canViewDataApps grants the embed JWT the additional permissions a data app needs to mint a preview token and execute its queries. User attributes and SQL filters on the JWT still apply, so row-level access controls are enforced inside the data app exactly as they are on chart tiles.When this option is off (the default), data app tiles on the dashboard render as a placeholder and no queries run. Turn it on when you trust the embed audience to see the data the app can request and you want the tile to behave the same as it does in Lightdash.
Write actions let embedded users save changes back to Lightdash. When the JWT includes a writeActions claim, Lightdash uses a configured actor (a service account or a regular user) to perform the write on behalf of the embedded viewer, and forces created or edited content into a specific destination space.With writeActions, embedded users can:
Save a new chart from the embedded Explore view.
Edit an existing embedded dashboard with the React SDK — rename it, add saved charts from the allowed space, and move or resize tiles. See Lightdash.Dashboard edit mode.
This is useful when you want to let your customers explore data, change charts and dashboards, and save the result back to Lightdash — without giving them a Lightdash login.
writeActions?: { spaceUuid: string, // Required: destination space for created content serviceAccountUserUuid?: string, // Use a service account as the actor userUuid?: string, // Or use an existing Lightdash user as the actor}
Rules:
spaceUuid is required and must belong to the same project as the embed.
You must provide exactly one of serviceAccountUserUuid or userUuid.
The actor must belong to the same organization as the project and (for userUuid) be active.
The actor’s permissions and space access still apply — the embed inherits whatever the actor can do in that space.
When editing or building a dashboard, add-tile content is filtered to spaceUuid, so embedded users can only pick saved charts from the allowed space.
Dashboards and charts created or edited through write actions are normal Lightdash objects — they can be viewed and edited from Lightdash and vice versa.
Newly saved charts are not added to the embed allowlist automatically. They behave like normal private content and won’t be re-embeddable unless you add them explicitly.
Configure write actions from Settings → Embedding by toggling Enable write actions and selecting:
Service account — the Lightdash actor that performs the write. You can pick an existing service account or create one inline. Only service accounts with a writable role (Admin, Developer, Editor, or an equivalent custom role) can be used.
Space for created content — the destination space. You can pick an existing space or create one inline.
The settings panel generates a JWT snippet that includes the writeActions claim with the selected service account user UUID and space UUID. Copy that snippet into your backend token-generation code.
Service accounts are the recommended actor for embed write actions because their attribution stays consistent regardless of which embedded user is viewing.
import jwt from 'jsonwebtoken';const token = jwt.sign({ content: { type: 'dashboard', dashboardUuid: 'your-dashboard-uuid', canExplore: true, // Required to reach the Explore view from a chart tile }, writeActions: { serviceAccountUserUuid: 'service-account-user-uuid', spaceUuid: 'destination-space-uuid', }, userAttributes: { tenant_id: 'tenant-abc', },}, LIGHTDASH_EMBED_SECRET, { expiresIn: '1h' });
If you want saved charts attributed to a specific Lightdash user (e.g. an internal owner), use userUuid instead. The user must be active in the organization.
Embedded users cannot pick a different space when saving. The space is fixed by the spaceUuid in the JWT. This keeps embedded write activity contained and predictable.
For self-hosted deployments, you can configure new project embeds to allow all dashboards and/or charts by default using environment variables:
Variable
Description
Default
EMBED_ALLOW_ALL_DASHBOARDS_BY_DEFAULT
When creating new embeds, allow all dashboards by default
false
EMBED_ALLOW_ALL_CHARTS_BY_DEFAULT
When creating new embeds, allow all charts by default
false
When these are set to true, new project embeddings will automatically have all dashboards or charts allowed without needing to manually configure the allowed content list.See the environment variables reference for the complete list of embedding-related configuration options.
User attributes enable row-level and column-level security by filtering data based on user properties. Column-level security is especially useful when embedded users can Explore from here, since it ensures restricted fields stay hidden in the explore view as well.
The JWT userAttributes field drives both row-level and column-level access controls, the same ones available to Lightdash account users:
Row-level filtering: attributes are substituted into any sql_filter on the dbt model (e.g. ${lightdash.attributes.tenant_id}), restricting which rows the embedded user can query.
Column-level filtering: attributes are matched against required_attributes and any_attributes rules on dimensions, metrics, and tables. Fields whose rules the embedded user’s attributes don’t satisfy are stripped from the explore entirely: they don’t appear in the field list, can’t be queried, and return Forbidden if requested directly. Metrics derived from a hidden dimension are hidden too.
Pass user information to track who’s viewing embedded content.
user?: { externalId?: string, // Your internal user ID email?: string, // User's email address}
This metadata appears in query tags for usage analytics. If you don’t provide an externalId, Lightdash automatically generates one based on the embed token.Example: