Skip to main content

Sessions Operations API

Info

Last Updated: 2026-01-08

Complete API reference for session lifecycle management and multi-session support.

Overview

The Sessions API (cortex.sessions.*) provides native session management for user-facing agentic platforms. Sessions track user interactions, manage activity timeouts, and provide an isolated context for conversations.

Key Features:

  • Multi-Session Support: Users can have multiple active sessions (web, mobile, API)
  • Activity Tracking: Automatic idle detection and session expiration
  • Extensible Metadata: Fully flexible metadata for any developer needs
  • Configurable Timeouts: Per-tenant or per-session lifecycle policies (via Governance API)
  • GDPR Integration: Sessions cascade delete with user deletion

Session 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 (Configurable via Governance):

ParameterTypeRequiredDefaultDescription
Active30mNoNo activity moves to idle
Idle24hNoAfter 24h idle, session is ended

Session Type

interface Session {
_id: string; // Convex document ID
sessionId: string; // Unique session identifier
userId: string;
tenantId?: string;
memorySpaceId?: string;

// Session state
status: "active" | "idle" | "ended";
startedAt: number;
lastActiveAt: number;
endedAt?: number;
expiresAt?: number;

// Fully extensible metadata - any shape you need
metadata?: SessionMetadata;

// Stats
messageCount: number;
memoryCount: number;
}

interface SessionMetadata {
device?: string;
browser?: string;
browserVersion?: string;
os?: string;
deviceType?: "desktop" | "mobile" | "tablet" | string;
ip?: string;
location?: string;
timezone?: string;
language?: string;
userAgent?: string;
[key: string]: unknown; // Any additional custom fields
}

Metadata Flexibility

The metadata field is completely flexible and can store 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",
},
});

Core Operations

create()

Create a new session for a user.

Signature:

cortex.sessions.create(params: CreateSessionParams): Promise<Session>

Parameters:

ParameterTypeRequiredDefaultDescription
sessionIdstringNoCustom ID (auto-generated if not provided)
userIdstringYesUser this session belongs to
tenantIdstringNoMulti-tenant isolation
memorySpaceIdstringNoAssociate with memory space
metadataSessionMetadataNoFully extensible metadata
expiresAtnumberNoOverride default expiry (ms since epoch)

Example:

// Create a new session
const session = await cortex.sessions.create({
userId: "user-123",
tenantId: "tenant-abc",
memorySpaceId: "user-123-personal",
metadata: {
deviceType: "web",
userAgent: "Mozilla/5.0...",
authProvider: "google",
},
});

console.log(`Session ${session.sessionId} started`);

get()

Get a session by ID.

Signature:

cortex.sessions.get(sessionId: string): Promise<Session | null>

Parameters:

ParameterTypeRequiredDefaultDescription
sessionIdstringYesThe unique session identifier to retrieve

Example:

const session = await cortex.sessions.get("session-xyz");
if (session) {
console.log(`Status: ${session.status}`);
console.log(`Last active: ${new Date(session.lastActiveAt)}`);
}

getOrCreate()

Get existing active session for user, or create a new one.

Signature:

cortex.sessions.getOrCreate(
userId: string,
metadata?: Record<string, unknown>
): Promise<Session>

Parameters:

ParameterTypeRequiredDefaultDescription
userIdstringYesUser ID to find or create session for
metadataRecord<string, unknown>NoMetadata for new session if created

Example:

// Returns existing active session or creates new one
const session = await cortex.sessions.getOrCreate("user-123", {
deviceType: "mobile",
});

// Useful for "resume session" flows
console.log(`Using session ${session.sessionId}`);

touch()

Update session activity timestamp (heartbeat).

Signature:

cortex.sessions.touch(sessionId: string): Promise<void>

Parameters:

ParameterTypeRequiredDefaultDescription
sessionIdstringYesSession ID to update activity for

Example:

// Update activity on every user interaction
await cortex.sessions.touch(session.sessionId);

// Prevents session from going idle

end()

Explicitly end a session.

Signature:

cortex.sessions.end(sessionId: string): Promise<void>

