or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

api

features

charts

charts.mdconditional-formatting.mdvisualizations.md
authorization.mdchangesets.mdcharts-as-code.mdcompiler.mddashboards.mddbt.mdee-features.mdformatting.mdparameters.mdpivot.mdprojects-spaces.mdsql-runner.mdtemplating.mdwarehouse.md
index.md
tile.json

ee-features.mddocs/api/features/

Enterprise Edition Features

Enterprise Edition features for Lightdash Common providing advanced functionality including AI Agents, embedding with interactivity controls, SCIM user provisioning, and service account authentication.

Capabilities

Commercial Feature Flags

Enterprise features controlled by feature flags.

enum CommercialFeatureFlags {
  Embedding = 'embedding',
  Scim = 'scim-token-management',
  AiCopilot = 'ai-copilot',
  ServiceAccounts = 'service-accounts',
  OrganizationWarehouseCredentials = 'organization-warehouse-credentials',
  AgentReasoning = 'agent-reasoning',
}

AI Agent System

Comprehensive AI assistant functionality with conversation threads, tool calls, artifact management, admin controls, and evaluation capabilities.

/**
 * AI Agent configuration with instructions, integrations, and access controls
 */
interface AiAgent {
  uuid: string;
  projectUuid: string;
  organizationUuid: string;
  name: string;
  description: string | null;
  imageUrl: string | null;
  tags: string[] | null;
  integrations: Array<{
    type: 'slack';
    channelId: string;
  }>;
  createdAt: Date;
  updatedAt: Date;
  instruction: string | null; // Custom instructions (max 8192 characters)
  provider: string;
  model: string;
  groupAccess: string[];
  userAccess: string[];
  spaceAccess: string[];
  enableDataAccess: boolean;
  enableSelfImprovement: boolean;
  enableReasoning: boolean;
  version: number;
}

/**
 * Compact agent summary for listing
 */
type AiAgentSummary = Pick<
  AiAgent,
  | 'uuid'
  | 'name'
  | 'description'
  | 'integrations'
  | 'tags'
  | 'projectUuid'
  | 'organizationUuid'
  | 'createdAt'
  | 'updatedAt'
  | 'instruction'
  | 'imageUrl'
  | 'groupAccess'
  | 'userAccess'
  | 'spaceAccess'
  | 'enableDataAccess'
  | 'enableSelfImprovement'
  | 'enableReasoning'
  | 'version'
>;

/**
 * Conversation thread containing messages
 */
interface AiAgentThread<TUser extends AiAgentUser = AiAgentUser> {
  uuid: string;
  agentUuid: string;
  createdAt: string;
  createdFrom: string;
  title: string | null;
  titleGeneratedAt: string | null;
  firstMessage: {
    uuid: string;
    message: string;
  };
  user: TUser;
  messages: AiAgentMessage<TUser>[];
}

/**
 * User message in conversation
 */
interface AiAgentMessageUser<TUser extends AiAgentUser = AiAgentUser> {
  role: 'user';
  uuid: string;
  threadUuid: string;
  message: string;
  createdAt: string;
  user: TUser;
}

/**
 * Assistant response with tool calls and results
 */
interface AiAgentMessageAssistant {
  role: 'assistant';
  status: 'idle' | 'pending' | 'error';
  uuid: string;
  threadUuid: string;
  message: string | null;
  createdAt: string;
  humanScore: number | null;
  humanFeedback?: string | null;
  toolCalls: AiAgentToolCall[];
  toolResults: AiAgentToolResult[];
  reasoning: AiAgentReasoning[];
  savedQueryUuid: string | null;
  artifacts: AiAgentMessageAssistantArtifact[] | null;
  referencedArtifacts: AiAgentMessageAssistantArtifact[] | null;
  modelConfig: {
    modelName: string;
    modelProvider: string;
    reasoning?: boolean;
  } | null;
}

/**
 * Tool invocation by AI agent
 */
interface AiAgentToolCall {
  uuid: string;
  promptUuid: string;
  toolCallId: string;
  createdAt: Date;
  toolName: string;
  toolArgs: object;
}

