diff --git a/docs-mintlify/embedding/iframe/creator-mode.mdx b/docs-mintlify/embedding/iframe/creator-mode.mdx
index 793d2d952911d..63b1851cfe0b5 100644
--- a/docs-mintlify/embedding/iframe/creator-mode.mdx
+++ b/docs-mintlify/embedding/iframe/creator-mode.mdx
@@ -7,48 +7,29 @@ description: Embed the full Cube application so users can build and modify their
Creator Mode is available on the [Enterprise plan](https://cube.dev/pricing).
-Creator Mode lets you embed the entire Cube application into your product so users can create and modify their own dashboards directly within the embedded experience.
+Creator Mode embeds the full Cube application instead of an individual dashboard or chat. Users can create and modify workbooks, dashboards, and reports inside the iframe.
+
+To enable it, pass `creatorMode: true` to the [Generate Session API][ref-generate-session].
Creator Mode requires [Signed embedding](/embedding/iframe/auth/signed). Private embedding is not supported.
-## How it works
-
-In Creator Mode, you embed the full Cube application instead of individual dashboards or chat interfaces. This provides users with the complete Cube experience, including the ability to:
-
-- Create new dashboards
-- Modify existing dashboards
-- Access all workbook and dashboard functionality
-- Build custom analytics experiences
-
-To enable Creator Mode, pass `creatorMode: true` to the [Generate Session API][ref-generate-session] when generating an embed session. Optionally, pass `embedTenantName` to scope content to a specific embed tenant — by default, creator mode uses the current tenant's name.
+## Embed tenant scoping
-## Embed tenant name
+In Creator Mode, content (workbooks, dashboards) and the groups/user attributes referenced by the session are scoped to an **embed tenant**. Pass `embedTenantName` to isolate content per customer; omit it to use the current tenant.
-`embedTenantName` is an optional parameter on the Generate Session API used to scope all content (dashboards, workbooks, etc.) that users create within the embedded application to a specific embed tenant. This ensures proper data isolation in multi-tenant scenarios.
+`embedTenantName` must be lowercase, 5–36 chars, start with a letter, end with a letter or digit, and contain only `a-z`, `0-9`, or `-`.
-When `embedTenantName` is omitted in creator mode, the session defaults to the current tenant, so most setups don't need to set it.
+## Provisioning groups and user attributes
-The value must be lowercase, start with a letter, end with a letter or number, and only contain letters, numbers, or hyphens (length 5–36).
+In Creator Mode, `groups` and `userAttributes` resolve against the embed tenant's scoped catalog (separate from the tenant-wide one used by read-only embedding). To define those entries from your backend in the same call, pass `groupDefinitions` and `userAttributeDefinitions` — they are idempotent and validated before memberships and values are applied.
-## Using Creator Mode
+See [Generate Session → Creator mode bootstrap][ref-bootstrap] for the input shape, value types, and the immutable-`type` rule on attribute definitions.
-To use Creator Mode and embed the app:
-
-1. **Set the embed type to "App"** in the form
-2. **Fill in Deployment ID** and either **External ID** or **Internal ID** (email). Optionally provide an **Embed Tenant Name** to scope content to a specific embed tenant
-3. **Click "Generate Session & Embed"** — the request automatically includes `creatorMode: true` for app embeddings
-4. The app is embedded at `/embed/d/{deploymentId}/app?session={sessionId}` and displayed in the iframe
-
-Creator mode is enabled automatically when the embed type is "app"; no additional configuration is needed.
-
-### Example
+## Example
```javascript
-const API_KEY = "YOUR_API_KEY";
-const DEPLOYMENT_ID = 32;
-
const session = await fetch(
"https://your-account.cubecloud.dev/api/v1/embed/generate-session",
{
@@ -58,22 +39,18 @@ const session = await fetch(
Authorization: `Api-Key ${API_KEY}`,
},
body: JSON.stringify({
- deploymentId: DEPLOYMENT_ID,
+ deploymentId: 32,
externalId: "user@example.com",
- creatorMode: true,
- // Optional — defaults to the current tenant when omitted:
embedTenantName: "acme-corp",
+ creatorMode: true,
}),
},
);
-const data = await session.json();
-const sessionId = data.sessionId;
+const { sessionId } = await session.json();
```
-### Embedding the app
-
-Use the session ID to embed the full Cube application:
+Embed the app with the returned session ID:
```html
```
-Replace `{deploymentId}` with your deployment ID and `{sessionId}` with the session ID returned from the Generate Session API.
-
-## Example application
-
-For a complete working example of embedding, including Creator Mode, check out the [cube-embedding-demo](https://github.com/cubedevinc/cube-embedding-demo) repository. This demo application provides:
-
-- A full working example of iframe embedding
-- Implementation of signed iframe embedding with session generation
-- Support for creator mode with an optional embed tenant name
-- A React-based UI for testing embedding functionality
-- Backend server that securely handles API key authentication
-
-You can clone the repository, configure it with your Cube credentials, and run it locally to test embedding functionality or use it as a reference implementation for your own application.
+For a complete working example, see the [cube-embedding-demo](https://github.com/cubedevinc/cube-embedding-demo) repository.
[ref-generate-session]: /reference/embed-apis/generate-session
+[ref-bootstrap]: /reference/embed-apis/generate-session#creator-mode-bootstrapping-groups-and-user-attributes
diff --git a/docs-mintlify/reference/embed-apis/generate-session.mdx b/docs-mintlify/reference/embed-apis/generate-session.mdx
index d33e3ecd0b8fb..c14a05fedb2d8 100644
--- a/docs-mintlify/reference/embed-apis/generate-session.mdx
+++ b/docs-mintlify/reference/embed-apis/generate-session.mdx
@@ -32,15 +32,21 @@ POST https://{accountName}.cubecloud.dev/api/v1/embed/generate-session
| Field | Type | Required | Description |
|-------|------|----------|-------------|
-| `externalId` | string | No | Unique identifier for the external user. Either `externalId` or `internalId` should be provided. |
-| `internalId` | string | No | Email address of an internal Cube Cloud user. Either `externalId` or `internalId` should be provided. |
-| `userAttributes` | array | No | Array of `{name, value}` pairs for row-level security. Not allowed with `internalId`. |
-| `groups` | string[] | No | Array of group names for user. Not allowed with `internalId`. |
+| `deploymentId` | number | Yes | ID of the deployment the session should grant access to. |
+| `externalId` | string | Conditional | Stable identifier for the external user. Provide either `externalId` or `internalId` (not both). Must be lowercase and trimmed. |
+| `internalId` | string | Conditional | Username of an existing internal Cube Cloud user. Provide either `externalId` or `internalId` (not both). The user must already exist. |
+| `email` | string | No | Email to attach to the provisioned external user. Used only with `externalId`. |
+| `embedTenantName` | string | No | Embed tenant to scope content to. Lowercase, 5–36 chars, must start with a letter and end with a letter or digit, only `a-z`, `0-9`, `-`. Defaults to the current tenant. |
+| `creatorMode` | boolean | No | When `true`, mints a [creator-mode][ref-creator-mode] session and resolves groups/attributes against the embed tenant's scoped tables. Requires the `useCreatorMode` tenant flag. |
+| `userAttributes` | array | No | Attribute values for row-level security. See [User attributes](#user-attributes). Not allowed with `internalId`. |
+| `groups` | string[] | No | Group memberships for the user. See [Groups](#groups). Not allowed with `internalId`. |
+| `userAttributeDefinitions` | array | No | Idempotently upsert attribute definitions before applying values. Requires `creatorMode: true`. See [Creator mode bootstrap](#creator-mode-bootstrapping-groups-and-user-attributes). |
+| `groupDefinitions` | array | No | Idempotently upsert group definitions before assigning memberships. Requires `creatorMode: true`. See [Creator mode bootstrap](#creator-mode-bootstrapping-groups-and-user-attributes). |
| `securityContext` | object | No | Custom security context object passed to Cube queries. Not allowed with `internalId`. |
-When using `internalId`, the user must already exist in Cube Cloud. You cannot specify `roles`, `groups`, `userAttributes`, or `securityContext` with `internalId` — the internal user's existing permissions are used instead.
+When using `internalId`, the user must already exist in Cube Cloud. You cannot specify `groups`, `userAttributes`, `groupDefinitions`, `userAttributeDefinitions`, or `securityContext` with `internalId` — the internal user's existing permissions are used instead.
@@ -50,6 +56,167 @@ Accounts are limited to 10,000 external users. To increase this limit, please co
+## User attributes
+
+`userAttributes` is an array of `{ name, value }` pairs that drive row-level security in queries.
+
+```json
+{
+ "userAttributes": [
+ { "name": "department", "value": "Sales" },
+ { "name": "tier", "value": 2 },
+ { "name": "regions", "value": ["us-east", "eu-west"] },
+ { "name": "thresholds", "value": [10, 25, 50] }
+ ]
+}
+```
+
+| Property | Type | Notes |
+|----------|------|-------|
+| `name` | string | Must reference an existing attribute definition (see lookup rules below). |
+| `value` | `string` \| `number` \| `string[]` \| `number[]` \| `null` | The value type must match the definition's `type`. `null` clears the value. |
+
+**Attribute definition lookup**:
+
+- **Read-only mode** (`creatorMode` omitted or `false`): names are resolved against the tenant-wide attribute catalog (managed in **Settings → User Attributes** or via the admin GraphQL API). Any name not present there fails with `User attributes not found`.
+- **Creator mode** (`creatorMode: true`): names are resolved against the embed tenant's scoped catalog (`embed_user_attributes`). Use `userAttributeDefinitions` in the same request to upsert definitions on the fly — see [Creator mode bootstrap](#creator-mode-bootstrapping-groups-and-user-attributes).
+
+**Rules**:
+
+- Duplicate `name` entries are rejected with `400 Bad Request`.
+- Values are persisted per user. Subsequent calls with the same `externalId` overwrite previous values for the supplied names.
+
+## Groups
+
+`groups` is an array of group **names** (not IDs) that the user should belong to. Group definitions must already exist (or be created in the same request via `groupDefinitions` in creator mode).
+
+```json
+{ "groups": ["analysts", "marketing"] }
+```
+
+**Behavior**:
+
+| Value | Effect |
+|-------|--------|
+| Field omitted (`undefined`) | Existing memberships are preserved. |
+| `[]` (empty array) | All memberships are cleared. |
+| Populated array | Memberships are replaced with exactly the supplied names. |
+
+**Group definition lookup**:
+
+- **Read-only mode**: names are resolved against tenant-wide groups (managed in **Settings → Groups** or via the admin GraphQL API). The membership row references the global group.
+- **Creator mode**: names are resolved against the embed tenant's scoped groups (`embed_user_groups`). Use `groupDefinitions` in the same request to upsert them.
+
+If any name in `groups` cannot be resolved, the request fails with `Groups with names not found`.
+
+## Creator mode: bootstrapping groups and user attributes
+
+In API-first integrations you often want to mint an embed session and define the groups/attributes it references in a single call, without first making a round trip to the admin UI. The `groupDefinitions` and `userAttributeDefinitions` fields do that — they idempotently upsert definitions in the embed tenant's scoped tables and are validated **before** `groups` and `userAttributes` are applied.
+
+Both fields:
+
+- Require `creatorMode: true`.
+- Require an `embedTenantName` (definitions are only meaningful inside an embed tenant).
+- Require the `useCreatorMode` tenant flag — contact support to enable.
+- Land in the embed-tenant scope only — they never modify tenant-wide groups or attributes.
+- Are idempotent: running the same request twice produces the same end state.
+
+### `groupDefinitions`
+
+```json
+{
+ "groupDefinitions": [
+ { "name": "analysts", "description": "Read-only viewers" },
+ { "name": "marketing" }
+ ]
+}
+```
+
+| Property | Type | Required | Notes |
+|----------|------|----------|-------|
+| `name` | string | Yes | Group name. Existing groups with this name are reused. |
+| `description` | string | No | Updated when supplied and different from the stored value. Never cleared. |
+
+Duplicate `name` entries within the same request are rejected.
+
+### `userAttributeDefinitions`
+
+```json
+{
+ "userAttributeDefinitions": [
+ {
+ "name": "department",
+ "type": "string",
+ "displayName": "Department",
+ "defaultValue": "Unassigned",
+ "description": "Org unit"
+ }
+ ]
+}
+```
+
+| Property | Type | Required | Notes |
+|----------|------|----------|-------|
+| `name` | string | Yes | Attribute name. Existing attributes with this name are reused. |
+| `type` | enum | Yes | One of `string`, `number`, `string_array`, `number_array`. **Immutable** — see below. |
+| `displayName` | string | No | Updated when supplied and different from the stored value. |
+| `defaultValue` | string | No | Updated when supplied and different from the stored value. |
+| `description` | string | No | Updated when supplied and different from the stored value. |
+
+**`type` is immutable.** If a definition with the supplied `name` already exists with a different `type`, the request fails with `cannot change type` and nothing is upserted. This protects every value already stored against that attribute from silently becoming invalid. To change the type, delete the attribute via the [embed-tenant admin API](#embed-tenant-admin-api) and recreate it.
+
+Duplicate `name` entries within the same request are rejected.
+
+### Bootstrap example
+
+Define a group and an attribute, assign the user to both, and mint a session — all in one call:
+
+```javascript
+const session = await fetch(
+ `https://${ACCOUNT_NAME}.cubecloud.dev/api/v1/embed/generate-session`,
+ {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: `Api-Key ${API_KEY}`,
+ },
+ body: JSON.stringify({
+ deploymentId: DEPLOYMENT_ID,
+ externalId: 'user-123',
+ embedTenantName: 'acme-corp',
+ creatorMode: true,
+
+ // Upserted before validation runs
+ groupDefinitions: [
+ { name: 'analysts', description: 'Read-only viewers' },
+ ],
+ userAttributeDefinitions: [
+ { name: 'department', type: 'string', displayName: 'Department' },
+ ],
+
+ // Reference the names we just defined
+ groups: ['analysts'],
+ userAttributes: [{ name: 'department', value: 'Sales' }],
+ }),
+ },
+);
+```
+
+A second call with the same body produces the same end state: the group and attribute already exist, descriptions/display names are reconciled if they changed, and the user's memberships and values are re-applied.
+
+## Embed-tenant admin API
+
+To list or delete the groups and attributes that have been bootstrapped into an embed tenant, use the admin endpoints scoped to that tenant:
+
+```text
+GET /api/v1/embed-tenants/{embedTenantName}/groups
+DELETE /api/v1/embed-tenants/{embedTenantName}/groups/{id}
+GET /api/v1/embed-tenants/{embedTenantName}/user-attributes
+DELETE /api/v1/embed-tenants/{embedTenantName}/user-attributes/{id}
+```
+
+These endpoints use the same `Api-Key` authentication as Generate Session and require admin access. List endpoints return cursor-paginated results (`?first=`, `?after=`).
+
### Response
The API returns a session object:
@@ -84,10 +251,12 @@ response = requests.post(
'Authorization': f'Api-Key {API_KEY}'
},
json={
+ 'deploymentId': 32,
'externalId': 'user@example.com',
'userAttributes': [
{'name': 'department', 'value': 'Sales'}
- ]
+ ],
+ 'groups': ['analysts']
}
)
@@ -108,10 +277,12 @@ const response = await fetch(
'Authorization': `Api-Key ${API_KEY}`
},
body: JSON.stringify({
+ deploymentId: 32,
externalId: 'user@example.com',
userAttributes: [
{ name: 'department', value: 'Sales' }
- ]
+ ],
+ groups: ['analysts']
})
}
);
@@ -124,10 +295,12 @@ curl -X POST "https://your-account.cubecloud.dev/api/v1/embed/generate-session"
-H "Content-Type: application/json" \
-H "Authorization: Api-Key YOUR_API_KEY" \
-d '{
+ "deploymentId": 32,
"externalId": "user@example.com",
"userAttributes": [
{"name": "department", "value": "Sales"}
- ]
+ ],
+ "groups": ["analysts"]
}'
```
@@ -136,4 +309,5 @@ curl -X POST "https://your-account.cubecloud.dev/api/v1/embed/generate-session"
Use session ID in [signed embedding][ref-signed-embedding].
[ref-api-keys]: /admin/account-billing/api-keys
-[ref-signed-embedding]: /embedding/iframe/auth/signed
\ No newline at end of file
+[ref-signed-embedding]: /embedding/iframe/auth/signed
+[ref-creator-mode]: /embedding/iframe/creator-mode
diff --git a/docs/content/product/apis-integrations/embed-apis/generate-session.mdx b/docs/content/product/apis-integrations/embed-apis/generate-session.mdx
index 49f5460268f48..60843f85ab12e 100644
--- a/docs/content/product/apis-integrations/embed-apis/generate-session.mdx
+++ b/docs/content/product/apis-integrations/embed-apis/generate-session.mdx
@@ -29,15 +29,21 @@ POST https://{accountName}.cubecloud.dev/api/v1/embed/generate-session
| Field | Type | Required | Description |
|-------|------|----------|-------------|
-| `externalId` | string | No | Unique identifier for the external user. Either `externalId` or `internalId` should be provided. |
-| `internalId` | string | No | Email address of an internal Cube Cloud user. Either `externalId` or `internalId` should be provided. |
-| `userAttributes` | array | No | Array of `{name, value}` pairs for row-level security. Not allowed with `internalId`. |
-| `groups` | string[] | No | Array of group names for user. Not allowed with `internalId`. |
+| `deploymentId` | number | Yes | ID of the deployment the session should grant access to. |
+| `externalId` | string | Conditional | Stable identifier for the external user. Provide either `externalId` or `internalId` (not both). Must be lowercase and trimmed. |
+| `internalId` | string | Conditional | Username of an existing internal Cube Cloud user. Provide either `externalId` or `internalId` (not both). The user must already exist. |
+| `email` | string | No | Email to attach to the provisioned external user. Used only with `externalId`. |
+| `embedTenantName` | string | No | Embed tenant to scope content to. Lowercase, 5–36 chars, must start with a letter and end with a letter or digit, only `a-z`, `0-9`, `-`. Defaults to the current tenant. |
+| `creatorMode` | boolean | No | When `true`, mints a [creator-mode][ref-creator-mode] session and resolves groups/attributes against the embed tenant's scoped tables. Requires the `useCreatorMode` tenant flag. |
+| `userAttributes` | array | No | Attribute values for row-level security. See [User attributes](#user-attributes). Not allowed with `internalId`. |
+| `groups` | string[] | No | Group memberships for the user. See [Groups](#groups). Not allowed with `internalId`. |
+| `userAttributeDefinitions` | array | No | Idempotently upsert attribute definitions before applying values. Requires `creatorMode: true`. See [Creator mode bootstrap](#creator-mode-bootstrapping-groups-and-user-attributes). |
+| `groupDefinitions` | array | No | Idempotently upsert group definitions before assigning memberships. Requires `creatorMode: true`. See [Creator mode bootstrap](#creator-mode-bootstrapping-groups-and-user-attributes). |
| `securityContext` | object | No | Custom security context object passed to Cube queries. Not allowed with `internalId`. |
-When using `internalId`, the user must already exist in Cube Cloud. You cannot specify `roles`, `groups`, `userAttributes`, or `securityContext` with `internalId` — the internal user's existing permissions are used instead.
+When using `internalId`, the user must already exist in Cube Cloud. You cannot specify `groups`, `userAttributes`, `groupDefinitions`, `userAttributeDefinitions`, or `securityContext` with `internalId` — the internal user's existing permissions are used instead.
@@ -47,6 +53,167 @@ Accounts are limited to 10,000 external users. To increase this limit, please co
+## User attributes
+
+`userAttributes` is an array of `{ name, value }` pairs that drive row-level security in queries.
+
+```json
+{
+ "userAttributes": [
+ { "name": "department", "value": "Sales" },
+ { "name": "tier", "value": 2 },
+ { "name": "regions", "value": ["us-east", "eu-west"] },
+ { "name": "thresholds", "value": [10, 25, 50] }
+ ]
+}
+```
+
+| Property | Type | Notes |
+|----------|------|-------|
+| `name` | string | Must reference an existing attribute definition (see lookup rules below). |
+| `value` | `string` \| `number` \| `string[]` \| `number[]` \| `null` | The value type must match the definition's `type`. `null` clears the value. |
+
+**Attribute definition lookup**:
+
+- **Read-only mode** (`creatorMode` omitted or `false`): names are resolved against the tenant-wide attribute catalog (managed in **Settings → User Attributes** or via the admin GraphQL API). Any name not present there fails with `User attributes not found`.
+- **Creator mode** (`creatorMode: true`): names are resolved against the embed tenant's scoped catalog (`embed_user_attributes`). Use `userAttributeDefinitions` in the same request to upsert definitions on the fly — see [Creator mode bootstrap](#creator-mode-bootstrapping-groups-and-user-attributes).
+
+**Rules**:
+
+- Duplicate `name` entries are rejected with `400 Bad Request`.
+- Values are persisted per user. Subsequent calls with the same `externalId` overwrite previous values for the supplied names.
+
+## Groups
+
+`groups` is an array of group **names** (not IDs) that the user should belong to. Group definitions must already exist (or be created in the same request via `groupDefinitions` in creator mode).
+
+```json
+{ "groups": ["analysts", "marketing"] }
+```
+
+**Behavior**:
+
+| Value | Effect |
+|-------|--------|
+| Field omitted (`undefined`) | Existing memberships are preserved. |
+| `[]` (empty array) | All memberships are cleared. |
+| Populated array | Memberships are replaced with exactly the supplied names. |
+
+**Group definition lookup**:
+
+- **Read-only mode**: names are resolved against tenant-wide groups (managed in **Settings → Groups** or via the admin GraphQL API). The membership row references the global group.
+- **Creator mode**: names are resolved against the embed tenant's scoped groups (`embed_user_groups`). Use `groupDefinitions` in the same request to upsert them.
+
+If any name in `groups` cannot be resolved, the request fails with `Groups with names not found`.
+
+## Creator mode: bootstrapping groups and user attributes
+
+In API-first integrations you often want to mint an embed session and define the groups/attributes it references in a single call, without first making a round trip to the admin UI. The `groupDefinitions` and `userAttributeDefinitions` fields do that — they idempotently upsert definitions in the embed tenant's scoped tables and are validated **before** `groups` and `userAttributes` are applied.
+
+Both fields:
+
+- Require `creatorMode: true`.
+- Require an `embedTenantName` (definitions are only meaningful inside an embed tenant).
+- Require the `useCreatorMode` tenant flag — contact support to enable.
+- Land in the embed-tenant scope only — they never modify tenant-wide groups or attributes.
+- Are idempotent: running the same request twice produces the same end state.
+
+### `groupDefinitions`
+
+```json
+{
+ "groupDefinitions": [
+ { "name": "analysts", "description": "Read-only viewers" },
+ { "name": "marketing" }
+ ]
+}
+```
+
+| Property | Type | Required | Notes |
+|----------|------|----------|-------|
+| `name` | string | Yes | Group name. Existing groups with this name are reused. |
+| `description` | string | No | Updated when supplied and different from the stored value. Never cleared. |
+
+Duplicate `name` entries within the same request are rejected.
+
+### `userAttributeDefinitions`
+
+```json
+{
+ "userAttributeDefinitions": [
+ {
+ "name": "department",
+ "type": "string",
+ "displayName": "Department",
+ "defaultValue": "Unassigned",
+ "description": "Org unit"
+ }
+ ]
+}
+```
+
+| Property | Type | Required | Notes |
+|----------|------|----------|-------|
+| `name` | string | Yes | Attribute name. Existing attributes with this name are reused. |
+| `type` | enum | Yes | One of `string`, `number`, `string_array`, `number_array`. **Immutable** — see below. |
+| `displayName` | string | No | Updated when supplied and different from the stored value. |
+| `defaultValue` | string | No | Updated when supplied and different from the stored value. |
+| `description` | string | No | Updated when supplied and different from the stored value. |
+
+**`type` is immutable.** If a definition with the supplied `name` already exists with a different `type`, the request fails with `cannot change type` and nothing is upserted. This protects every value already stored against that attribute from silently becoming invalid. To change the type, delete the attribute via the [embed-tenant admin API](#embed-tenant-admin-api) and recreate it.
+
+Duplicate `name` entries within the same request are rejected.
+
+### Bootstrap example
+
+Define a group and an attribute, assign the user to both, and mint a session — all in one call:
+
+```javascript
+const session = await fetch(
+ `https://${ACCOUNT_NAME}.cubecloud.dev/api/v1/embed/generate-session`,
+ {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: `Api-Key ${API_KEY}`,
+ },
+ body: JSON.stringify({
+ deploymentId: DEPLOYMENT_ID,
+ externalId: 'user-123',
+ embedTenantName: 'acme-corp',
+ creatorMode: true,
+
+ // Upserted before validation runs
+ groupDefinitions: [
+ { name: 'analysts', description: 'Read-only viewers' },
+ ],
+ userAttributeDefinitions: [
+ { name: 'department', type: 'string', displayName: 'Department' },
+ ],
+
+ // Reference the names we just defined
+ groups: ['analysts'],
+ userAttributes: [{ name: 'department', value: 'Sales' }],
+ }),
+ },
+);
+```
+
+A second call with the same body produces the same end state: the group and attribute already exist, descriptions/display names are reconciled if they changed, and the user's memberships and values are re-applied.
+
+## Embed-tenant admin API
+
+To list or delete the groups and attributes that have been bootstrapped into an embed tenant, use the admin endpoints scoped to that tenant:
+
+```
+GET /api/v1/embed-tenants/{embedTenantName}/groups
+DELETE /api/v1/embed-tenants/{embedTenantName}/groups/{id}
+GET /api/v1/embed-tenants/{embedTenantName}/user-attributes
+DELETE /api/v1/embed-tenants/{embedTenantName}/user-attributes/{id}
+```
+
+These endpoints use the same `Api-Key` authentication as Generate Session and require admin access. List endpoints return cursor-paginated results (`?first=`, `?after=`).
+
### Response
The API returns a session object:
@@ -81,10 +248,12 @@ const response = await fetch(
'Authorization': `Api-Key ${API_KEY}`
},
body: JSON.stringify({
+ deploymentId: 32,
externalId: 'user@example.com',
userAttributes: [
{ name: 'department', value: 'Sales' }
- ]
+ ],
+ groups: ['analysts']
})
}
);
@@ -106,10 +275,12 @@ response = requests.post(
'Authorization': f'Api-Key {API_KEY}'
},
json={
+ 'deploymentId': 32,
'externalId': 'user@example.com',
'userAttributes': [
{'name': 'department', 'value': 'Sales'}
- ]
+ ],
+ 'groups': ['analysts']
}
)
@@ -121,10 +292,12 @@ curl -X POST "https://your-account.cubecloud.dev/api/v1/embed/generate-session"
-H "Content-Type: application/json" \
-H "Authorization: Api-Key YOUR_API_KEY" \
-d '{
+ "deploymentId": 32,
"externalId": "user@example.com",
"userAttributes": [
{"name": "department", "value": "Sales"}
- ]
+ ],
+ "groups": ["analysts"]
}'
```
@@ -134,4 +307,4 @@ Use session ID in [signed embedding][ref-signed-embedding].
[ref-api-keys]: /product/administration/api-keys
[ref-signed-embedding]: /product/embedding/signed-embedding
-
+[ref-creator-mode]: /product/embedding/creator-mode
diff --git a/docs/content/product/embedding/creator-mode.mdx b/docs/content/product/embedding/creator-mode.mdx
index be7d9dd119386..ef78daea2032a 100644
--- a/docs/content/product/embedding/creator-mode.mdx
+++ b/docs/content/product/embedding/creator-mode.mdx
@@ -6,44 +6,25 @@ Available on the [Enterprise plan](https://cube.dev/pricing).
-Creator mode enables you to embed the entire Cube application with workbooks-dashboard functionality. Users will be able to create and modify their dashboards directly within the embedded application.
+Creator mode embeds the full Cube application instead of an individual dashboard or chat. Users can create and modify workbooks, dashboards, and reports inside the iframe.
-## How it works
+To enable it, pass `creatorMode: true` to the [Generate Session API][ref-generate-session].
-In creator mode, you embed the full Cube application instead of individual dashboards or chat interfaces. This provides users with the complete Cube experience, including the ability to:
+## Embed tenant scoping
-- Create new dashboards
-- Modify existing dashboards
-- Access all workbook and dashboard functionality
-- Build custom analytics experiences
+In creator mode, content (workbooks, dashboards) and the groups/user attributes referenced by the session are scoped to an **embed tenant**. Pass `embedTenantName` to isolate content per customer; omit it to use the current tenant.
-To enable creator mode, pass `creatorMode: true` to the [Generate Session API][ref-generate-session] when generating an embed session. Optionally, pass `embedTenantName` to scope content to a specific embed tenant — by default, creator mode uses the current tenant's name.
+`embedTenantName` must be lowercase, 5–36 chars, start with a letter, end with a letter or digit, and contain only `a-z`, `0-9`, or `-`.
-## Embed tenant name
+## Provisioning groups and user attributes
-`embedTenantName` is an optional parameter on the Generate Session API used to scope all content (dashboards, workbooks, etc.) that users create within the embedded application to a specific embed tenant. This ensures proper data isolation in multi-tenant scenarios.
+In creator mode, `groups` and `userAttributes` resolve against the embed tenant's scoped catalog (separate from the tenant-wide one used by read-only embedding). To define those entries from your backend in the same call, pass `groupDefinitions` and `userAttributeDefinitions` — they are idempotent and validated before memberships and values are applied.
-When `embedTenantName` is omitted in creator mode, the session defaults to the current tenant, so most setups don't need to set it.
+See [Generate Session → Creator mode bootstrap][ref-bootstrap] for the input shape, value types, and the immutable-`type` rule on attribute definitions.
-The value must be lowercase, start with a letter, end with a letter or number, and only contain letters, numbers, or hyphens (length 5–36).
-
-## Using creator mode
-
-To use creator mode and embed an app:
-
-1. **Set the embed type to "App"** in the form
-2. **Fill in Deployment ID** and either **External ID** or **Internal ID** (email). Optionally provide an **Embed Tenant Name** to scope content to a specific embed tenant
-3. **Click "Generate Session & Embed"** — the request automatically includes `creatorMode: true` for app embeddings
-4. The app is embedded at `/embed/d/{deploymentId}/app?session={sessionId}` and displayed in the iframe
-
-Creator mode is enabled automatically when the embed type is "app"; no additional configuration is needed.
-
-### Example
+## Example
```javascript
-const API_KEY = "YOUR_API_KEY";
-const DEPLOYMENT_ID = 32;
-
const session = await fetch(
"https://your-account.cubecloud.dev/api/v1/embed/generate-session",
{
@@ -53,22 +34,18 @@ const session = await fetch(
Authorization: `Api-Key ${API_KEY}`,
},
body: JSON.stringify({
- deploymentId: DEPLOYMENT_ID,
+ deploymentId: 32,
externalId: "user@example.com",
+ embedTenantName: "acme-corp",
creatorMode: true,
- // Optional — defaults to the current tenant when omitted:
- // embedTenantName: "acme-corp",
}),
},
);
-const data = await session.json();
-const sessionId = data.sessionId;
+const { sessionId } = await session.json();
```
-### Embedding the app
-
-Use the session ID to embed the full Cube application:
+Embed the app with the returned session ID:
```html
```
-Replace `{deploymentId}` with your deployment ID and `{sessionId}` with the session ID returned from the Generate Session API.
-
-## Example application
-
-For a complete working example of embedding, including creator mode, check out the [cube-embedding-demo](https://github.com/cubedevinc/cube-embedding-demo) repository. This demo application provides:
-
-- A full working example of iframe embedding
-- Implementation of signed iframe embedding with session generation
-- Support for creator mode with an optional embed tenant name
-- A React-based UI for testing embedding functionality
-- Backend server that securely handles API key authentication
-
-You can clone the repository, configure it with your Cube credentials, and run it locally to test embedding functionality or use it as a reference implementation for your own application.
+For a complete working example, see the [cube-embedding-demo](https://github.com/cubedevinc/cube-embedding-demo) repository.
[ref-generate-session]: /product/apis-integrations/embed-apis/generate-session
+[ref-bootstrap]: /product/apis-integrations/embed-apis/generate-session#creator-mode-bootstrapping-groups-and-user-attributes