Parameters:

ParameterTypeRequiredDefaultDescription
sessionIdstringYesSession ID to end

Example:

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

// Session is now 'ended' and cannot be resumed

endAll()

End all active sessions for a user.

Signature:

cortex.sessions.endAll(
userId: string,
options?: EndAllOptions
): Promise<EndSessionsResult>

Parameters:

ParameterTypeRequiredDefaultDescription
userIdstringYesUser ID to end all sessions for
options.tenantIdstringNoTenant ID for multi-tenant isolation. When provided, only ends sessions for the user within that tenant. Without this (and no AuthContext), ALL sessions for the userId across ALL tenants are ended.

Returns:

ParameterTypeRequiredDefaultDescription
endednumberYesNumber of sessions ended
sessionIdsstring[]YesIDs of sessions that were ended

Example:

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

// End sessions only for a specific tenant (multi-tenant safe)
const tenantResult = await cortex.sessions.endAll("user-123", {
tenantId: "tenant-abc",
});
console.log(`Ended ${tenantResult.ended} sessions in tenant`);

list()

List sessions with filters.

Signature:

cortex.sessions.list(filters: SessionFilters): Promise<Session[]>

Parameters:

ParameterTypeRequiredDefaultDescription
userIdstringNoFilter by user ID
tenantIdstringNoFilter by tenant ID
memorySpaceIdstringNoFilter by memory space
status'active' | 'idle' | 'ended'NoFilter by status
limitnumberNo50Max results (1-1000)
offsetnumberNo0Pagination offset
Tip

PLANNED: Date range filters (startedAfter, startedBefore) are planned for a future release.

Example:

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

// Get tenant-wide sessions
const tenantSessions = await cortex.sessions.list({
tenantId: "tenant-abc",
status: "active",
limit: 100,
});

// Get sessions by memory space
const spaceSessions = await cortex.sessions.list({
memorySpaceId: "project-workspace",
status: "active",
});

count()

Count sessions matching filters.

Signature:

cortex.sessions.count(filters: SessionFilters): Promise<number>

Parameters:

ParameterTypeRequiredDefaultDescription
userIdstringNoFilter by user ID
tenantIdstringNoFilter by tenant ID
memorySpaceIdstringNoFilter by memory space
status'active' | 'idle' | 'ended'NoFilter by status

Example:

const activeSessions = await cortex.sessions.count({
tenantId: "tenant-abc",
status: "active",
});

console.log(`${activeSessions} users currently online`);

getActive()

Get all active sessions for a user.

Signature:

cortex.sessions.getActive(userId: string): Promise<Session[]>

Parameters:

ParameterTypeRequiredDefaultDescription
userIdstringYesUser ID to get active sessions for

Example:

const sessions = await cortex.sessions.getActive("user-123");
console.log(`User has ${sessions.length} active sessions`);

// Useful for "active devices" view
sessions.forEach((s) => {
console.log(`- ${s.metadata?.deviceType}: ${s.sessionId}`);
});

expireIdle()

Expire idle sessions (typically run as background job).

Signature:

cortex.sessions.expireIdle(options?: ExpireSessionsOptions): Promise<{ expired: number }>

Parameters:

ParameterTypeRequiredDefaultDescription
tenantIdstringNoLimit to specific tenant
idleTimeoutnumberNo1800000Idle timeout in milliseconds (default: 30 minutes)
Tip

PLANNED: Dry run mode (dryRun: boolean) is planned for a future release to preview without expiring.

Example:

// Background job to clean up idle sessions
const result = await cortex.sessions.expireIdle({
tenantId: "tenant-abc",
});
console.log(`Expired ${result.expired} idle sessions`);

// Custom idle timeout (15 minutes)
const customResult = await cortex.sessions.expireIdle({
tenantId: "tenant-abc",
idleTimeout: 15 * 60 * 1000, // 15 minutes in ms
});

Session Policies (Governance)

Session lifecycle timeouts are configurable via the Governance API.