/**
 * Result from tool execution
 */
type AiAgentToolResult = {
  uuid: string;
  promptUuid: string;
  result: string;
  createdAt: Date;
  toolCallId: string;
} & (
  | {
      toolName: 'proposeChange';
      metadata: ToolProposeChangeOutput['metadata'];
    }
  | {
      toolName: Exclude<ToolName, 'proposeChange'>;
      metadata: AgentToolOutput['metadata'];
    }
);

/**
 * Agent reasoning step (when enableReasoning is true)
 */
interface AiAgentReasoning {
  uuid: string;
  promptUuid: string;
  reasoningId: string;
  text: string;
  createdAt: Date;
}

/**
 * Saved artifact (chart or dashboard) created by agent
 */
interface AiArtifact {
  artifactUuid: string;
  threadUuid: string;
  promptUuid: string | null;
  artifactType: 'chart' | 'dashboard';
  savedQueryUuid: string | null;
  savedDashboardUuid: string | null;
  createdAt: Date;
  versionNumber: number;
  versionUuid: string;
  title: string | null;
  description: string | null;
  chartConfig:
    | ToolTableVizArgs
    | ToolTimeSeriesArgs
    | ToolVerticalBarArgs
    | ToolRunQueryArgs
    | null;
  dashboardConfig: ToolDashboardArgs | null;
  versionCreatedAt: Date;
  verifiedByUserUuid: string | null;
  verifiedAt: Date | null;
}

/**
 * User-verified artifact for reuse
 */
interface AiAgentVerifiedArtifact {
  artifactUuid: string;
  versionUuid: string;
  artifactType: 'chart' | 'dashboard';
  title: string | null;
  description: string | null;
  verifiedAt: Date;
  verifiedBy: {
    userUuid: string;
    firstName: string;
    lastName: string;
  };
  referenceCount: number;
  threadUuid: string;
  promptUuid: string | null;
}

/**
 * Agent evaluation test suite
 */
interface AiAgentEvaluation {
  evalUuid: string;
  agentUuid: string;
  title: string;
  description: string | null;
  createdAt: Date;
  updatedAt: Date;
  prompts: AiAgentEvaluationPrompt[];
}

/**
 * Evaluation run results
 */
interface AiAgentEvaluationRun {
  runUuid: string;
  evalUuid: string;
  status: 'pending' | 'running' | 'completed' | 'failed';
  completedAt: Date | null;
  createdAt: Date;
  passedAssessments: number;
  failedAssessments: number;
  results: AiAgentEvaluationRunResult[];
}

/**
 * Agent model configuration options
 */
interface AiModelOption {
  name: string;
  displayName: string;
  description: string;
  provider: string;
  default: boolean;
  supportsReasoning: boolean;
}

AI Agent Administration

Admin-level types for managing AI agents across the organization, viewing conversations, tracking feedback, and configuring organization-wide AI settings.

/**
 * Filters for AI agent admin dashboard
 */
type AiAgentAdminFilters = {
  projectUuids?: string[];
  agentUuids?: string[];
  userUuids?: string[];
  createdFrom?: 'slack' | 'web_app';
  humanScore?: number; // Feedback score: -1 (downvote), 0 (neutral), 1 (upvote)
  dateFrom?: string; // ISO date string
  dateTo?: string; // ISO date string
  search?: string; // Search by thread title
};

/**
 * Sort field options for admin conversations
 */
type AiAgentAdminSortField = 'createdAt' | 'title';

/**
 * Sort configuration for admin views
 */
type AiAgentAdminSort = {
  field: AiAgentAdminSortField;
  direction: 'asc' | 'desc';
};

/**
 * Aggregated feedback statistics for a thread
 */
type AiAgentAdminFeedbackSummary = {
  upvotes: number;
  downvotes: number;
  neutral: number;
  total: number;
};

/**
 * Extended thread summary with admin-level information
 */
