Learn how to configure the Entity feature to associate users with organizational entities (accounts, companies, departments) and enable entity-based access control throughout Pika.
What You'll Accomplish
Section titled “What You'll Accomplish”By the end of this guide, you will:
- Enable and configure the Entity feature site-wide
- Associate users with entities through authentication
- Implement entity-based access control for chat apps
- Set up entity filtering in the admin interface
- Configure entity autocomplete for admin users
Prerequisites
Section titled “Prerequisites”- A running Pika installation
- Custom authentication provider configured
- Users with organizational entity data available
- Admin access to configure site features
Understanding Entities
Section titled “Understanding Entities”An entity represents an organizational unit that users belong to.
Common Entity Types
Section titled “Common Entity Types”- Customer Accounts: Individual customer companies or organizations
- Business Units: Different divisions within your organization
- Partners: External partner organizations
- Departments: Internal organizational departments
Entity Benefits
Section titled “Entity Benefits”- Multi-Tenancy: Isolate data and access by organization
- Access Control: Restrict chat apps to specific entities
- Session Filtering: Filter admin insights by entity
- User Management: Organize users by their organizations
Step 1: Enable Entity Feature
Section titled “Step 1: Enable Entity Feature”Configure the Entity feature in your pika-config.ts.
Location: apps/pika-chat/pika-config.ts
export const pikaConfig: PikaConfig = { siteFeatures: { entity: { enabled: true, attributeName: 'accountId', // Field in customData containing entity ID searchPlaceholderText: 'Search for an account...', displayNameSingular: 'Account', displayNamePlural: 'Accounts', tableColumnHeaderTitle: 'Account ID' } }};Configuration Options
Section titled “Configuration Options”| Property | Type | Description |
|---|---|---|
enabled | boolean | Whether the entity feature is active site-wide |
attributeName | string | Field name in user.customData containing entity identifier |
searchPlaceholderText | string | Placeholder text for entity search inputs |
displayNameSingular | string | Singular form of entity name for UI display |
displayNamePlural | string | Plural form of entity name for UI display |
tableColumnHeaderTitle | string | Header text for entity columns in tables |
Step 2: Configure User Entity Association
Section titled “Step 2: Configure User Entity Association”Ensure your authentication provider populates user data with entity identifiers.
Location: apps/pika-chat/src/lib/server/auth-provider/index.ts
export default class YourAuthProvider extends AuthProvider<YourAuthData, YourCustomData> { async authenticate(event: RequestEvent): Promise<AuthenticateResult<YourAuthData, YourCustomData>> { // ... authentication logic
return { 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, // Optional: human-readable name email: userData.email // ... other custom data }, // ... other user properties } }; }}Step 3: Implement Entity Data Functions
Section titled “Step 3: Implement Entity Data Functions”Create the entity lookup functions for the admin interface.
Location: apps/pika-chat/src/routes/(auth)/api/site-admin/custom-data.ts
import type { AuthenticatedUser, SimpleOption } from 'pika-shared/types/chatbot/chatbot-types';
// Function 1: Search entities for autocompleteexport async function getValuesForEntityAutoComplete( valueProvidedByUser: string, user: AuthenticatedUser<RecordOrUndef, RecordOrUndef>, chatAppId?: string): Promise<SimpleOption[] | undefined> { // Query your entity data source (API, database, etc.) // Filter based on valueProvidedByUser // Return formatted options
// Example implementation: const entities = await fetchEntitiesFromAPI(valueProvidedByUser);
return entities.map(entity => ({ value: entity.id, // This gets stored in access control label: entity.displayName // This is displayed to admins }));}
// Function 2: Get entity details by IDsexport async function getValuesForEntityList( entityIds: string[], user: AuthenticatedUser<RecordOrUndef, RecordOrUndef>, chatAppId?: string): Promise<SimpleOption[] | undefined> { // Fetch entity details for the given IDs // Return formatted options
// Example implementation: const entities = await fetchEntitiesByIds(entityIds);
return entities.map(entity => ({ value: entity.id, // Entity identifier label: entity.displayName // Display name for admin UI }));}Example Implementations
Section titled “Example Implementations”Database Query:
// Autocomplete: Search for entitiesexport async function getValuesForEntityAutoComplete( valueProvidedByUser: string, user: AuthenticatedUser<RecordOrUndef, RecordOrUndef>): Promise<SimpleOption[]> { const query = ` SELECT account_id, account_name FROM accounts WHERE account_name ILIKE $1 LIMIT 20 `;
const results = await db.query(query, [`%${valueProvidedByUser}%`]);
return results.rows.map(row => ({ value: row.account_id, label: row.account_name }));}
// List: Get entities by IDsexport async function getValuesForEntityList( entityIds: string[], user: AuthenticatedUser<RecordOrUndef, RecordOrUndef>): Promise<SimpleOption[]> { const query = ` SELECT account_id, account_name FROM accounts WHERE account_id = ANY($1) `;
const results = await db.query(query, [entityIds]);
return results.rows.map(row => ({ value: row.account_id, label: row.account_name }));}External API:
// Autocomplete: Search for entitiesexport async function getValuesForEntityAutoComplete( valueProvidedByUser: string, user: AuthenticatedUser<RecordOrUndef, RecordOrUndef>): Promise<SimpleOption[]> { const response = await fetch( `https://your-api.com/accounts/search?q=${encodeURIComponent(valueProvidedByUser)}`, { headers: { 'Authorization': `Bearer ${process.env.API_TOKEN}` } } );
const data = await response.json();
return data.accounts.map(account => ({ value: account.id, label: `${account.name} (${account.id})` }));}
// List: Get entities by IDsexport async function getValuesForEntityList( entityIds: string[], user: AuthenticatedUser<RecordOrUndef, RecordOrUndef>): Promise<SimpleOption[]> { const response = await fetch( `https://your-api.com/accounts/batch`, { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.API_TOKEN}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ ids: entityIds }) } );
const data = await response.json();
return data.accounts.map(account => ({ value: account.id, label: `${account.name} (${account.id})` }));}Step 4: Configure Entity-Based Access Control
Section titled “Step 4: Configure Entity-Based Access Control”Use the Site Admin interface or configuration to restrict chat apps to specific entities.
Via Admin Interface
Section titled “Via Admin Interface”- Navigate to Site Admin
- Select a chat app
- Scroll to Access Control section
- Search and select entities in:
- Exclusive External Access Control: For external users
- Exclusive Internal Access Control: For internal users
Via Configuration Override
Section titled “Via Configuration Override”// Create override through admin APIconst entityOverride = { enabled: true, exclusiveExternalAccessControl: [ 'account_enterprise_001', 'account_enterprise_002', 'account_premium_tier' ], exclusiveInternalAccessControl: [ 'customer_success_team', 'enterprise_support_team' ]};Step 5: Test Entity Configuration
Section titled “Step 5: Test Entity Configuration”Test User Association
Section titled “Test User Association”- Log in as a user with entity data
- Check user profile shows correct entity
- Verify entity field is populated in customData
// In browser console or logsconsole.log('User Entity:', user.customData.accountId);Test Access Control
Section titled “Test Access Control”- Create test chat app with entity restrictions
- Log in as user from allowed entity → Should have access
- Log in as user from different entity → Should be denied
- Log in as internal user → Check internal entity rules
Test Admin Interface
Section titled “Test Admin Interface”- Open Site Admin as admin user
- Edit a chat app
- Search for entities in access control section
- Verify autocomplete shows matching entities
- Save and test access with restricted entities
Entity Access Control Precedence
Section titled “Entity Access Control Precedence”Understanding how entity-based access works with other rules:
- Disabled Override: If
enabled: false, no access regardless of entity - Exclusive User ID Control: Specific user IDs take precedence over entity rules
- Exclusive Entity Control: Entity-based restrictions (this feature)
- General Access Rules: Fall back to user type/role checking
// Precedence exampleif (override.enabled === false) { return DENY_ACCESS; // Level 1: Disabled}
if (override.exclusiveUserIdAccessControl?.length > 0) { return checkUserIds(user); // Level 2: Specific users}
if (override.exclusiveExternalAccessControl?.length > 0) { return checkEntityAccess(user); // Level 3: Entity-based}
return checkGeneralRules(user); // Level 4: Type/role basedCommon Patterns
Section titled “Common Patterns”Pattern 1: Multi-Tenant SaaS
Section titled “Pattern 1: Multi-Tenant SaaS”Different customers can only access their own chat apps:
// Customer A's chat appconst customerAOverride = { enabled: true, exclusiveExternalAccessControl: ['customer_a_account']};
// Customer B's chat appconst customerBOverride = { enabled: true, exclusiveExternalAccessControl: ['customer_b_account']};Pattern 2: Partner Portal
Section titled “Pattern 2: Partner Portal”Different partners have access to different tools:
const partnerToolsOverride = { enabled: true, exclusiveExternalAccessControl: [ 'partner_gold', 'partner_platinum', 'partner_diamond' ]};Pattern 3: Department Isolation
Section titled “Pattern 3: Department Isolation”Internal users from specific departments:
const hrToolsOverride = { enabled: true, exclusiveInternalAccessControl: [ 'hr_department', 'executive_team' ]};Pattern 4: Enterprise Account Access
Section titled “Pattern 4: Enterprise Account Access”External users from specific enterprise accounts:
const enterpriseOverride = { enabled: true, exclusiveExternalAccessControl: [ 'enterprise_client_001', 'enterprise_client_002', 'enterprise_client_003' ]};Session Insights Integration
Section titled “Session Insights Integration”When entity feature is enabled, session insights gain entity filtering:
- Entity Display: Shows which entity each session belongs to
- Entity Filtering: Filter session data by specific entities
- Entity Aggregation: Group and analyze sessions by entity
Testing Checklist
Section titled “Testing Checklist”Verify entity configuration works correctly:
Troubleshooting
Section titled “Troubleshooting”Entity Feature Not Working
Section titled “Entity Feature Not Working”- Verify Configuration: Ensure
entity.enabled: truein pika-config - Check attributeName: Must match field name in
customData - Validate User Data: Confirm users have entity field populated
- Test Field Path: Verify
attributeNamepoints to correct field
Entity Autocomplete Issues
Section titled “Entity Autocomplete Issues”- Implement Both Functions: Ensure both
getValuesForEntityAutoComplete()andgetValuesForEntityList()are implemented - Check API Permissions: Verify web app can access entity data source
- Validate Response Format: Confirm both functions return
SimpleOption[]withvalueandlabel - Test Search: Try autocomplete with various search terms
- Test Display: Verify entity names display correctly for already-configured entities
Access Control Not Working
Section titled “Access Control Not Working”- Entity Matching: Ensure entity values in access lists exactly match user data
- Field Path: Verify
entity.attributeNamecorrectly points to entity field - Precedence Rules: Check if higher precedence rules are interfering
- User Type: Verify internal vs external entity lists are configured correctly
Debug Entity Access
Section titled “Debug Entity Access”// Log entity resolutionconsole.log('Entity Config:', pikaConfig.siteFeatures.entity);console.log('User Entity:', user.customData[pikaConfig.siteFeatures.entity.attributeName]);console.log('Override Rules:', chatApp.override?.exclusiveExternalAccessControl);
// Check entity extractionconst entityId = user.customData[pikaConfig.siteFeatures.entity.attributeName];console.log('Extracted Entity ID:', entityId);console.log('Allowed Entities:', allowedEntityList);console.log('Has Access:', allowedEntityList.includes(entityId));Security Considerations
Section titled “Security Considerations”Data Validation
Section titled “Data Validation”- Always validate entity field exists and contains expected values
- Ensure entity field is always populated for users who need entity-based access
- Validate entity values before using in access control
Field Accessibility
Section titled “Field Accessibility”- Ensure specified field path is always populated for relevant users
- Handle cases where entity data is missing or invalid
- Provide clear error messages for configuration issues
Logging
Section titled “Logging”- Log entity matching decisions for audit trails
- Track entity-based access grants and denials
- Monitor for suspicious access patterns
Next Steps
Section titled “Next Steps”- Configure Chat App Access Control - Understand full access control system
- Integrate Your Authentication System - Set up entity field mapping
- Manage User Sessions - Entity-scoped sessions
Related Documentation
Section titled “Related Documentation”- Entity Management Capability - Learn more about entities
- Multi-Tenancy - Multi-tenant architecture
- Access Control Reference - Complete access control options