Skip to main content

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

  1. Manual ID — If userIdField is set in the Inspector, it is used and stored
  2. Snap Account ID — If useSnapAccountId is enabled (default: true), uses a hash of the Snapchat account's displayName for cross-device persistence
  3. Cached Snap ID — If the Snap Account ID was resolved in a previous session, uses the cached value from estuary_snap_user_id storage key
  4. Stored Device ID — Loads a previously stored ID from device PersistentStorage (estuary_user_id key)
  5. 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

In the EstuaryCredentials component Inspector:

FieldTypeDefaultDescription
Use Snap Account IdbooleantrueUse Snapchat account ID for cross-device conversation persistence
User IDstring""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:

  1. Open EstuaryCredentials in the Inspector
  2. Enter the desired User ID in the User ID field
  3. 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:

  1. Server recognizes the playerId
  2. Loads previous conversation history
  3. AI has full context of past interactions
  4. Conversation continues seamlessly

Session vs Conversation

ConceptDescriptionPersistence
Session IDUnique per connectionChanges each connect
Player IDUser identifierPersists on device
Conversation IDThread identifierTied 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

  1. Ensure EstuaryCredentials is in your scene
  2. Check that it runs before other Estuary components
  3. Verify PersistentStorageSystem is available

Wrong Conversation Context

If the AI doesn't remember previous conversations:

  1. Verify the same User ID is being used
  2. Check console for the User ID banner
  3. Try setting the ID manually in Inspector

Multiple IDs Being Generated

If a new ID is generated each session:

  1. Check for errors in PersistentStorage
  2. Ensure the storage key isn't being cleared elsewhere
  3. Verify EstuaryCredentials.onAwake() is running

Next Steps