Skip to main content

Memory Space Operations API

Info

Last Updated: 2026-01-09 | Version: v0.29.0+

This document covers the cortex.memorySpaces.* namespace for managing memory spaces, participants, and access control.

Overview​

Memory spaces are the fundamental isolation boundary in Cortex. The cortex.memorySpaces.* API provides explicit management and registration capabilities beyond the implicit creation that happens automatically when you first use a memorySpaceId.

When to use this API:

  • Production deployments (explicit registration)
  • Analytics and observability needs
  • Hive Mode with participant tracking
  • Memory space lifecycle management
  • Access control and permissions
  • Multi-tenant SaaS (automatic tenantId isolation via AuthContext)

When you don't need it:

  • Quick prototypes (use implicit creation)
  • Simple single-agent apps
  • When you just want memory to "just work"

Table of Contents​


register()​

Register a memory space with metadata and participant tracking.

Signature​

cortex.memorySpaces.register(
params: RegisterMemorySpaceParams
): Promise<MemorySpace>

Parameters​

ParameterTypeRequiredDefaultDescription
memorySpaceIdstringYes—Memory space ID (e.g., "user-123-personal")
tenantIdstringNo—Multi-tenancy isolation (auto-injected from AuthContext)
namestringNo—Human-readable name
type"personal" | "team" | "project" | "custom"Yes—Organization type
participantsArray<{ id: string; type: string }>No—For Hive Mode tracking - participant identifier and type ("user", "agent", "tool", etc.)
metadataRecord<string, unknown>No—Custom metadata
Info

v0.29.0+: Graph sync is now automatic when CORTEX_GRAPH_SYNC=true. The syncToGraph option has been removed.

Returns​

interface MemorySpace {
_id: string;
memorySpaceId: string;
name?: string;
type: "personal" | "team" | "project" | "custom";
tenantId?: string; // Auto-injected from AuthContext for multi-tenancy
participants: Array<{
id: string;
type: string;
joinedAt: number;
}>;
metadata: Record<string, unknown>;
status: "active" | "archived";
createdAt: number;
updatedAt: number;
}

Examples​

// Register user's personal memory space
await cortex.memorySpaces.register({
memorySpaceId: "user-123-personal",
name: "Alice's Personal AI Memory",
type: "personal",
participants: [
{ id: "cursor", type: "ai-tool" },
{ id: "claude", type: "ai-tool" },
{ id: "notion-ai", type: "ai-tool" },
],
metadata: {
owner: "user-123",
environment: "production",
},
});

Error Handling​

try {
await cortex.memorySpaces.register({
memorySpaceId: "space-123",
type: "personal",
});
} catch (error) {
if (error.code === "MEMORYSPACE_ALREADY_EXISTS") {
console.log("Space already registered");
// Use update() instead
}
}

get()​

Retrieve memory space details and metadata.

Signature​

cortex.memorySpaces.get(memorySpaceId: string): Promise<MemorySpace | null>

Parameters​

ParameterTypeRequiredDefaultDescription
memorySpaceIdstringYes—The memory space ID to retrieve

Examples​

const space = await cortex.memorySpaces.get("user-123-personal");

if (space) {
console.log(space.name); // "Alice's Personal AI Memory"
console.log(space.participants); // [{ id: 'cursor', type: 'ai-tool', joinedAt: 1234567890 }, ...]
console.log(space.type); // 'personal'
}

list()​

List memory spaces with filtering, pagination, and sorting.

Signature​

cortex.memorySpaces.list(filters?: ListMemorySpacesFilter): Promise<ListMemorySpacesResult>

Parameters​

ParameterTypeRequiredDefaultDescription
type"personal" | "team" | "project" | "custom"No—Filter by memory space type
status"active" | "archived"No—Filter by status
tenantIdstringNo—Multi-tenancy filter (auto-injected from AuthContext)
participantstringNo—Filter by participant ID
limitnumberNo100Maximum number of results
offsetnumberNo0Pagination offset
sortBy"createdAt" | "updatedAt" | "name"No—Sort field
sortOrder"asc" | "desc"No"desc"Sort direction

Returns​

interface ListMemorySpacesResult {
spaces: MemorySpace[];
total: number;
hasMore: boolean;
offset: number;
}

Examples​