type AiAgentAdminThreadSummary = {
  uuid: string;
  createdAt: string;
  createdFrom: 'slack' | 'web_app';
  title: string | null;
  user: AiAgentUser & {
    slackUserId: string | null;
    email: string | null;
  };
  agent: {
    uuid: string;
    name: string;
    imageUrl: string | null;
  };
  project: {
    uuid: string;
    name: string;
  };
  feedbackSummary: AiAgentAdminFeedbackSummary;
  promptCount: number;
  slackChannelId: string | null;
  slackThreadTs: string | null;
};

/**
 * Collection of admin thread summaries
 */
type AiAgentAdminConversationsSummary = {
  threads: AiAgentAdminThreadSummary[];
};

/**
 * API response for admin conversations endpoint
 */
type ApiAiAgentAdminConversationsResponse = ApiSuccess<
  KnexPaginatedData<AiAgentAdminConversationsSummary>
>;

/**
 * Computed/derived AI settings for organization
 */
type ComputedAiOrganizationSettings = {
  isCopilotEnabled: boolean;
  isTrial: boolean;
};

/**
 * AI feature settings at organization level
 */
type AiOrganizationSettings = {
  organizationUuid: string;
  aiAgentsVisible: boolean;
};

/**
 * Create AI organization settings request
 */
type CreateAiOrganizationSettings = AiOrganizationSettings;

/**
 * Update AI organization settings request
 */
type UpdateAiOrganizationSettings = Omit<
  AiOrganizationSettings,
  'organizationUuid' | 'isTrial' | 'isCopilotEnabled'
>;

/**
 * API response for organization settings
 */
type ApiAiOrganizationSettingsResponse = ApiSuccess<
  AiOrganizationSettings & ComputedAiOrganizationSettings
>;

/**
 * API response for updating organization settings
 */
type ApiUpdateAiOrganizationSettingsResponse = ApiSuccess<AiOrganizationSettings>;

Example:

import {
  type AiAgentAdminFilters,
  type AiAgentAdminSort,
  type AiAgentAdminThreadSummary,
} from '@lightdash/common';

// Filter admin conversations by project and feedback
const filters: AiAgentAdminFilters = {
  projectUuids: ['proj-123'],
  humanScore: 1, // Only show upvoted conversations
  dateFrom: '2024-01-01',
  dateTo: '2024-12-31',
  createdFrom: 'slack',
};

// Sort by most recent
const sort: AiAgentAdminSort = {
  field: 'createdAt',
  direction: 'desc',
};

// Thread summary includes agent, project, and feedback stats
const thread: AiAgentAdminThreadSummary = {
  uuid: 'thread-456',
  title: 'Revenue analysis request',
  createdAt: '2024-06-15T10:30:00Z',
  createdFrom: 'slack',
  user: {
    uuid: 'user-789',
    name: 'John Doe',
    email: 'john@example.com',
    slackUserId: 'U123ABC',
  },
  agent: {
    uuid: 'agent-001',
    name: 'Analytics Assistant',
    imageUrl: 'https://example.com/agent.png',
  },
  project: {
    uuid: 'proj-123',
    name: 'Sales Analytics',
  },
  feedbackSummary: {
    upvotes: 5,
    downvotes: 1,
    neutral: 2,
    total: 8,
  },
  promptCount: 12,
  slackChannelId: 'C123DEF',
  slackThreadTs: '1234567890.123456',
};

AI Agent Evaluation and Assessment

Types for evaluating AI agent performance with test suites and automated assessments (human or LLM judge).

/**
 * Type of assessment
 */
type AssessmentType = 'human' | 'llm';

/**
 * Assessment record for evaluation run result
 */
type AiEvalRunResultAssessment = {
  assessmentUuid: string;
  runResultUuid: string;
  assessmentType: AssessmentType;
  passed: boolean;
  reason: string | null;
  assessedByUserUuid: string | null; // Set when assessmentType is 'human'
  llmJudgeProvider: string | null; // Set when assessmentType is 'llm'
  llmJudgeModel: string | null; // Set when assessmentType is 'llm'
  assessedAt: Date;
  createdAt: Date;
};

