This is the single reference for how DataBrain enforces tenant isolation and metric visibility when you embed dashboards in a multi-tenant application.
Concepts, top-down
- Workspace — outermost container. Holds Data Sources, Datamarts, Data Apps, Dashboards, Metrics. Members and permissions are Workspace-scoped. Most teams run one Workspace.
- Data Source — connection to your warehouse. Credentials live here.
- Datamart — logical selection of tables, columns, relationships, and joins on top of a Data Source. This is where the Client ID column for tenant isolation is set, and where the semantic layer lives.
- Data App — the unit of embedding. Carries the API key that authenticates guest-token minting and the reference to its Datamart. One Data App per embedding surface — you do not need a Data App per tenant.
- Client Group — a logical grouping of end-tenants that share a dashboard structure.
- Manage Metrics — a per-dashboard toggle that lets users pick which metrics appear. See the gotcha below before relying on it.
The tenant identifier: three names, three places
| Name | Where | What it is |
|---|
clientId | Guest-token JSON payload | Supplies the tenant value for the session. The only accepted spelling — client_id and tenant_id are rejected |
'client_id_variable' | Custom metric SQL | A bare quoted token (no curly braces) substituted with the session’s tenant value at query time, e.g. WHERE client_column = 'client_id_variable' |
{{DATABASE_NAME}} | Custom metric SQL | The only curly-brace tenancy variable — resolves to the tenant’s database/schema for database-per-tenant setups |
Tenant-isolation strategies
1. Row-per-tenant (client_id column)
All tenants share tables; every table has a tenant column. Configure the Datamart’s Client ID column mapping. How the filter is applied depends on the metric type:
- Builder (visual/dataset) metrics: DataBrain auto-injects the tenant filter into the query. Do not add your own.
- Custom SQL metrics: the filter is not auto-injected. You must write it yourself:
SELECT * FROM orders WHERE client_id = 'client_id_variable'
A custom SQL metric that omits the tenant filter runs unscoped and returns every tenant’s rows. Always include 'client_id_variable' in custom SQL when your Datamart uses table-level tenancy.
2. Schema- or database-per-tenant
Each tenant has its own schema/database. Reference it in metric SQL via {{DATABASE_NAME}}, which resolves from the guest token’s clientId through the Datamart mapping.
Anti-pattern: hard-coded tenant IDs
Never save a literal tenant id in metric SQL (WHERE client_id = 'acme') — for example by saving a metric while previewing as a specific client. That literal ships to every viewer and has caused real cross-tenant data leaks. If a metric must be scoped to one tenant on purpose, use RLS or an App Filter in the guest token instead.
Manage Metrics scope — the important gotcha
Manage Metrics visibility is per-dashboard, not per-viewer. If Customer A unchecks a metric on a shared dashboard, it disappears for Customer B too. That is by design — do not use Manage Metrics for per-tenant metric visibility. Use the pattern below instead.
Master + client dashboards pattern
For “each customer picks their own metric subset from a shared library”:
- Create one master dashboard with every metric in the library.
- For each customer (or Client Group), create a client dashboard derived from the master — its own layout and visible-metric subset.
- Embed the client dashboard per tenant with that tenant’s
clientId in the guest token.
Client dashboards can be provisioned programmatically — via the Import Dashboard API or the MCP server’s create_embed tool (which can clone a templateDashboardId per client). Note that import/clone is a copy operation: later edits to the master do not propagate to already-created client dashboards.
Row-level security (RLS)
RLS (params.rlsSettings in the guest token) is an additional per-session constraint on top of tenant isolation — for example region = 'NA' for one user. It is enforced server-side and invisible to the end user. Do not use RLS as a substitute for the Datamart’s Client ID mapping.
Client selector
Embedded dashboards never render a client selector — the tenant is fixed by the guest token’s clientId. The client/database dropdown you see inside the DataBrain app is for workspace members previewing data as different clients; it is not part of the embed.
Where each setting lives
| Setting | Where | Scope |
|---|
| Client ID column mapping | Datamart | Per Datamart |
| Data App API key | Data App | Per Data App |
| Whitelist Domains | Edited from Data App settings | Account-wide (one list for all Data Apps) |
clientId, rlsSettings, appFilters | Guest token payload | Per session |
| Manage Metrics toggle | Dashboard settings | Per dashboard, shared across viewers |
| Client Group membership | Workspace settings | Per tenant grouping |