const result = await cortex.memorySpaces.list();

console.log(`Found ${result.total} memory spaces`);
result.spaces.forEach((space) => {
console.log(`${space.name} (${space.type})`);
});

count()​

Count memory spaces matching filter criteria.

Signature​

cortex.memorySpaces.count(filter?: {
type?: "personal" | "team" | "project" | "custom";
status?: "active" | "archived";
}): Promise<number>

Parameters​

ParameterTypeRequiredDefaultDescription
type"personal" | "team" | "project" | "custom"No—Filter by memory space type
status"active" | "archived"No—Filter by status

Examples​

// Count all spaces
const total = await cortex.memorySpaces.count();

// Count active personal spaces
const activePersonal = await cortex.memorySpaces.count({
type: "personal",
status: "active",
});

console.log(`${activePersonal} active personal spaces out of ${total} total`);

Search memory spaces by name or metadata.

Signature​

cortex.memorySpaces.search(query: string, options?: SearchOptions): Promise<MemorySpace[]>

Parameters​

ParameterTypeRequiredDefaultDescription
querystringYes—Search query string
type"personal" | "team" | "project" | "custom"No—Filter by memory space type
status"active" | "archived"No—Filter by status
limitnumberNo—Maximum number of results

Examples​

const results = await cortex.memorySpaces.search("engineering");

// Returns spaces with "engineering" in name or metadata
// [
// { memorySpaceId: 'team-engineering-workspace', name: 'Engineering Team', ... },
// { memorySpaceId: 'project-engineering-docs', name: 'Engineering Docs Project', ... }
// ]

update()​

Update memory space metadata (not participants - use updateParticipants()).

Signature​

cortex.memorySpaces.update(
memorySpaceId: string,
updates: MemorySpaceUpdates
): Promise<MemorySpace>
// Note: syncToGraph option removed in v0.29.0+ - sync is automatic

Parameters​

ParameterTypeRequiredDefaultDescription
memorySpaceIdstringYes—The memory space ID to update
namestringNo—New human-readable name
metadataRecord<string, unknown>No—Metadata to merge with existing
status"active" | "archived"No—New status

Options:

Info

v0.29.0+: Graph sync is now automatic when CORTEX_GRAPH_SYNC=true. No options parameter needed.

Examples​

await cortex.memorySpaces.update("user-123-personal", {
name: "Alice's Updated Personal Space",
});

addParticipant()​

Add a single participant to a memory space.

Signature​

cortex.memorySpaces.addParticipant(
memorySpaceId: string,
participant: { id: string; type: string; joinedAt: number }
): Promise<MemorySpace>

Parameters​

ParameterTypeRequiredDefaultDescription
memorySpaceIdstringYes—The memory space ID
participant.idstringYes—Participant identifier
participant.typestringYes—Participant type ("user", "agent", "tool", etc.)
participant.joinedAtnumberYes—Timestamp when participant joined

Examples​

// User installs new AI tool
await cortex.memorySpaces.addParticipant("user-123-personal", {
id: "github-copilot",
type: "ai-tool",
joinedAt: Date.now(),
});

removeParticipant()​

Remove a single participant from a memory space.

Signature​

cortex.memorySpaces.removeParticipant(
memorySpaceId: string,
participantId: string
): Promise<MemorySpace>

Parameters​

ParameterTypeRequiredDefaultDescription
memorySpaceIdstringYes—The memory space ID
participantIdstringYes—ID of the participant to remove

Examples​

// User uninstalls tool
await cortex.memorySpaces.removeParticipant("user-123-personal", "old-tool");

// Note: Participant's existing memories remain (audit trail)
// But space no longer lists it as active participant
Info

Removing a participant does not delete their existing memories. The memories remain for audit trail purposes, but the space no longer lists them as an active participant.


updateParticipants()​

Add or remove multiple participants from a memory space (Hive Mode).

Signature​

cortex.memorySpaces.updateParticipants(
memorySpaceId: string,
updates: ParticipantUpdates
): Promise<MemorySpace>

Parameters​

ParameterTypeRequiredDefaultDescription
memorySpaceIdstringYes—The memory space ID
addArray<{ id: string; type: string; joinedAt: number }>No—Participants to add
removestring[]No—Participant IDs to remove