/**
 * Create LLM judge assessment request
 */
type CreateLlmAssessment = {
  runResultUuid: string;
  passed: boolean;
  reason: string | null;
  llmJudgeProvider: string;
  llmJudgeModel: string;
};

Example:

import {
  type AiEvalRunResultAssessment,
  type CreateLlmAssessment,
  type AssessmentType,
} from '@lightdash/common';

// Human assessment of agent response
const humanAssessment: AiEvalRunResultAssessment = {
  assessmentUuid: 'assess-123',
  runResultUuid: 'result-456',
  assessmentType: 'human',
  passed: true,
  reason: 'Response was accurate and helpful',
  assessedByUserUuid: 'user-789',
  llmJudgeProvider: null,
  llmJudgeModel: null,
  assessedAt: new Date('2024-06-15T14:30:00Z'),
  createdAt: new Date('2024-06-15T14:30:00Z'),
};

// LLM judge assessment of agent response
const llmAssessment: CreateLlmAssessment = {
  runResultUuid: 'result-789',
  passed: true,
  reason: 'The response correctly identified the key metrics and provided accurate calculations.',
  llmJudgeProvider: 'anthropic',
  llmJudgeModel: 'claude-sonnet-4-5',
};

// Use in evaluation workflow
async function assessEvaluationResult(
  resultUuid: string,
  assessmentType: AssessmentType
) {
  if (assessmentType === 'llm') {
    // Create automated LLM assessment
    const assessment: CreateLlmAssessment = {
      runResultUuid: resultUuid,
      passed: await llmJudge.evaluate(resultUuid),
      reason: await llmJudge.getReason(resultUuid),
      llmJudgeProvider: 'anthropic',
      llmJudgeModel: 'claude-sonnet-4-5',
    };
    return assessment;
  } else {
    // Human assessment workflow
    return await promptForHumanReview(resultUuid);
  }
}

Embedding Interactivity

Advanced embedding controls for dashboards and charts with filter, parameter, and export interactivity.

/**
 * Filter interactivity level enum
 */
enum FilterInteractivityValues {
  some = 'some', // Only specific filters are interactive
  all = 'all',   // All filters are interactive
  none = 'none', // No filters are interactive
}

/**
 * Dashboard filter interactivity configuration
 */
interface DashboardFilterInteractivityOptions {
  enabled: boolean | FilterInteractivityValues;
  allowedFilters?: string[] | null; // Required when enabled is 'some'
  hidden?: boolean; // Whether filters should be hidden in UI
}

/**
 * Parameter interactivity configuration
 */
interface ParameterInteractivityOptions {
  enabled: boolean;
}

/**
 * Dashboard interactivity options
 */
interface InteractivityOptions {
  dashboardFiltersInteractivity?: DashboardFilterInteractivityOptions;
  parameterInteractivity?: ParameterInteractivityOptions;
  canExportCsv?: boolean;
  canExportImages?: boolean;
  canExportPagePdf?: boolean;
  canDateZoom?: boolean;
  canExplore?: boolean;
  canViewUnderlyingData?: boolean;
}

/**
 * Chart-specific interactivity options
 */
interface ChartInteractivityOptions {
  scopes?: string[];
  canExportCsv?: boolean;
  canExportImages?: boolean;
  canViewUnderlyingData?: boolean;
}

/**
 * Embed JWT configuration for dashboard or chart
 */
interface EmbedJwt {
  userAttributes?: Record<string, unknown>;
  user?: {
    externalId?: string;
    email?: string;
  };
  content:
    | {
        type: 'dashboard';
        projectUuid?: string;
        dashboardUuid: string;
        isPreview?: boolean;
      } & InteractivityOptions
    | {
        type: 'dashboard';
        projectUuid?: string;
        dashboardSlug: string;
        isPreview?: boolean;
      } & InteractivityOptions
    | {
        type: 'chart';
        projectUuid?: string;
        contentId: string;
        isPreview?: boolean;
      } & ChartInteractivityOptions;
  iat?: number;
  exp: number;
}