await cortex.governance.setPolicy({
organizationId: "org-123",

// Session lifecycle configuration
sessions: {
lifecycle: {
idleTimeout: "30m", // Duration before session becomes idle
maxDuration: "24h", // Maximum session duration regardless of activity
autoExtend: true, // Automatically extend on activity
warnBeforeExpiry: "5m", // Optional: warn user before expiry
},
cleanup: {
autoExpireIdle: true, // Automatically expire idle sessions
deleteEndedAfter: "30d", // Delete ended sessions after 30 days
archiveAfter: "7d", // Optional: archive before deletion
},
limits: {
maxActiveSessions: 10, // Optional: max concurrent sessions per user
maxSessionsPerDevice: 3, // Optional: max sessions per device type
},
},
});

See Governance Policies API for full configuration options.


Multi-Tenancy

Sessions support full multi-tenant isolation via tenantId:

// 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',
});

Integration with Auth Context

Sessions integrate with the Auth Context system for tenant isolation:

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

// Create auth context with session info
const auth = createAuthContext({
userId: "user-123",
sessionId: "session-xyz",
tenantId: "tenant-abc",
authProvider: "auth0",
claims: {
email: "user@example.com",
roles: ["admin"],
},
});

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

// tenantId is automatically injected from auth context
// This provides tenant isolation without passing tenantId to every call
const session = await cortex.sessions.create({
userId: "user-123",
// tenantId is auto-injected from auth context
metadata: { deviceType: "web" },
});

// Queries are automatically scoped to the tenant
const sessions = await cortex.sessions.list({
userId: "user-123",
// tenantId is auto-injected, only returns sessions for this tenant
});
Note

The sessionId in AuthContext is for reference/tracking purposes. Session activity tracking (incrementing message/memory counts) should be done explicitly by calling touch() on user interactions.


GDPR Cascade

Sessions are included in GDPR cascade deletion:

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

// Sessions are deleted as part of the cascade:
// - User profile
// - All sessions for user
// - All conversations
// - All memories
// - All facts
// - Graph nodes (if configured)

Real-World Patterns

Session Resume Flow

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

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

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

Multi-Device Support

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,
}));
}

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

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,
};
}

Error Handling

SessionValidationError

The Sessions API throws SessionValidationError for client-side 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"
}
}

Validation Error Codes:

ParameterTypeRequiredDefaultDescription
INVALID_SESSION_IDerrorNosessionId must be a string
EMPTY_SESSION_IDerrorNosessionId cannot be empty
SESSION_ID_TOO_LONGerrorNosessionId cannot exceed 256 characters
INVALID_USER_IDerrorNouserId must be a string
EMPTY_USER_IDerrorNouserId cannot be empty
USER_ID_TOO_LONGerrorNouserId cannot exceed 256 characters
INVALID_TENANT_IDerrorNotenantId must be a string
EMPTY_TENANT_IDerrorNotenantId cannot be empty
TENANT_ID_TOO_LONGerrorNotenantId cannot exceed 256 characters
INVALID_STATUSerrorNostatus must be a string
INVALID_STATUS_VALUEerrorNostatus must be: active, idle, or ended
INVALID_PARAMSerrorNoSession params must be an object
MISSING_USER_IDerrorNouserId is required
INVALID_MEMORY_SPACE_IDerrorNomemorySpaceId must be a string
INVALID_EXPIRES_ATerrorNoexpiresAt must be a positive number
INVALID_METADATAerrorNometadata must be a plain object
INVALID_FILTERSerrorNoSession filters must be an object
INVALID_LIMITerrorNolimit must be a number between 1 and 1000
INVALID_OFFSETerrorNooffset must be a non-negative number

Runtime Errors

Backend operations throw standard Error for runtime failures:

try {
await cortex.sessions.touch("non-existent-session");
} catch (error) {
if (error instanceof Error) {
// Error message: "Session not found: non-existent-session"
console.log(error.message);
}
}
Tip

PLANNED: Session-specific error codes (CortexErrorCode.SESSION_NOT_FOUND, CortexErrorCode.SESSION_EXPIRED, CortexErrorCode.SESSION_ALREADY_ENDED) are planned for a future release to provide more structured error handling.