Examples​

// User installs new AI tools
await cortex.memorySpaces.updateParticipants("user-123-personal", {
add: [
{ id: "github-copilot", type: "ai-tool", joinedAt: Date.now() },
{ id: "windsurf", type: "ai-tool", joinedAt: Date.now() },
],
});

findByParticipant()​

Find all memory spaces where a participant is active.

Signature​

cortex.memorySpaces.findByParticipant(participantId: string): Promise<MemorySpace[]>

Parameters​

ParameterTypeRequiredDefaultDescription
participantIdstringYes—The participant ID to search for

Examples​

// Find all spaces where a specific user participates
const userSpaces = await cortex.memorySpaces.findByParticipant("user-123");

console.log(`User is active in ${userSpaces.length} memory spaces`);
userSpaces.forEach((space) => {
console.log(`- ${space.name} (${space.type})`);
});

archive()​

Mark memory space as archived (inactive). Data is preserved but space is hidden from default lists.

Signature​

cortex.memorySpaces.archive(memorySpaceId: string, options?: ArchiveOptions): Promise<MemorySpace>

Parameters​

ParameterTypeRequiredDefaultDescription
memorySpaceIdstringYes—The memory space ID to archive
reasonstringNo—Why the space is being archived
metadataRecord<string, unknown>No—Additional archive metadata

Examples​

// Project complete, archive its memory space
await cortex.memorySpaces.archive("project-apollo", {
reason: "Project completed successfully",
metadata: {
archivedBy: "admin-456",
completedAt: Date.now(),
},
});

// Space still exists, data intact, but marked inactive
const space = await cortex.memorySpaces.get("project-apollo");
console.log(space.status); // 'archived'

reactivate()​

Reactivate an archived memory space.

Signature​

cortex.memorySpaces.reactivate(memorySpaceId: string): Promise<MemorySpace>

Parameters​

ParameterTypeRequiredDefaultDescription
memorySpaceIdstringYes—The archived memory space ID to reactivate

Examples​

// User returns, reactivate their space
await cortex.memorySpaces.reactivate(`user-${userId}-personal`);

const space = await cortex.memorySpaces.get(`user-${userId}-personal`);
console.log(space.status); // 'active'

delete()​

Delete the exact memory-space registry and its supported exclusive primary data using an explicit administrator capability. The operation requires cascade:true, a reason and matching confirmId when supplied. Omitted tenant selects tenantless only.

const erasureTransport = cortex.memorySpaces.createErasureTransport({
url: process.env.CONVEX_URL!,
deploymentKey: process.env.CONVEX_DEPLOY_KEY!,
});
try {
const preview = await cortex.memorySpaces.delete("team-alpha", {
cascade: true,
reason: "Reviewed project retention cleanup",
confirmId: "team-alpha",
tenantId: "team-a",
erasureTransport,
dryRun: true,
verify: true,
});
console.log(preview.erasure);
} finally {
erasureTransport.close();
}

Cascade requires the separate fixed administrative erasure transport and an exact tenant selection. Omitted tenant selects tenantless data only. Its scope is exclusive supported primary records, with shared/foreign/operational/provenance residuals disclosed in result.erasure; complete is supported verified completion, while fullUserDataErased remains false. Partial/unknown errors carry a receipt: inspect that and persisted state before another attempt. No transaction-wide rollback or universal physical erasure is promised. A configured graph or selected managed graph jobs additionally require an explicit graph participant using the matching adapter and fixed createGraphErasureTransport({url, deploymentKey}); manual/legacy graph data is preserved. Ordinary AuthContext is not administrator authority.

The result retains compatibility counts in cascade, plus the detailed erasure receipt. deleted reflects whether the registry actually deleted. totalBytesKind is serializedDocumentEstimate, not physical disk space freed. Archiving is a separate non-destructive registry lifecycle operation; it does not erase data.

getStats()​

Get analytics and usage statistics for a memory space.

Signature​

cortex.memorySpaces.getStats(
memorySpaceId: string,
options?: GetMemorySpaceStatsOptions
): Promise<MemorySpaceStats>

Parameters​

ParameterTypeRequiredDefaultDescription
memorySpaceIdstringYes—The memory space ID
timeWindow"24h" | "7d" | "30d" | "90d" | "all"No"all"Time window for activity metrics
includeParticipantsbooleanNofalseInclude participant activity breakdown (Hive Mode)

