# Generate Session

The Generate Session API provides secure, session-based authentication for [signed embedding][ref-signed-embedding]. This API creates temporary sessions that allow external users to access embedded dashboards and visualizations without exposing your API keys.

<InfoBox>

The Generate Session API is available on [Premium and Enterprise plans](https://cube.dev/pricing).

</InfoBox>

## Authentication

The Generate Session API requires your [Cube Cloud API key][ref-api-keys] for authentication.

## Endpoint

```
POST https://{accountName}.cubecloud.dev/api/v1/embed/generate-session
```

### Request Headers

| Header | Value | Required |
|--------|-------|----------|
| `Content-Type` | `application/json` | Yes |
| `Authorization` | `Api-Key YOUR_API_KEY` | Yes |

### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `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`. |

<InfoBox>

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.

</InfoBox>

<WarningBox>

Accounts are limited to 10,000 external users. To increase this limit, please contact support.

</WarningBox>

## 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 <missing> 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:

```json
{
  "sessionId": "abc123def456..."
}
```

| Field | Type | Description |
|-------|------|-------------|
| `sessionId` | string | Unique session identifier to use for embedding content |

Use the `sessionId` directly in your embed URL to authenticate and load content securely.

## Code Example

<CodeTabs>

```javascript JavaScript
const API_KEY = 'YOUR_API_KEY';
const ACCOUNT_NAME = 'your-account';

// Generate a session on your server
const response = 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: 32,
      externalId: 'user@example.com',
      userAttributes: [
        { name: 'department', value: 'Sales' }
      ],
      groups: ['analysts']
    })
  }
);

const { sessionId } = await response.json();
```

```python Python
import requests

API_KEY = 'YOUR_API_KEY'
ACCOUNT_NAME = 'your-account'

# Generate a session on your server
response = requests.post(
    f'https://{ACCOUNT_NAME}.cubecloud.dev/api/v1/embed/generate-session',
    headers={
        'Content-Type': 'application/json',
        'Authorization': f'Api-Key {API_KEY}'
    },
    json={
        'deploymentId': 32,
        'externalId': 'user@example.com',
        'userAttributes': [
            {'name': 'department', 'value': 'Sales'}
        ],
        'groups': ['analysts']
    }
)

session_id = response.json()['sessionId']
```

```bash cURL
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"]
  }'
```

</CodeTabs>

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
