Entity management provides the foundation for organizing users into logical groups representing organizational boundaries - companies, accounts, departments, or business units. This capability enables fine-grained access control, data filtering, and multi-tenant operation within a single Pika deployment.
What It Does
Section titled “What It Does”Entity management gives you the infrastructure to organize and control access based on organizational membership:
Organizational Boundaries
Associate users with entities representing their companies, accounts, or departments for logical grouping and access control.
Entity-Based Access Control
Restrict chat app access and session visibility to specific entities, ensuring data isolation and appropriate access.
Administrative Tools
Manage entity associations, filter insights by entity, and configure entity-specific access rules through the admin interface.
Why It Matters
Section titled “Why It Matters”Multi-Tenant Operations
Section titled “Multi-Tenant Operations”Organizations deploying Pika often serve multiple companies, accounts, or departments that require data separation:
Software Vendors serving multiple customer companies need to ensure Company A cannot see Company B's chat sessions and data.
Enterprises with multiple business units need to control which employees access which internal tools and data.
Service Providers managing different client accounts need entity-based isolation for compliance and confidentiality.
Beyond Simple User Types
Section titled “Beyond Simple User Types”While user types (internal/external) provide basic categorization, real-world applications need finer granularity:
- User types tell you WHO users are (customers vs employees)
- Entities tell you WHICH ORGANIZATION users belong to
This distinction enables sophisticated access patterns:
- External users from Company A see only Company A's data
- Internal users (support staff) can access any entity's data when authorized
- Administrators manage entity-specific configurations
Key Concepts
Section titled “Key Concepts”What is an Entity?
Section titled “What is an Entity?”An entity represents an organizational unit that requires data isolation:
Customer Accounts:
Entity: "acct-001" (Acme Corp)Entity: "acct-002" (Global Industries)Entity: "acct-003" (Tech Solutions)Business Units:
Entity: "sales" (Sales Department)Entity: "engineering" (Engineering)Entity: "support" (Customer Support)Partner Organizations:
Entity: "partner-alpha" (Partner Alpha)Entity: "partner-beta" (Partner Beta)Entity Association
Section titled “Entity Association”Users are linked to entities through their authentication data:
// User data from authentication provider{ userId: "user123", userType: "external-user", customData: { accountId: "acct-001", // Entity identifier accountName: "Acme Corp", // Human-readable name email: "user@acmecorp.com" }}The accountId field (or whatever field you configure) becomes the entity identifier used throughout the platform.
Entity-Based Access Models
Section titled “Entity-Based Access Models”Chat apps and sessions operate in one of two access modes:
Entity-Scoped Access:
- Users only see content from their own entity
- Perfect for customer-facing applications
- Ensures complete data isolation between organizations
Global Access:
- Content visible to any authorized user regardless of entity
- Ideal for internal tools and public chat apps
- Still respects user type and role restrictions
Core Capabilities
Section titled “Core Capabilities”Entity-Based Session Filtering
Section titled “Entity-Based Session Filtering”Sessions automatically respect entity boundaries:
// Customer from Acme Corp views sessionsGET /api/chat/sessions// Returns only sessions where entity = "acct-001"
// Customer from Global Industries views sessionsGET /api/chat/sessions// Returns only sessions where entity = "acct-002"Filtering happens automatically based on the user's entity membership.
Entity-Based Access Control
Section titled “Entity-Based Access Control”Restrict chat app access to specific entities:
{ chatAppId: 'customer-portal', accessControl: { externalUsers: { exclusiveEntityAccess: { enabled: true, entityIds: ['acct-001', 'acct-002'] } } }}Result:
- Only users from
acct-001oracct-002can access this chat app - Users from other entities are denied access
- Administrators can still access for support purposes
Admin Site Entity Filtering
Section titled “Admin Site Entity Filtering”The admin interface provides entity-based filtering:
Session Insights:
- Filter sessions by specific entity
- See which entity each session belongs to
- Analyze patterns within entities
Access Control Management:
- Configure entity-specific access rules
- Use entity autocomplete for easy selection
- Manage which entities can access which apps
Entity Autocomplete
Section titled “Entity Autocomplete”The platform provides entity lookup for administrative interfaces:
// Implement entity search for autocompleteexport async function getValuesForEntityAutoComplete( searchTerm: string, user: AuthenticatedUser): Promise<SimpleOption[]> { // Query your entity data source const entities = await fetchEntitiesMatching(searchTerm);
return entities.map(entity => ({ value: entity.id, // Used in access control label: entity.name // Displayed to admins }));}This enables admins to easily search and select entities when configuring access controls.
Entity List Display
Section titled “Entity List Display”The platform also needs to display entity names for already-selected entities:
// Implement entity lookup by IDsexport async function getValuesForEntityList( entityIds: string[], user: AuthenticatedUser): Promise<SimpleOption[]> { // Fetch entity details for the given IDs const entities = await fetchEntitiesByIds(entityIds);
return entities.map(entity => ({ value: entity.id, // Entity identifier label: entity.name // Display name }));}This enables the admin interface to show human-readable names for entities that are already configured in access control rules.
Entity List Display
Section titled “Entity List Display”The platform also needs to display entity names for already-selected entities:
// Implement entity lookup by IDsexport async function getValuesForEntityList( entityIds: string[], user: AuthenticatedUser): Promise<SimpleOption[]> { // Fetch entity details for the given IDs const entities = await fetchEntitiesByIds(entityIds);
return entities.map(entity => ({ value: entity.id, // Entity identifier label: entity.name // Display name }));}This enables the admin interface to show human-readable names for entities that are already configured in access control rules.
Configuration
Section titled “Configuration”Enable Entity Feature
Section titled “Enable Entity Feature”In your pika-config.ts:
export const siteFeatures: SiteFeatures = { entity: { enabled: true, attributeName: 'accountId', // Field in user.customData searchPlaceholderText: 'Search for an account...', displayNameSingular: 'Account', displayNamePlural: 'Accounts', tableColumnHeaderTitle: 'Account ID' }};Configuration Options
Section titled “Configuration Options”enabled: Whether entity feature is active site-wide
attributeName: Field name in user.customData containing entity identifier
- Must match your authentication provider's data structure
- Common values:
accountId,companyId,departmentId,organizationId
searchPlaceholderText: Placeholder text for entity search inputs in admin interface
displayNameSingular: Singular form of entity name for UI display
- Examples: "Account", "Company", "Department", "Organization"
displayNamePlural: Plural form for UI display
- Examples: "Accounts", "Companies", "Departments", "Organizations"
tableColumnHeaderTitle: Header text for entity columns in tables
- Examples: "Account ID", "Company", "Department"
Authentication Provider Integration
Section titled “Authentication Provider Integration”Ensure your authentication provider populates the entity field:
// In your auth provider's authenticate() methodreturn { authenticatedUser: { userId: userData.id, firstName: userData.firstName, lastName: userData.lastName, userType: userData.isEmployee ? 'internal-user' : 'external-user', customData: { accountId: userData.accountId, // MUST match entity.attributeName accountName: userData.accountName, email: userData.email, department: userData.department } }};Critical: The field name in customData must match entity.attributeName in your configuration.
Use Cases
Section titled “Use Cases”Multi-Customer SaaS Platform
Section titled “Multi-Customer SaaS Platform”Scenario: Software vendor serving multiple customer companies
// Entity configurationentity: { enabled: true, attributeName: 'companyId', displayNameSingular: 'Company', displayNamePlural: 'Companies'}
// Customer-facing chat app{ chatAppId: 'customer-support', accessControl: { externalUsers: { // All customers can access accessByUserTypes: ['external-user'], // But only see their company's sessions exclusiveEntityAccess: { enabled: false } // Global access } }}
// Account-specific premium features{ chatAppId: 'premium-analytics', accessControl: { externalUsers: { // Only premium companies exclusiveEntityAccess: { enabled: true, entityIds: ['company-001', 'company-005', 'company-012'] } } }}Result:
- All customers access the support chat
- Each company only sees their own sessions
- Premium companies access advanced analytics
- Support staff (internal users) see all companies' data
Enterprise Departments
Section titled “Enterprise Departments”Scenario: Large enterprise with multiple departments
// Entity configurationentity: { enabled: true, attributeName: 'departmentId', displayNameSingular: 'Department', displayNamePlural: 'Departments'}
// Sales department tools{ chatAppId: 'sales-assistant', accessControl: { internalUsers: { exclusiveEntityAccess: { enabled: true, entityIds: ['sales'] } } }}
// Engineering tools{ chatAppId: 'code-assistant', accessControl: { internalUsers: { exclusiveEntityAccess: { enabled: true, entityIds: ['engineering', 'qa'] } } }}Result:
- Sales team accesses sales assistant only
- Engineering and QA access code assistant
- Each department sees only their sessions
- Cross-department access requires explicit configuration
Partner Network
Section titled “Partner Network”Scenario: Organization managing multiple partner companies
// Entity configurationentity: { enabled: true, attributeName: 'partnerId', displayNameSingular: 'Partner', displayNamePlural: 'Partners'}
// Partner portal{ chatAppId: 'partner-portal', accessControl: { externalUsers: { accessByUserTypes: ['external-user'], // Each partner sees only their data exclusiveEntityAccess: { enabled: false } } }}
// Exclusive partner program{ chatAppId: 'elite-partner-tools', accessControl: { externalUsers: { exclusiveEntityAccess: { enabled: true, entityIds: ['partner-alpha', 'partner-gamma'] } } }}Result:
- All partners access the general portal
- Partner Alpha and Gamma access elite tools
- Each partner's data remains isolated
- Partner managers (internal) can view all partner data
Access Control Precedence
Section titled “Access Control Precedence”Entity-based access control follows this precedence order:
- Disabled Override - If
enabled: false, no access regardless of entity - Exclusive User ID Control - Specific user IDs take precedence
- Exclusive Entity Control - Entity-based restrictions apply
- General Access Rules - Fall back to user type/role checking
// Example showing precedence{ chatAppId: 'restricted-app', accessControl: { externalUsers: { enabled: true, // Feature enabled
// Exclusive user list takes highest precedence exclusiveUserIdAccess: { enabled: true, userIds: ['special-user-001'] },
// Entity restrictions apply if user not in exclusive list exclusiveEntityAccess: { enabled: true, entityIds: ['acct-premium'] },
// General rules apply if no exclusive controls match accessByUserTypes: ['external-user'] } }}Benefits
Section titled “Benefits”For Organizations
Section titled “For Organizations”Data Isolation
Complete separation between entities ensures confidentiality and compliance with data protection regulations.
Flexible Architecture
Support both entity-scoped applications (customer portals) and global applications (internal tools) on the same platform.
Scalable Management
Add new entities without platform changes. Entity associations come from authentication provider.
For Administrators
Section titled “For Administrators”Fine-Grained Control
Configure which entities can access which chat apps with entity-specific access rules.
Visibility & Insights
Filter session data by entity to analyze usage patterns, identify issues, and measure success per organization.
Easy Configuration
Entity autocomplete makes it simple to select entities when configuring access controls.
For Users
Section titled “For Users”Relevant Content Only
Users see only sessions and data relevant to their organization, reducing clutter and confusion.
Privacy & Security
Automatic entity-based filtering ensures users cannot accidentally access other organizations' data.
Implementation Guide
Section titled “Implementation Guide”1. Define Your Entity Model
Section titled “1. Define Your Entity Model”Determine what "entity" means for your organization:
Questions to answer:
- What organizational boundaries require data separation?
- Where does entity data come from? (HR system, CRM, custom database)
- What field name represents entities in your authentication system?
- How do you want to refer to entities in the UI?
Example decisions:
// SaaS vendorattributeName: 'companyId'displayNameSingular: 'Company'
// EnterpriseattributeName: 'departmentId'displayNameSingular: 'Department'
// HealthcareattributeName: 'facilityId'displayNameSingular: 'Facility'2. Update Authentication Provider
Section titled “2. Update Authentication Provider”Ensure entity field is populated in user data:
// Modify your authenticate() methodconst userData = await fetchUserFromAuthSystem(credentials);
return { authenticatedUser: { // ... standard fields ... customData: { [entityAttributeName]: userData.entityId, // Key: matches config entityName: userData.entityDisplayName, // ... other custom data ... } }};3. Implement Entity Data Functions
Section titled “3. Implement Entity Data Functions”Create entity lookup functions for the admin interface:
// In apps/pika-chat/src/routes/(auth)/api/site-admin/custom-data.ts
// Function 1: Search entities for autocompleteexport async function getValuesForEntityAutoComplete( searchTerm: string, user: AuthenticatedUser): Promise<SimpleOption[]> { // Query your entity data source const entities = await yourEntityDatabase.search({ name: { contains: searchTerm }, limit: 20 });
return entities.map(entity => ({ value: entity.id, // Entity identifier label: `${entity.name} (${entity.id})` // Display name }));}
// Function 2: Get entity details by IDsexport async function getValuesForEntityList( entityIds: string[], user: AuthenticatedUser): Promise<SimpleOption[]> { // Fetch entity details for the given IDs const entities = await yourEntityDatabase.findByIds(entityIds);
return entities.map(entity => ({ value: entity.id, // Entity identifier label: `${entity.name} (${entity.id})` // Display name }));}Why both functions are needed:
getValuesForEntityAutoComplete: Used when admins search for entities to add to access controlgetValuesForEntityList: Used to display names for entities already configured in access control
4. Configure Chat Apps
Section titled “4. Configure Chat Apps”Add entity-based access control to chat apps:
{ chatAppId: 'my-chat-app', accessControl: { externalUsers: { enabled: true, accessByUserTypes: ['external-user'],
// Enable entity-based access control exclusiveEntityAccess: { enabled: true, entityIds: ['entity-001', 'entity-002'] } } }}5. Test Entity Filtering
Section titled “5. Test Entity Filtering”Verify entity-based access works correctly:
Test scenarios:
- User from Entity A cannot see Entity B's sessions
- Admin can filter insights by specific entity
- Entity autocomplete returns correct results
- Access control denies unauthorized entities
Advanced Patterns
Section titled “Advanced Patterns”Dynamic Entity Access
Section titled “Dynamic Entity Access”Grant entity access based on business rules:
function getAuthorizedEntities(user: AuthenticatedUser): string[] { const entities: string[] = [];
// User's primary entity if (user.customData.accountId) { entities.push(user.customData.accountId); }
// Parent organization entities (for enterprise hierarchy) if (user.customData.parentOrgId) { entities.push(user.customData.parentOrgId); }
// Partner entities (for partner managers) if (user.roles.includes('partner-manager')) { entities.push(...user.customData.managedPartnerIds); }
return entities;}Hierarchical Entities
Section titled “Hierarchical Entities”Support entity hierarchies:
// Entity hierarchy exampleconst entityHierarchy = { 'parent-corp': { children: ['subsidiary-a', 'subsidiary-b'] }, 'subsidiary-a': { parent: 'parent-corp', children: ['division-1', 'division-2'] }};
// Grant access to entity and all descendantsfunction getEntityAndDescendants(entityId: string): string[] { const entities = [entityId]; const entity = entityHierarchy[entityId];
if (entity?.children) { entity.children.forEach(child => { entities.push(...getEntityAndDescendants(child)); }); }
return entities;}Entity Metadata
Section titled “Entity Metadata”Enrich entity data with metadata:
// Store entity metadata for UI displayinterface EntityMetadata { id: string; name: string; tier: 'free' | 'pro' | 'enterprise'; features: string[]; contactEmail: string;}
// Use metadata to customize chat app behaviorif (entityMetadata.tier === 'enterprise') { enablePremiumFeatures();}Best Practices
Section titled “Best Practices”Entity Modeling
Section titled “Entity Modeling”Security
Section titled “Security”Performance
Section titled “Performance”Related Capabilities
Section titled “Related Capabilities”- Multi-Tenancy - Entity management enables multi-tenant deployments
- Access Control - Entity-based access control integration
- Admin Site - Entity filtering and management interface
Technical Details
Section titled “Technical Details”Entity Field Resolution
Section titled “Entity Field Resolution”The platform resolves entity identifiers from user data:
function getUserEntity(user: AuthenticatedUser, entityConfig: EntityFeature): string | undefined { if (!entityConfig.enabled) { return undefined; }
const attributeName = entityConfig.attributeName; return user.customData?.[attributeName];}Entity-Based Query Filtering
Section titled “Entity-Based Query Filtering”Sessions are automatically filtered by entity:
// DynamoDB query with entity filterconst params = { TableName: 'sessions', FilterExpression: 'entityId = :entityId', ExpressionAttributeValues: { ':entityId': userEntity }};Access Control Evaluation
Section titled “Access Control Evaluation”Entity-based access control is evaluated during authorization:
function hasEntityAccess( user: AuthenticatedUser, chatApp: ChatApp, userEntity: string): boolean { const exclusiveAccess = chatApp.accessControl.exclusiveEntityAccess;
if (!exclusiveAccess?.enabled) { return true; // No entity restrictions }
return exclusiveAccess.entityIds.includes(userEntity);}Next Steps
Section titled “Next Steps”Ready to implement entity management?
Configure Multi-Tenancy
Learn how entity management enables multi-tenant deployments.
Setup Access Control
Configure entity-based access rules for your chat apps.
Implementation Guide
Step-by-step instructions for implementing entity management.