Skip to main content

Sessions Management

Info

Last Updated: 2026-01-08

Native session lifecycle management for user-facing agentic platforms with multi-device support, activity tracking, and configurable timeouts.

Overview

Cortex provides built-in session management through the cortex.sessions.* API. Sessions track user interactions, manage activity timeouts, and provide isolated context for conversations.

Why Sessions Matter:

  • Multi-Device Support: Users can have concurrent sessions (web, mobile, API) with activity tracked per device
  • Activity-Based Lifecycle: Automatic idle detection and session expiration protect resources
  • Context Isolation: Sessions provide a boundary for conversation context
  • Analytics Ready: Built-in tracking of message counts, memory usage, and activity patterns

Quick Start

import { Cortex } from "@cortex-platform/sdk";

const cortex = new Cortex({
convexUrl: process.env.CONVEX_URL!,
});

// Start a session when user connects
const session = await cortex.sessions.create({
userId: "user-123",
metadata: {
deviceType: "web",
userAgent: navigator.userAgent,
},
});

// Keep session alive with heartbeat
await cortex.sessions.touch(session.sessionId);

// End session on logout
await cortex.sessions.end(session.sessionId);

Session Lifecycle

Sessions follow a three-state lifecycle:

Session State Transitions
ACTIVE
User active (< 30min since last touch)
IDLE
No activity (< 24h) — touch() returns to ACTIVE
ENDED
Terminated — expireIdle() after 24h idle

Default Timeouts:

StateTimeoutDescription
Active30mNo activity moves session to idle
Idle24hAfter 24h idle, session is ended

Timeouts are configurable via the Governance Policies.

Core Operations

Creating Sessions

// Create a new session
const session = await cortex.sessions.create({
userId: "user-123",
tenantId: "tenant-abc", // Optional: multi-tenant isolation
memorySpaceId: "personal", // Optional: associate with memory space
metadata: {
deviceType: "mobile",
appVersion: "2.1.0",
platform: "ios",
},
});

// Or get existing session if active, create new if not
const session = await cortex.sessions.getOrCreate("user-123", {
deviceType: "web",
});

Activity Tracking

Keep sessions alive by calling touch() on user interactions:

// Update activity timestamp
await cortex.sessions.touch(session.sessionId);

// Prevents session from going idle
// Call on message send, page navigation, etc.

Ending Sessions

// End single session (on logout)
await cortex.sessions.end(session.sessionId);

// End all sessions for user (on password change)
const result = await cortex.sessions.endAll("user-123");
console.log(`Ended ${result.ended} sessions`);

// End sessions for specific tenant only
await cortex.sessions.endAll("user-123", {
tenantId: "tenant-abc",
});

Querying Sessions

// Get specific session
const session = await cortex.sessions.get("session-xyz");

// List active sessions for user
const activeSessions = await cortex.sessions.list({
userId: "user-123",
status: "active",
});

// Count sessions by status
const activeCount = await cortex.sessions.count({
tenantId: "tenant-abc",
status: "active",
});
console.log(`${activeCount} users currently online`);

Real-World Patterns

Session Resume Flow

Automatically resume existing sessions when users reconnect:

async function handleUserConnection(
userId: string,
deviceInfo: Record<string, unknown>,
) {
// Try to resume existing active session
const existingSessions = await cortex.sessions.getActive(userId);

if (existingSessions.length > 0) {
const session = existingSessions[0];
await cortex.sessions.touch(session.sessionId);
return session;
}

// Create new session
return cortex.sessions.create({
userId,
metadata: deviceInfo,
});
}

Multi-Device Management

Show users their active devices and allow remote logout:

// Get all active sessions for "active devices" UI
async function getActiveSessions(userId: string) {
const sessions = await cortex.sessions.getActive(userId);

return sessions.map((s) => ({
id: s.sessionId,
device: s.metadata?.deviceType || "unknown",
lastActive: new Date(s.lastActiveAt),
isCurrent: s.sessionId === currentSessionId,
}));
}

