Skip to main content

Embed Portal: persist your setup as code

Introduction​

Your backend tells Holistics which dashboards and datasets to show by listing each one by name in the embed payload. This means every embedded object must be kept in sync between your Holistics project and your backend. When the two sides don't match, your production embed breaks and customers see an error instead of their analytics:

A dashboard rename breaks the embed until your backend is redeployed
  • A rename breaks your embed until the backend updates and redeploys
  • [HTML node]
  • [HTML node]
  • [HTML node]
  • [HTML node]
  • [HTML node]
  • [HTML node]
  • [HTML node]
  • [HTML node]
  • [HTML node]
  • [HTML node]
  • [HTML node]
  • [HTML node]
  • [HTML node]
  • [HTML node]
  • [HTML node]
  • [HTML node]
  • [HTML node]
  • [HTML node]
  • [HTML node]
  • [HTML node]
  • [HTML node]
  • a-b-before → h-old (refers to)
  • a-b-between → ghost (✗ not found)
  • b-new → a-h-after (refers to)
  • a-b-between-down → c-2

An Embed Portal solves the sync problem. It's an AML file in your Holistics project that lists the dashboards and datasets you want to embed.

Your payload then only needs to reference the portal by name, and everything else in it (viewer identity, permissions, and settings) stays the same. When something changes, you update the portal in Holistics, and your backend doesn't need a redeploy.

Before: the embed payload in your app backend lists every dashboard and dataset. After: the payload references one Embed Portal, and the portal lists the objects inside your Holistics project.
  • How an Embed Portal solves the sync problem
  • Before: Your payload lists every object
  • Embed payload objects: { dashboards: ['sales_overview'], datasets: ['ecommerce'], }
  • Holistics project
  • Your app backend
  • Dashboards & datasets sales_overview dashboard ecommerce dataset
  • After: Your payload references one Embed Portal
  • Holistics project
  • Your app backend
  • Embed payload + embed_portal: 'customer_portal' - objects: { ... }
  • Embed Portal (customer_portal.embed.aml) EmbedPortal customer_portal { objects: [ sales_overview, ecommerce, ] }
  • Dashboards & datasets sales_overview dashboard ecommerce dataset
  • bp-sales → bo-sales (references)
  • bp-ecommerce → bo-ecommerce
  • at-sales → ao-sales
  • at-ecommerce → ao-ecommerce
  • ap-portal → at-portal (references)

Why use an Embed Portal​

Beyond removing the sync problem, a portal makes embed changes safer and easier to manage:

  • No broken embeds. Holistics validates every reference before you publish, so errors are caught before they reach production.
  • No backend redeploys. Add, remove, or rename embedded dashboards and datasets without redeploying your backend.
  • Version controlled. Portals live in your Holistics project, so every change is version-controlled alongside your models and dashboards.
  • Preview before going live. Test the portal in Development and see exactly what your embed users will get. See Step 2: Preview and publish.

Step 1: Define your Embed Portal​

In Development mode, create a new file with the naming convention *.embed.aml, and list the dashboards and datasets you want to embed.

customer_portal.embed.aml
EmbedPortal customer_portal { // object with a unique name
objects: [
sales_overview, // dashboard
revenue_trends, // dashboard
ecommerce, // dataset: enables exploration, drill-down, and Ask AI
],
}

Objects are unique across your Holistics project, so you only list the names. Holistics detects whether each one is a dashboard or a dataset.

Step 2: Preview and publish​

Preview the portal before it reaches production by clicking Preview in the file header. This lets you validate changes, catch broken references, and test the experience your embed users will get.

Once you're satisfied, publish your changes so the portal is available for embedding. See Publishing to production.

Step 3: Reference the Embed Portal from your payload​

Replace the objects block in your payload with embed_portal and the portal's name. Everything about the viewer stays in the payload exactly as before.

Your backend: payload
const payload = {
objects: {
dashboards: ['sales_overview', 'revenue_trends'],
datasets: ['ecommerce'],
},
embed_portal: 'customer_portal',

// viewer identity and permissions stay in the payload
embed_user_id: 'user_123',
embed_org_id: 'org_456',
user_attributes: {
company_id: [42],
},
settings: {
allow_dashboard_export: true,
},
exp: Math.floor(Date.now() / 1000) + 24 * 60 * 60,
};

const token = jwt.sign(payload, embedSecret, { algorithm: 'HS256' });

Your iframe still points at /embed/{embed_key_id}?_token={token} with the same Key ID.

One source of objects

A payload references either a portal or inline objects, not both. If you set embed_portal, leave out objects.dashboards and objects.datasets: a payload that sets both is rejected.

More Embed Portal settings​

Besides objects, a portal can set the initial object (the page viewers land on) and white-label the Ask AI assistant.

Initial object​

By default, the embed opens on the first dashboard or dataset in objects. Use initial_object to pick a different starting page. It accepts:

  • An object: open a specific dashboard or dataset from objects.

    customer_portal.embed.aml
    EmbedPortal customer_portal {
    objects: [sales_overview, ecommerce],
    initial_object: ecommerce
    }
  • 'ai': open the Ask AI page (requires settings.ai.enabled: true in your payload).

    customer_portal.embed.aml
    EmbedPortal customer_portal {
    objects: [sales_overview, ecommerce],
    initial_object: 'ai'
    }

Note that you can override this setting for a single link by using a URL path such as /objects/sales_overview or /ai.

White-label Ask AI​

Add an ai block to replace Holistics' default branding with your own: a custom icon, an assistant name, a welcome message, and prompt placeholder text. See White-label Ask AI for every property and where it appears in the interface.

customer_portal.embed.aml
EmbedPortal customer_portal {
objects: [sales_overview, ecommerce],
ai {
customization: EmbedPortalAiCustomization {
global {
icon: 'https://cdn.example.com/logo.png'
assistant_name: 'Ask Hooli'
}
}
}
}
Customized Ask AI page

Next steps​


Open Markdown
Let us know what you think about this document :)