Skip to main content
AppSession represents one user’s active connection to your app. Each time a user starts your app on their glasses, a new AppSession is created with a unique ID.

Core Concept

When you extend AppServer, you override onSession() which receives an AppSession object:
import { AppServer, AppSession } from '@mentraos/sdk';

class MyApp extends AppServer {
  protected async onSession(session: AppSession, sessionId: string, userId: string) {
    // 'session' is the AppSession object
    // Use it to interact with this user's glasses
  }
}

What AppSession Provides

AppSession is your interface to everything happening on the user’s glasses:
PropertyPurpose
session.eventsSubscribe to data streams (voice, buttons, sensors)
session.layoutsUpdate what displays on glasses
session.audioPlay audio, text-to-speech, capture voice
session.cameraTake photos, stream video
session.ledControl LED lights
session.locationAccess GPS location
session.dashboardShow persistent status info
session.simpleStorageStore user preferences and data
session.settingsAccess app settings from dev console
session.capabilitiesCheck what hardware is available
session.loggerStructured logging with session context

Session Lifecycle

1. Session Starts

Cloud calls your onSession() with:
  • session - The AppSession object
  • sessionId - Unique ID (UUID string)
  • userId - User’s ID (email)
protected async onSession(session: AppSession, sessionId: string, userId: string) {
  session.logger.info('Session started', { sessionId, userId });

  // Set up your app for this user
  this.setupApp(session);
}

2. Session is Active

Use the session object to interact with glasses:
protected async onSession(session: AppSession, sessionId: string, userId: string) {
  // Display content
  session.layouts.showTextWall('Welcome!');

  // Listen for voice input
  session.events.onTranscription((data) => {
    session.logger.info('User said:', data.text);
  });

  // Play audio
  session.audio.playTTS('Hello from your app');
}

3. Session Ends

Session ends when:
  • User stops the app
  • Glasses disconnect
  • Network error occurs
  • Your server calls session.disconnect()
protected async onStop(sessionId: string, userId: string, reason: string) {
  // Clean up resources
  console.log(`Session ${sessionId} ended: ${reason}`);
}
The session object is NOT available in onStop(). Do cleanup that doesn’t require the session object.

One Session Per User

Each user gets their own isolated AppSession:
class MyApp extends AppServer {
  private sessions = new Map<string, AppSession>();

  protected async onSession(session: AppSession, sessionId: string, userId: string) {
    // Store this user's session
    this.sessions.set(sessionId, session);

    session.layouts.showTextWall(`Active users: ${this.sessions.size}`);

    // Each user has independent state
    session.events.onTranscription((data) => {
      // Only this user's voice data
      session.layouts.showTextWall(`You said: ${data.text}`);
    });
  }

  protected async onStop(sessionId: string, userId: string, reason: string) {
    this.sessions.delete(sessionId);
  }
}
Key points:
  • Sessions don’t interfere with each other
  • Each has separate event subscriptions
  • Each has separate display state
  • Each has separate storage (via userId)

Session Information

Access session details:
protected async onSession(session: AppSession, sessionId: string, userId: string) {
  // Session identifiers
  console.log('Session ID:', sessionId);  // UUID
  console.log('User ID:', userId);        // Email address

  // Device capabilities
  if (session.capabilities) {
    console.log('Device:', session.capabilities.modelName);
    console.log('Has camera:', session.capabilities.hasCamera);
    console.log('Has display:', session.capabilities.hasDisplay);
  }

  // User settings from dev console
  const theme = session.settings.get('theme', 'light');

  // User's stored data
  const savedData = await session.simpleStorage.get('preferences');
}

Working with Sessions

Store Session References

class MyApp extends AppServer {
  private userSessions = new Map<string, AppSession>();

  protected async onSession(session: AppSession, sessionId: string, userId: string) {
    // Store by sessionId for cleanup
    this.userSessions.set(sessionId, session);

    // Or store by userId to find user's session later
    this.userSessions.set(userId, session);
  }

  // Helper to get a user's session
  private getSessionForUser(userId: string): AppSession | undefined {
    return this.userSessions.get(userId);
  }
}

Session-Scoped State

protected async onSession(session: AppSession, sessionId: string, userId: string) {
  // Store state specific to this session
  const sessionState = {
    startTime: Date.now(),
    messageCount: 0,
    currentView: 'home'
  };

  session.events.onTranscription((data) => {
    sessionState.messageCount++;

    if (data.text.includes('stats')) {
      const duration = Date.now() - sessionState.startTime;
      session.layouts.showTextWall(
        `Messages: ${sessionState.messageCount}\n` +
        `Time: ${Math.floor(duration / 1000)}s`
      );
    }
  });
}

Cross-Session Communication

class ChatApp extends AppServer {
  private activeSessions = new Map<string, AppSession>();

  protected async onSession(session: AppSession, sessionId: string, userId: string) {
    this.activeSessions.set(sessionId, session);

    // Broadcast to all connected users
    session.events.onTranscription((data) => {
      if (data.isFinal) {
        this.broadcastMessage(userId, data.text);
      }
    });
  }

  private broadcastMessage(fromUserId: string, message: string) {
    // Send to all active sessions
    for (const [sid, sess] of this.activeSessions) {
      sess.layouts.showTextWall(`${fromUserId}: ${message}`);
    }
  }

  protected async onStop(sessionId: string, userId: string, reason: string) {
    this.activeSessions.delete(sessionId);
  }
}

Important Notes

Don’t share AppSession objects between users. Each session is isolated and user-specific.
UserId is persistent for each user If a user stops and restarts your app, they get the same userId.
Use session.logger for debugging. It automatically includes session context in logs.

Quick Reference

protected async onSession(session: AppSession, sessionId: string, userId: string) {
  // Display
  session.layouts.showTextWall('Hello');

  // Events
  session.events.onTranscription(data => {});
  session.events.onButtonPress(data => {});

  // Audio
  await session.audio.playTTS('Hello');

  // Storage
  await session.simpleStorage.set('key', 'value');
  const val = await session.simpleStorage.get('key');

  // Settings
  const setting = session.settings.get('settingKey', 'default');

  // Capabilities
  if (session.capabilities?.hasCamera) {
    await session.camera.requestPhoto();
  }

  // Logging
  session.logger.info('Message', { extra: 'data' });

  // Disconnect
  session.disconnect();
}

Next Steps