Returns​

interface MemorySpaceStats {
memorySpaceId: string;

// Counts
totalMemories: number;
totalConversations: number;
totalFacts: number;
totalMessages: number;

// Activity (based on timeWindow)
memoriesThisWindow?: number;
conversationsThisWindow?: number;

// Storage
storage: {
conversationsBytes: number;
memoriesBytes: number;
factsBytes: number;
totalBytes: number;
};

// Performance
avgSearchTime?: string; // Average search latency (e.g., "12ms")

// Content Analysis
topTags: string[];
importanceBreakdown: {
critical: number; // 90-100
high: number; // 70-89
medium: number; // 40-69
low: number; // 10-39
trivial: number; // 0-9
};

// Participant Activity (if includeParticipants: true)
participants?: Array<{
participantId: string;
memoriesStored: number;
conversationsStored: number;
factsExtracted: number;
firstActive: number;
lastActive: number;
avgImportance: number;
topTags: string[];
}>;
}

Examples​

const stats = await cortex.memorySpaces.getStats("user-123-personal");

console.log(`Total memories: ${stats.totalMemories}`);
console.log(
`Storage used: ${(stats.storage.totalBytes / 1024 / 1024).toFixed(2)} MB`,
);
console.log(`Top tags: ${stats.topTags.join(", ")}`);

Common Patterns​

Pattern 1: Dynamic Space Creation (SaaS)​

// Create memory space per customer on signup
async function onUserSignup(userId: string) {
const memorySpaceId = `customer-${userId}-space`;

await cortex.memorySpaces.register({
memorySpaceId,
name: `Customer ${userId} Memory`,
type: "personal",
metadata: {
customerId: userId,
tier: "free",
signupDate: Date.now(),
},
});

return memorySpaceId;
}

Pattern 2: Team Workspace Management​

// Admin creates team workspace
async function createTeamWorkspace(
teamId: string,
teamName: string,
members: string[],
) {
const memorySpaceId = `team-${teamId}-workspace`;

await cortex.memorySpaces.register({
memorySpaceId,
name: `${teamName} Workspace`,
type: "team",
participants: members.map((m) => ({
id: `bot-${m}`,
type: "agent",
})),
metadata: {
teamId,
createdBy: "admin",
members,
},
});

return memorySpaceId;
}

// Add new member
async function addTeamMember(teamId: string, memberId: string) {
await cortex.memorySpaces.addParticipant(`team-${teamId}-workspace`, {
id: `bot-${memberId}`,
type: "agent",
joinedAt: Date.now(),
});
}

Pattern 3: Lifecycle Management​

const erasureTransport = cortex.users.createErasureTransport({
url: process.env.CONVEX_URL!,
deploymentKey: process.env.CONVEX_DEPLOY_KEY!,
});

// Complete lifecycle
async function memorySpaceLifecycle(projectId: string) {
const memorySpaceId = `project-${projectId}`;

// 1. Create
await cortex.memorySpaces.register({
memorySpaceId,
type: "project",
metadata: { status: "active" },
});

// 2. Use (implicit in app)
// await cortex.memory.remember({ memorySpaceId, ... });

// 3. Complete project - archive
await cortex.memorySpaces.archive(memorySpaceId, {
reason: "Project completed",
});

// 4. After retention period - delete
await cortex.memorySpaces.delete(memorySpaceId, {
cascade: true, erasureTransport, tenantId: "team-a",
reason: "Retention period (2 years) expired",
});
}

Pattern 4: Analytics Dashboard​

// Build memory space analytics dashboard
async function getMemorySpaceDashboard() {
const result = await cortex.memorySpaces.list({ status: "active" });

const dashboard = await Promise.all(
result.spaces.map(async (space) => {
const stats = await cortex.memorySpaces.getStats(space.memorySpaceId, {
timeWindow: "7d",
includeParticipants: true,
});

return {
name: space.name,
type: space.type,
participants: space.participants.length,
memoriesThisWeek: stats.memoriesThisWindow,
storage: stats.storage.totalBytes,
topParticipant: stats.participants?.[0]?.participantId,
};
}),
);

return dashboard;
}