/**
 * Create embed JWT request
 */
interface CreateEmbedJwt {
  content:
    | EmbedJwtContentDashboardUuid
    | EmbedJwtContentDashboardSlug
    | EmbedJwtContentChart;
  userAttributes?: { [key: string]: string };
  user?: {
    email?: string;
    externalId?: string;
  };
  expiresIn?: string;
  iat?: number;
  exp?: number;
}

/**
 * Check if filter interactivity is enabled
 */
function isFilterInteractivityEnabled(
  filterInteractivityOptions?: DashboardFilterInteractivityOptions
): boolean;

/**
 * Check if parameter interactivity is enabled
 */
function isParameterInteractivityEnabled(
  parameterInteractivityOptions?: ParameterInteractivityOptions
): boolean;

/**
 * Get filter interactivity value (handles backward compatibility)
 */
function getFilterInteractivityValue(
  enabled: boolean | FilterInteractivityValues
): FilterInteractivityValues;

/**
 * Type guard for dashboard UUID content
 */
function isDashboardUuidContent(
  content: CreateEmbedJwt['content']
): content is EmbedJwtContentDashboardUuid;

/**
 * Type guard for dashboard slug content
 */
function isDashboardSlugContent(
  content: CreateEmbedJwt['content']
): content is EmbedJwtContentDashboardSlug;

/**
 * Type guard for chart content
 */
function isChartContent(
  content: CreateEmbedJwt['content']
): content is EmbedJwtContentChart;

/**
 * Type guard for dashboard content (UUID or slug)
 */
function isDashboardContent(
  content: CreateEmbedJwt['content'] | EmbedContent
): content is EmbedJwtContentDashboardUuid | EmbedJwtContentDashboardSlug;

SCIM Integration

SCIM 2.0 user and group provisioning for automated user management.

/**
 * SCIM schema type enum
 */
enum ScimSchemaType {
  ERROR = 'urn:ietf:params:scim:api:messages:2.0:Error',
  USER = 'urn:ietf:params:scim:schemas:core:2.0:User',
  GROUP = 'urn:ietf:params:scim:schemas:core:2.0:Group',
  ROLE = 'urn:ietf:params:scim:schemas:extension:2.0:Role',
  LIST_RESPONSE = 'urn:ietf:params:scim:api:messages:2.0:ListResponse',
  SCHEMA = 'urn:ietf:params:scim:schemas:core:2.0:Schema',
  PATCH = 'urn:ietf:params:scim:api:messages:2.0:PatchOp',
  LIGHTDASH_USER_EXTENSION = 'urn:lightdash:params:scim:schemas:extension:2.0:User',
  SERVICE_PROVIDER_CONFIG = 'urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig',
  RESOURCE_TYPE = 'urn:ietf:params:scim:schemas:core:2.0:ResourceType',
}

/**
 * Base SCIM resource with metadata
 */
interface ScimResource {
  schemas: string[];
  id: string;
  meta?: {
    resourceType?: string;
    created?: Date;
    lastModified?: Date;
    location?: string;
    version?: string;
  };
}

/**
 * SCIM user resource
 */
interface ScimUser extends ScimResource {
  schemas: string[];
  userName: string;
  name?: {
    givenName?: string;
    familyName?: string;
  };
  active?: boolean;
  emails?: {
    value: string;
    primary?: boolean;
  }[];
  roles?: ScimUserRole[];
  [ScimSchemaType.LIGHTDASH_USER_EXTENSION]?: LightdashScimExtension;
}

/**
 * SCIM user role assignment
 */
interface ScimUserRole {
  value: string;
  display?: string;
  type?: string;
  primary?: boolean;
}

/**
 * Lightdash SCIM extension for user roles
 */
interface LightdashScimExtension {
  /**
   * @deprecated - use ScimUser['roles'] instead
   */
  role?: string;
}

/**
 * SCIM group resource
 */