// Logout from all other devices
async function logoutOtherDevices(userId: string, currentSessionId: string) {
const sessions = await cortex.sessions.getActive(userId);

for (const session of sessions) {
if (session.sessionId !== currentSessionId) {
await cortex.sessions.end(session.sessionId);
}
}
}

Session Analytics Dashboard

Track platform engagement:

async function getSessionStats(tenantId: string) {
const [active, idle, ended] = await Promise.all([
cortex.sessions.count({ tenantId, status: "active" }),
cortex.sessions.count({ tenantId, status: "idle" }),
cortex.sessions.count({ tenantId, status: "ended" }),
]);

return {
currentlyOnline: active,
recentlyActive: idle,
totalSessions: active + idle + ended,
};
}

Background Session Cleanup

Run periodic cleanup of idle sessions:

// Background job (e.g., cron every 15 minutes)
async function cleanupIdleSessions() {
const result = await cortex.sessions.expireIdle({
tenantId: "tenant-abc",
idleTimeout: 30 * 60 * 1000, // 30 minutes
});

console.log(`Expired ${result.expired} idle sessions`);
}

Session Metadata

The metadata field is fully flexible and stores any data your application needs:

// Device and client info
await cortex.sessions.create({
userId: "user-123",
metadata: {
deviceType: "mobile",
appVersion: "2.1.0",
platform: "ios",
deviceId: "device-xyz",
},
});

// Auth provider claims
await cortex.sessions.create({
userId: "user-123",
metadata: {
authProvider: "auth0",
authMethod: "oauth",
email: "user@example.com",
roles: ["admin", "editor"],
permissions: ["read", "write", "delete"],
},
});

// Geographic context
await cortex.sessions.create({
userId: "user-123",
metadata: {
ipAddress: "192.168.1.1",
country: "US",
timezone: "America/New_York",
locale: "en-US",
},
});

Multi-Tenancy

Sessions support full multi-tenant isolation:

// Create tenant-scoped session
const session = await cortex.sessions.create({
userId: "user-123",
tenantId: "customer-abc", // SaaS platform isolation
metadata: { ... },
});

// Query sessions within tenant
const tenantSessions = await cortex.sessions.list({
tenantId: "customer-abc",
status: "active",
});

When using Auth Context, tenantId is automatically injected:

import { createAuthContext } from "@cortex-platform/sdk";

const auth = createAuthContext({
userId: "user-123",
sessionId: "session-xyz",
tenantId: "tenant-abc", // Auto-injected to all operations
});

const cortex = new Cortex({
convexUrl: process.env.CONVEX_URL!,
auth,
});

// tenantId is automatically applied
const session = await cortex.sessions.create({
userId: "user-123",
// tenantId auto-injected from auth context
});

GDPR Compliance

Sessions are included in GDPR cascade deletion:

// When deleting a user, all their sessions are deleted
await cortex.users.delete("user-123", { cascade: true });

// Cascade includes:
// - User profile
// - All sessions for user
// - All conversations
// - All memories
// - All facts
// - Graph nodes (if configured)

Error Handling

The Sessions API throws SessionValidationError for validation failures:

import { SessionValidationError } from "@cortex-platform/sdk";

try {
await cortex.sessions.create({
userId: "", // Invalid: empty userId
});
} catch (error) {
if (error instanceof SessionValidationError) {
console.log(`Validation error: ${error.message}`);
console.log(`Code: ${error.code}`); // e.g., "EMPTY_USER_ID"
console.log(`Field: ${error.field}`); // e.g., "userId"
}
}

Common Validation Errors:

CodeDescription
MISSING_USER_IDuserId is required
EMPTY_USER_IDuserId cannot be empty
INVALID_STATUS_VALUEstatus must be: active, idle, or ended
INVALID_LIMITlimit must be between 1 and 1000