User ID Management
Learn how to manage user identity and maintain conversation persistence across sessions using the Estuary SDK.
Overview
User ID management is crucial for:
- Conversation Persistence: Continue conversations where the user left off
- Context Retention: AI remembers previous interactions
- User Personalization: Tailor experiences based on history
The Estuary SDK provides automatic User ID management through the EstuaryCredentials component.
How User IDs Work
Each user is identified by a unique userId (also called playerId in the SDK). This ID is sent with every connection to the Estuary server.
┌─────────────────────────────────────────────────────────────┐
│ User ID Flow │
├─────────────────────────────────────────────────────────────┤
│ │
│ Session 1 (First Use) │
│ ┌────────────────────┐ ┌────────────────────┐ │
│ │ EstuaryCredentials │---→│ Generate User ID │ │
│ │ (userId empty) │ │ "spectacles_abc123"│ │
│ └────────────────────┘ └─────────┬──────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────┐ │
│ │ Store in Device │ │
│ │ PersistentStorage │ │
│ └───────────────────┘ │
│ │
│ Session 2+ (Return User) │
│ ┌────────────────────┐ ┌────────────────────┐ │
│ │ EstuaryCredentials │---→│ Load from Storage │ │
│ │ (userId empty) │ │ "spectacles_abc123"│ │
│ └────────────────────┘ └─────────┬──────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────┐ │
│ │ Same User ID! │ │
│ │ Conversation │ │
│ │ Context Restored │ │
│ └───────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Automatic User ID Management
The EstuaryCredentials component handles User IDs automatically with a 5-level priority system:
Priority Order
- Manual ID — If
userIdFieldis set in the Inspector, it is used and stored - Snap Account ID — If
useSnapAccountIdis enabled (default:true), uses a hash of the Snapchat account'sdisplayNamefor cross-device persistence - Cached Snap ID — If the Snap Account ID was resolved in a previous session, uses the cached value from
estuary_snap_user_idstorage key - Stored Device ID — Loads a previously stored ID from device
PersistentStorage(estuary_user_idkey) - Generated ID — Creates a new random ID (
spectacles_+ timestamp) and stores it
Default Behavior
// When EstuaryCredentials initializes, it resolves the user ID:
// 1. Manual override from Inspector
if (this.userIdField && this.userIdField.trim().length > 0) {
// Uses the manually entered ID
}
// 2. Snap Account ID (cross-device persistence)
else if (this.useSnapAccountId) {
// Hashes displayName from userContextSystem
// Format: "spectacles_" + hash
// Same Snapchat account → same ID on any device
}
// 3. Cached Snap ID from previous session
// Falls back if live Snap ID isn't available right now
// 4. Stored ID from device persistent storage
// Device-specific, persists across Lens sessions
// 5. Generate new random ID
// "spectacles_" + Date.now().toString(36)
Snap Account ID (Cross-Device Persistence)
The Snap Account ID feature is the recommended approach for production. When enabled, the SDK uses the Snapchat account's displayName (via userContextSystem) to create a stable, cross-device user ID:
- Same Snapchat account on different devices → same user ID → conversations persist
- No manual configuration needed — works automatically
- Falls back to device-local storage if the Snap API is unavailable
The SDK also asynchronously requests the display name via callback. If it arrives after initial resolution, it updates the current session and caches the result for next time.
Configuration Options
Using Inspector (Recommended)
In the EstuaryCredentials component Inspector:
| Field | Type | Default | Description |
|---|---|---|---|
| Use Snap Account Id | boolean | true | Use Snapchat account ID for cross-device conversation persistence |
| User ID | string | "" | Override: enter a specific User ID (leave empty for automatic) |
Console Output
When initialized, EstuaryCredentials logs the User ID with its source:
╔════════════════════════════════════════════════════════════╗
║ ESTUARY USER ID ║
║ spectacles_lq8x7k4j ║
║ Source: Snap account ║
║ ✓ Cross-device persistence enabled (Snap account) ║
╚════════════════════════════════════════════════════════════╝
Manual User ID Setting
For Testing Specific Conversations
You can set a specific User ID to test or resume a specific conversation:
- Open
EstuaryCredentialsin the Inspector - Enter the desired User ID in the
User IDfield - This ID will be used and stored for future sessions
// In Inspector:
userIdField: "test_user_12345"
// Result:
// - Uses "test_user_12345" for this and future sessions
// - Previous conversation with this ID will be restored
Programmatic User ID
For dynamic User ID assignment:
@component
export class CustomUserManagement extends BaseScriptComponent {
onAwake() {
// Create character with specific user ID
const userId = this.getOrCreateUserId();
const character = new EstuaryCharacter(characterId, userId);
}
private getOrCreateUserId(): string {
const store = global.persistentStorageSystem.store;
// Check for existing ID
if (store.has("my_custom_user_id")) {
return store.getString("my_custom_user_id");
}
// Generate new ID based on your logic
const newId = this.generateCustomId();
store.putString("my_custom_user_id", newId);
return newId;
}
private generateCustomId(): string {
// Your custom ID generation logic
return `user_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
}
Accessing the User ID
From EstuaryCredentials Singleton
import { EstuaryCredentials } from 'estuary-lens-studio-sdk';
// Check if credentials exist
if (EstuaryCredentials.hasInstance) {
const userId = EstuaryCredentials.instance.userId;
print(`Current User ID: ${userId}`);
}
From Session Info
After connecting, the session info includes the player ID:
character.on('connected', (session: SessionInfo) => {
print(`Session ID: ${session.sessionId}`);
print(`Player ID: ${session.playerId}`);
print(`Conversation ID: ${session.conversationId}`);
});
The conversationId is particularly useful - it remains the same across sessions for the same user, enabling conversation continuity.
Conversation Persistence
How It Works
When a user reconnects with the same User ID:
- Server recognizes the
playerId - Loads previous conversation history
- AI has full context of past interactions
- Conversation continues seamlessly
Session vs Conversation
| Concept | Description | Persistence |
|---|---|---|
| Session ID | Unique per connection | Changes each connect |
| Player ID | User identifier | Persists on device |
| Conversation ID | Thread identifier | Tied to Player ID |
Resetting User Identity
Clear Stored User ID
To start fresh (new user, new conversation):
// Clear all stored User IDs
const store = global.persistentStorageSystem.store;
store.remove("estuary_user_id");
store.remove("estuary_snap_user_id");
// Next initialization will re-resolve from Snap account or generate a new ID
Force New Conversation
Alternatively, use a different User ID format:
// Generate unique ID per session (no persistence)
const sessionOnlyId = `session_${Date.now()}`;
const character = new EstuaryCharacter(characterId, sessionOnlyId);
Multi-User Scenarios
For experiences with multiple users:
@component
export class MultiUserManager extends BaseScriptComponent {
private users: Map<string, EstuaryCharacter> = new Map();
/**
* Create or get a character instance for a specific user
*/
getCharacterForUser(userId: string): EstuaryCharacter {
if (this.users.has(userId)) {
return this.users.get(userId)!;
}
const character = new EstuaryCharacter(
characterId,
userId
);
this.users.set(userId, character);
return character;
}
/**
* Switch active user
*/
switchToUser(userId: string) {
const character = this.getCharacterForUser(userId);
EstuaryManager.instance.setActiveCharacter(character);
}
}
Best Practices
Let the SDK Manage IDs
Unless you have specific requirements, let EstuaryCredentials handle User IDs automatically:
// Just leave userIdField empty in Inspector
// SDK handles generation and persistence
Don't Expose User IDs
User IDs should be treated as internal identifiers:
// Don't show to users
print(`Your ID is: ${userId}`);
// Keep internal
// Just use for conversation persistence
Handle Storage Errors Gracefully
try {
const store = global.persistentStorageSystem.store;
userId = store.getString("estuary_user_id");
} catch (e) {
// Fall back to generated ID
userId = this.generateFallbackId();
}
Consider Privacy
- User IDs should not contain personal information
- Generated IDs are random and anonymous
- No user data is stored in the ID itself
Troubleshooting
User ID Not Persisting
- Ensure
EstuaryCredentialsis in your scene - Check that it runs before other Estuary components
- Verify
PersistentStorageSystemis available
Wrong Conversation Context
If the AI doesn't remember previous conversations:
- Verify the same User ID is being used
- Check console for the User ID banner
- Try setting the ID manually in Inspector
Multiple IDs Being Generated
If a new ID is generated each session:
- Check for errors in PersistentStorage
- Ensure the storage key isn't being cleared elsewhere
- Verify
EstuaryCredentials.onAwake()is running
Next Steps
- Action System - Trigger scene actions
- API Reference - Component details
- Voice Connection - Audio implementation