// Usage
const dashboard = await getMemorySpaceDashboard();
console.table(dashboard);

Best Practices​

1. Use Structured IDs​

// Good: Structured, searchable
"user-{userId}-personal";
"team-{teamId}-workspace";
"project-{projectId}-memory";

// Avoid: Random, opaque
"abc123";
"temp";
"space1";

2. Always Register in Production​

// Development: Implicit is fine
await cortex.memory.remember({ memorySpaceId: "test-space", ... });

// Production: Explicit registration
await cortex.memorySpaces.register({
memorySpaceId: "user-123-personal",
name: "Alice's Personal Space",
type: "personal",
metadata: { environment: "production" },
});

3. Track Participants in Hive Mode​

// Good: Clear participant list with types
await cortex.memorySpaces.register({
memorySpaceId: "user-123-personal",
participants: [
{ id: "cursor", type: "ai-tool" },
{ id: "claude", type: "ai-tool" },
{ id: "notion-ai", type: "ai-tool" },
],
});

4. Use Metadata for Organization​

// Rich metadata for filtering and search
await cortex.memorySpaces.register({
memorySpaceId: "user-123-personal",
metadata: {
owner: "user-123",
environment: "production",
tier: "pro",
region: "us-west-2",
created: Date.now(),
tags: ["active", "premium"],
},
});

5. Archive Before Delete​

Tip

Always archive a memory space before deleting it. This provides a grace period for recovery and maintains data integrity during the retention period.

const erasureTransport = cortex.users.createErasureTransport({
url: process.env.CONVEX_URL!,
deploymentKey: process.env.CONVEX_DEPLOY_KEY!,
});

// Recommended: Archive first, delete later
await cortex.memorySpaces.archive(memorySpaceId, { reason: "Inactive" });

// Wait for retention period...

// Then delete
await cortex.memorySpaces.delete(memorySpaceId, {
cascade: true, erasureTransport, tenantId: "team-a",
reason: "Retention period expired",
});

// Avoid: Direct deletion without archival period

Error Handling​

Common Errors​

MEMORYSPACE_ALREADY_EXISTS:

try {
await cortex.memorySpaces.register({ memorySpaceId: "user-123-personal", type: "personal" });
} catch (error) {
if (error.message?.includes("MEMORYSPACE_ALREADY_EXISTS")) {
// Use update() instead
await cortex.memorySpaces.update("user-123-personal", { ... });
}
}

MEMORYSPACE_NOT_FOUND:

const space = await cortex.memorySpaces.get("nonexistent-space");

if (space === null) {
// Create it
await cortex.memorySpaces.register({
memorySpaceId: "nonexistent-space",
type: "personal",
});
}

CASCADE_REQUIRED (Delete Prevention):

const erasureTransport = cortex.users.createErasureTransport({
url: process.env.CONVEX_URL!,
deploymentKey: process.env.CONVEX_DEPLOY_KEY!,
});

try {
await cortex.memorySpaces.delete("active-space", {
cascade: false,
reason: "Test",
});
} catch (error) {
if (error instanceof Error && error.message.includes("CASCADE_REQUIRED")) {
console.error('Must set cascade: true, erasureTransport, tenantId: "team-a" to delete space with data');
}
}

CONFIRM_ID_MISMATCH:

const erasureTransport = cortex.users.createErasureTransport({
url: process.env.CONVEX_URL!,
deploymentKey: process.env.CONVEX_DEPLOY_KEY!,
});

try {
await cortex.memorySpaces.delete("space-123", {
cascade: true, erasureTransport, tenantId: "team-a",
reason: "Test",
confirmId: "wrong-id", // Doesn't match
});
} catch (error) {
if (error.message?.includes("CONFIRM_ID_MISMATCH")) {
console.error("confirmId must match memorySpaceId");
}
}

Conclusion​

The Memory Space Operations API provides explicit control over memory space lifecycle, participant management, and analytics.

Key Takeaways:

  • Registration is optional (implicit creation works)
  • Register in production for analytics and tracking
  • Use participants for Hive Mode tracking
  • Archive before delete (safer)
  • Monitor with getStats() for observability
Info

Most important: memorySpaceId is the fundamental isolation boundary. Everything else builds on this.


Questions? Ask in Discussions.