Available on the Enterprise plan. Embedding and billing accounts are Enterprise features.
- Every tenant gets its own Basedash organization, so their chats, dashboards, insights, and members stay separate.
- All tenants share the same schema in one warehouse or database, with a column such as
tenant_idsaying which rows belong to whom. - You provision organizations and maintain a standard set of dashboards and models (“templates”) from your own code, using the public API.
Give this recipe to your AI agent
The agent brief is a Markdown spec your coding agent can follow to scaffold
the provisioning and sync scripts. Point it at
https://www.basedash.com/docs/recipes/agents/multi-tenant-embedding.md.How it fits together
You own three things: a provisioning script that creates and updates organizations, a set of templates checked into your repo, and a mapping from your tenant IDs to Basedash organization IDs. Basedash holds one organization per tenant, all attached to a single billing account.Decide how tenant data is isolated
This is the most important decision in the recipe. With full app embedding, your tenants’ users and the Basedash AI write their own SQL, so a filter in a dashboard is not a security boundary. Isolation has to be enforced by the database, through the credentials each organization connects with. How you do that depends on where your data lives. Pick your data source; the rest of this page follows your choice.- Redshift
- Postgres
The Since every tenant’s schema has the same tables and columns, your templates can use a placeholder such as
basedash.groups session variable that powers Basedash row level security is only set for Postgres, so on Redshift each tenant connects with its own database user.For each tenant, create a database user and a schema of views that only return that tenant’s rows. Give the user access to that schema and nothing else, so Basedash only ever sees the tenant’s own data, both when running queries and when reading the schema.{{schema}}.orders that the provisioning script replaces with tenant_acme before sending the SQL.If you prefer to keep a single schema, Redshift’s own row-level security policies can restrict each tenant user to its rows. Then template SQL needs no placeholder. Test the policies with each tenant user before relying on them.Snowflake, BigQuery, and other warehouses follow the same pattern: one user, role, or service account per tenant that can only read that tenant’s rows.Before you start
- A service account. Create a Basedash user for automation, such as
basedash-admin@yourcompany.com, and an API key for it. An API key acts as its user, and that user becomes an admin of every organization it creates, so keep it separate from a person’s account. - A billing account. Ask Basedash support to create one and attach it to your main organization. Organizations created with its
billingAccountIdare billed through it, with no trial. The service account must be an admin of the account’s owner organization. - A home for the mapping. A table in your own database with your tenant ID, the Basedash organization ID, and the IDs of the data source, dashboards, charts, and models created from templates.
- Your app’s origins, such as
https://app.yourcompany.com, for the embed allow list.
Provision a tenant
1
Create the organization
id and jwtSecret with the tenant. Each organization has its own JWT secret, which you can read again later with GET /organizations/{id}?include=jwtSecret.Use a slug derived from your tenant ID. If the request fails partway, a retry that gets 409 SlugAlreadyExists tells you the organization already exists, so look it up instead of creating a second one.2
Connect the tenant's data source
- Redshift
- Postgres
Create the tenant’s Redshift user and views first (see above), then create the data source with that user’s credentials:
POST /organizations/{orgId}/data-sources/verify takes the same connection fields and tests them without saving anything.3
Apply your templates
Create models first, then dashboards, then the charts on each dashboard, all pointing at the new data source ID. Store each created ID in your mapping, keyed by the template’s own stable key, so later syncs update the same objects instead of creating duplicates.See Keep tenants in sync for how to structure templates.
Keep tenants in sync
The API doesn’t copy dashboards between organizations, so your repo is the source of truth for templates. A simple layout:key (for example dashboards/overview) that never changes, plus the fields the API accepts. A sync job, run on deploy or on a schedule, does this for every tenant:
- Look up the Basedash ID for each template key in your mapping.
- If there is none, create the object and save the new ID.
- If there is one, update it with
PATCH. A404means someone deleted it, so create it again. - Delete objects whose template you removed.
Operate at scale
- Rate limits. API keys are rate limited. A
429response includes aRetry-Afterheader; wait that long and retry. Run tenants a few at a time rather than all at once. - Bulk changes. For a one-off change across hundreds or thousands of organizations, such as a new setting or a shared AI instruction, contact support. It’s often faster for us to apply it in one go.
- Insights. Every organization generates insights on its own schedule. Set
insightsFrequencydeliberately (NEVERif you generate insights through the API), since thousands of daily runs add up in AI usage and warehouse load. - AI usage limits.
aiUsageOverageLimitCentscaps each tenant’s AI spend within the billing account’s pooled credits. - Warehouse load. Each organization runs its own queries and schema syncs. Size your warehouse for your tenant count, and route Basedash users to their own queue or pool (for example a Redshift WLM queue) so analytics can’t starve your production workloads.
- Offboarding.
DELETE /organizations/{id}removes the organization. On Redshift, drop the tenant’s database user and views at the same time.
Checklist
- Each tenant can read only its own rows. On Redshift, test with a query as the tenant’s user; on Postgres, as a user in the tenant’s group.
- Organizations are created with
billingAccountId, so none end up on a trial. - Embed JWTs are signed server-side with the tenant’s own
orgIdand secret. -
embedAllowedOriginsis set on every organization. - Your mapping stores every ID created from a template, and the sync is safe to run twice.
- Your scripts retry
429responses afterRetry-After.
Related
Embedding
JWT claims, iframe options, and allowed origins.
Row level security
Postgres policies keyed on Basedash groups.
API overview
Authentication and response formats.
Agent brief
This recipe as a spec for your coding agent.