interface ScimGroup extends ScimResource {
  schemas: string[];
  id: string;
  displayName: string;
  members?: ScimGroupMember[];
  meta: ScimResource['meta'] & {
    resourceType: 'Group';
    created: Date;
    lastModified: Date;
    location: string;
  };
}

/**
 * SCIM group member reference
 */
interface ScimGroupMember {
  value: string;
  display?: string;
}

/**
 * SCIM role resource
 */
interface ScimRole extends ScimResource {
  schemas: ScimSchemaType.ROLE[];
  value: string;
  display?: string;
  type?: string; // Function level (e.g., Organization, Project)
  supported: boolean;
  meta: ScimResource['meta'] & {
    resourceType: 'Role';
    created?: Date;
    lastModified?: Date;
    location: string;
  };
}

/**
 * SCIM error response
 */
interface ScimErrorPayload {
  scimType?:
    | 'invalidFilter'
    | 'tooMany'
    | 'uniqueness'
    | 'mutability'
    | 'invalidSyntax'
    | 'invalidPath'
    | 'noTarget'
    | 'invalidValue'
    | 'invalidVers'
    | 'sensitive';
  schemas: ScimSchemaType.ERROR[];
  detail: string;
  status: string;
}

/**
 * SCIM list response with pagination
 */
interface ScimListResponse<T extends ScimResource> {
  schemas: ScimSchemaType.LIST_RESPONSE[];
  totalResults: number;
  itemsPerPage: number;
  startIndex: number;
  Resources: T[];
}

/**
 * Service provider configuration
 */
interface ScimServiceProviderConfig {
  schemas: ScimSchemaType.SERVICE_PROVIDER_CONFIG[];
  documentationUri?: string;
  patch: { supported: boolean };
  bulk: {
    supported: boolean;
    maxOperations?: number;
    maxPayloadSize?: number;
  };
  filter: {
    supported: boolean;
    maxResults?: number;
  };
  changePassword: { supported: boolean };
  sort: { supported: boolean };
  etag: { supported: boolean };
  authenticationSchemes: {
    type: string;
    name: string;
    description: string;
    specUri?: string;
    documentationUri?: string;
    primary?: boolean;
  }[];
}

Service Accounts

API authentication with scope-based access control for programmatic access.

/**
 * Service account scope enum
 */
enum ServiceAccountScope {
  SCIM_MANAGE = 'scim:manage',
  ORG_ADMIN = 'org:admin',
  ORG_EDIT = 'org:edit',
  ORG_READ = 'org:read',
}

/**
 * Service account with metadata and scopes
 */
interface ServiceAccount {
  uuid: string;
  createdByUserUuid: string | null;
  organizationUuid: string;
  createdAt: Date;
  expiresAt: Date | null;
  description: string;
  lastUsedAt: Date | null;
  rotatedAt: Date | null;
  scopes: ServiceAccountScope[];
}

/**
 * Service account with authentication token
 */
interface ServiceAccountWithToken extends ServiceAccount {
  token: string;
}

/**
 * Create service account request
 */
type ApiCreateServiceAccountRequest = Pick<
  ServiceAccount,
  'expiresAt' | 'description' | 'scopes'
>;

/**
 * Create service account response with token
 */
interface ApiCreateServiceAccountResponse {
  token: string;
  expiresAt: Date;
}

Types

interface AiAgentUser {
  uuid: string;
  name: string;
}

type AiAgentMessage<TUser extends AiAgentUser = AiAgentUser> =
  | AiAgentMessageUser<TUser>
  | AiAgentMessageAssistant;

type AiAgentThreadSummary<TUser extends AiAgentUser = AiAgentUser> = Pick<
  AiAgentThread<TUser>,
  'uuid' | 'agentUuid' | 'createdAt' | 'createdFrom' | 'title' | 'titleGeneratedAt' | 'firstMessage' | 'user'
>;

type CreateEvaluationPrompt =
  | { prompt: string; expectedResponse: string | null }
  | { promptUuid: string; threadUuid: string; expectedResponse: string | null };

type SessionServiceAccount = {
  organizationUuid: string;
};