Skip to main content

Context Operations API

Info

Last Updated: 2026-01-09

Complete API reference for context chain management and multi-agent workflow coordination.

Overview​

The Context Operations API (cortex.contexts.*) provides methods for managing hierarchical workflows where agents collaborate on complex tasks. Context chains track task delegation, shared state, and workflow evolution across multiple agents.

Key Characteristics:

  • Hierarchical - Parent-child relationships for task decomposition
  • Shared - All agents in the workflow can access the chain
  • Versioned - Automatic status/data change tracking
  • Conversation-linked - Optional links to originating ACID conversations
  • GDPR-enabled - Optional userId for cascade deletion
  • Multi-Tenant - Automatic tenantId isolation for SaaS platforms (auto-injected from AuthContext)

Relationship to Layers:

Context chains are a separate coordination entity that can reference all layers:

Context API Layer Relationships
Layer 1: ACID Stores

conversations., immutable., mutable.* — Contexts reference via conversationRef

Layer 2: Vector Index

vector.* — Memories can reference contexts via metadata.contextId

Layer 3: Facts Store

facts.* — Facts can be linked to contexts for workflow knowledge

Coordination Layer

contexts.* — Workflow/task hierarchy (links to conversations, used by memories)

Use Cases:

  • Multi-agent task delegation
  • Approval workflows
  • Project/campaign coordination
  • Hierarchical knowledge sharing
  • Workflow state tracking
  • Task decomposition

Core Operations​

create()​

Create a new context (root or child).

Signature:

cortex.contexts.create(
params: CreateContextParams
): Promise<Context>
Info

v0.29.0+: Graph sync is now automatic when CORTEX_GRAPH_SYNC=true is set in your environment. The syncToGraph option has been removed from all APIs.

Parameters:

ParameterTypeRequiredDefaultDescription
purposestringYes—What this context is for
memorySpaceIdstringYes—Memory space creating this context
parentIdstringNo—Parent context (omit for root)
userIdstringNo—User this relates to (GDPR-enabled)
conversationRefobjectNo—Conversation link with conversationId and optional messageIds
dataRecord<string, unknown>No—Flexible key-value storage
statusstringNo'active'One of: 'active', 'completed', 'cancelled', 'blocked'
descriptionstringNo—Detailed description

Returns:

interface Context {
// Identity
_id: string; // Internal Convex ID
contextId: string; // User-facing context ID (e.g., "ctx-1234567890-abc123")
parentId?: string;
rootId: string; // Auto-computed (self if root)

// Purpose
purpose: string;
description?: string;

// Ownership
memorySpaceId: string; // Which memory space
userId?: string;

// Hierarchy
depth: number; // Auto-computed (0 = root)
childIds: string[]; // Direct children
participants: string[]; // All agents in this context

// Cross-space access grants (Collaboration Mode)
grantedAccess?: Array<{
memorySpaceId: string;
scope: string;
grantedAt: number;
}>;

// Conversation link
conversationRef?: {
conversationId: string;
messageIds?: string[];
};

// Data
data?: Record<string, unknown>;
metadata?: Record<string, unknown>;

// Status
status: "active" | "completed" | "cancelled" | "blocked";

// Timestamps (epoch milliseconds)
createdAt: number;
updatedAt: number;
completedAt?: number;

// Versioning (automatic)
version: number;
previousVersions?: ContextVersion[];
}

interface ContextVersion {
version: number;
status: string;
data?: Record<string, unknown>;
timestamp: number; // epoch milliseconds
updatedBy?: string; // Agent/memory space that made the change
}

Side Effects:

  • If parentId provided: Updates parent's childIds array
  • If parentId provided: Inherits rootId and increments depth
  • Adds memorySpaceId to participants list

Example 1: Create root context (linked to conversation)

// User makes request in conversation
const msg = await cortex.conversations.addMessage("conv-456", {
role: "user",
text: "I need a refund for $500",
userId: "user-123",
});

// Create root context for the workflow
const root = await cortex.contexts.create({
purpose: "Process customer refund request",
memorySpaceId: "supervisor-agent-space",
userId: "user-123", // GDPR-enabled
conversationRef: {
conversationId: "conv-456",
messageIds: [msg.id], // Links back to triggering message
},
data: {
importance: 85,
tags: ["refund", "customer-service", "ticket-456"],
amount: 500,
reason: "defective product",
ticketId: "TICKET-456",
},
});

console.log(root.contextId); // 'ctx-1234567890-abc123'
console.log(root.depth); // 0 (root)
console.log(root.rootId); // 'ctx-1234567890-abc123' (self)

Example 1b: Graph sync is automatic

// Graph sync happens automatically when CORTEX_GRAPH_SYNC=true
const root = await cortex.contexts.create({
purpose: "Process customer refund request",
memorySpaceId: "supervisor-agent-space",
userId: "user-123",
conversationRef: {
conversationId: "conv-456",
messageIds: [msg.id],
},
data: {
importance: 85,
tags: ["refund", "customer-service", "ticket-456"],
},
});

// Context is automatically synced to graph if CORTEX_GRAPH_SYNC=true
// Now queryable via graph for multi-hop queries

Example 2: Create child context (delegation)

// Delegate to finance agent
const child = await cortex.contexts.create({
purpose: "Approve and process $500 refund",
memorySpaceId: "finance-agent-space",
parentId: root.contextId, // Links to parent
userId: "user-123",
conversationRef: root.conversationRef, // Inherit conversation link
data: {
importance: 85,
tags: ["refund", "finance", "approval"],
amount: 500,
approvalRequired: true,
},
});

console.log(child.depth); // 1 (child of root)
console.log(child.rootId); // 'ctx-1234567890-abc123' (same as parent)
console.log(child.parentId); // root.contextId

Errors:

  • ContextsValidationError('MISSING_REQUIRED_FIELD') - Purpose or memorySpaceId is empty
  • ContextsValidationError('WHITESPACE_ONLY') - Purpose contains only whitespace
  • ContextsValidationError('INVALID_CONTEXT_ID_FORMAT') - parentId format is invalid
  • ConvexError('PARENT_NOT_FOUND') - Parent context doesn't exist (from backend)

See Also:


get()​

Retrieve a context by ID with optional chain traversal.

Signature:

cortex.contexts.get(
contextId: string,
options?: GetOptions
): Promise<Context | ContextWithChain | null>

Parameters:

ParameterTypeRequiredDefaultDescription
contextIdstringYes—The context ID to retrieve

Options:

ParameterTypeRequiredDefaultDescription
includeChainbooleanNofalseInclude parent/children/siblings
includeConversationbooleanNofalseFetch ACID conversation

Returns:

// Default (includeChain: false)
Context | null;

// With includeChain: true
interface ContextChain {
current: Context; // This context
parent?: Context; // Parent context
root: Context; // Root of the chain
children: Context[]; // Direct children
siblings: Context[]; // Other children of same parent
ancestors: Context[]; // All ancestors up to root
descendants: Context[]; // All recursive descendants
depth: number; // Current depth
totalNodes: number; // Total nodes in the chain
}

// With includeConversation: true (extends ContextChain)
interface ContextWithConversation extends ContextChain {
conversation?: Conversation; // ACID conversation
triggerMessages?: Message[]; // Messages that started this context
}

Example 1: Get context only

const ctx = await cortex.contexts.get("ctx-1234567890-abc123");

console.log(ctx.purpose);
console.log(ctx.status);
console.log(ctx.data);

Example 2: Get with full chain

const chain = await cortex.contexts.get("ctx-1234567890-child", {
includeChain: true,
});

console.log("Current:", chain.current.purpose);
console.log("Parent:", chain.parent.purpose);
console.log("Root:", chain.root.purpose);
console.log(
"Siblings:",
chain.siblings.map((s) => s.purpose),
);
console.log("Depth:", chain.depth);

Example 3: Get with conversation context

const enriched = await cortex.contexts.get("ctx-1234567890-abc123", {
includeChain: true,
includeConversation: true,
});

// Context data
console.log("Workflow:", enriched.current.purpose);

// Original conversation that triggered this workflow
if (enriched.conversation) {
console.log("Original request:", enriched.triggerMessages[0].content);
console.log(
"Full conversation:",
enriched.conversation.messages.length,
"messages",
);
}

Errors:

  • ContextsValidationError('MISSING_REQUIRED_FIELD') - contextId is empty
  • ContextsValidationError('INVALID_CONTEXT_ID_FORMAT') - contextId format is invalid
  • Returns null if context doesn't exist

update()​

Update a context (status, data, etc.). Creates new version automatically.

Signature:

cortex.contexts.update(
contextId: string,
updates: ContextUpdate
): Promise<Context>
Info

v0.29.0+: Graph sync is now automatic when CORTEX_GRAPH_SYNC=true is set in your environment. The syncToGraph option has been removed from all APIs.

Parameters:

ParameterTypeRequiredDefaultDescription
contextIdstringYes—The context ID to update
statusstringNo—One of: 'active', 'completed', 'cancelled', 'blocked'
dataRecord<string, unknown>No—Merges with existing data
descriptionstringNo—Updated description
completedAtnumberNo—Unix timestamp (ms) - auto-set when status='completed'

Returns:

  • Context - Updated context with incremented version

Side Effects:

  • Creates new version
  • Updates updatedAt timestamp
  • If status changes to 'completed', sets completedAt if not provided

Example:

// Update status (creates v2)
await cortex.contexts.update("ctx-1234567890-abc123", {
status: "completed",
data: {
result: "success",
completedBy: "finance-agent",
confirmationNumber: "REF-789",
},
});

// Update data only (creates v3)
await cortex.contexts.update("ctx-1234567890-abc123", {
data: {
notes: "Customer satisfied with resolution",
},
});

// View version history
const ctx = await cortex.contexts.get("ctx-1234567890-abc123");
console.log(`Current status: ${ctx.status} (v${ctx.version})`);
ctx.previousVersions.forEach((v) => {
console.log(`v${v.version}: status=${v.status}, timestamp=${v.timestamp}`);
});

Errors:

  • ContextsValidationError('MISSING_REQUIRED_FIELD') - contextId is empty
  • ContextsValidationError('INVALID_CONTEXT_ID_FORMAT') - contextId format is invalid
  • ContextsValidationError('INVALID_STATUS') - Status is not one of valid values
  • ContextsValidationError('INVALID_TYPE') - data is not an object
  • ConvexError('CONTEXT_NOT_FOUND') - Context doesn't exist (from backend)

See Also:


delete()​

Delete a context and optionally its descendants.

Signature:

cortex.contexts.delete(
contextId: string,
options?: DeleteContextOptions
): Promise<DeleteResult>

Parameters:

ParameterTypeRequiredDefaultDescription
contextIdstringYes—The context ID to delete

Options:

ParameterTypeRequiredDefaultDescription
cascadeChildrenbooleanNofalseDelete all descendants
orphanChildrenbooleanNofalseIf false and has children, error
Info

v0.29.0+: Graph deletion with orphan cleanup is now automatic when CORTEX_GRAPH_SYNC=true is set in your environment. The syncToGraph option has been removed from all APIs.

Returns:

interface DeleteResult {
deleted: boolean; // true if deletion succeeded
contextId: string;
descendantsDeleted: number; // Number of descendants deleted (0 if no cascade)
orphanedChildren?: string[]; // IDs of children promoted to roots (if orphanChildren: true)
}

Example:

// Delete single context (must have no children)
await cortex.contexts.delete("ctx-1234567890-abc123");

// Delete context and all descendants
const result = await cortex.contexts.delete("ctx-1234567890-root", {
cascadeChildren: true,
});

console.log(`Deleted: ${result.deleted}`); // true
console.log(`Descendants: ${result.descendantsDeleted}`);

// Allow orphaning (remove parent, promote children to roots)
const orphanResult = await cortex.contexts.delete("ctx-1234567890-parent", {
orphanChildren: true,
});
console.log("Orphaned children:", orphanResult.orphanedChildren);

Errors:

  • ContextsValidationError('MISSING_REQUIRED_FIELD') - contextId is empty
  • ContextsValidationError('INVALID_CONTEXT_ID_FORMAT') - contextId format is invalid
  • ConvexError('CONTEXT_NOT_FOUND') - Context doesn't exist (from backend)
  • ConvexError('HAS_CHILDREN') - Context has children and cascadeChildren=false (from backend)

Search contexts with filters. This method is an alias for list() with filter support.

Signature:

cortex.contexts.search(
filter?: ListContextsFilter
): Promise<Context[]>

Parameters:

ParameterTypeRequiredDefaultDescription
memorySpaceIdstringNo—Filter by memory space
userIdstringNo—Filter by user
statusstringNo—One of: 'active', 'completed', 'cancelled', 'blocked'
parentIdstringNo—Filter by parent context
rootIdstringNo—Filter by root context
depthnumberNo—Filter by hierarchy depth (0 = root)
limitnumberNo100Max results (max: 1000)

Returns:

  • Context[] - Array of matching contexts

Example:

// Find active contexts for an agent
const active = await cortex.contexts.search({
memorySpaceId: "finance-agent-space",
status: "active",
});

// Find contexts for a user
const userContexts = await cortex.contexts.search({
userId: "user-123",
});

// Find all root contexts
const roots = await cortex.contexts.search({
depth: 0,
});

Errors:

  • ContextsValidationError - Filters are malformed

See Also:


list()​

List contexts with filters.

Signature:

cortex.contexts.list(
filter?: ListContextsFilter
): Promise<Context[]>

Parameters:

ParameterTypeRequiredDefaultDescription
memorySpaceIdstringNo—Filter by memory space
userIdstringNo—Filter by user
statusstringNo—One of: 'active', 'completed', 'cancelled', 'blocked'
parentIdstringNo—Filter by parent context
rootIdstringNo—Filter by root context
depthnumberNo—Filter by hierarchy depth (0 = root)
limitnumberNo100Max results (max: 1000)

Returns:

  • Context[] - Array of matching contexts

Example:

// List all contexts (up to default limit)
const all = await cortex.contexts.list();

// List contexts for a memory space
const spaceContexts = await cortex.contexts.list({
memorySpaceId: "finance-agent-space",
limit: 50,
});

// List active root workflows
const activeRoots = await cortex.contexts.list({
status: "active",
depth: 0, // Root contexts only
});

Errors:

  • ContextsValidationError - Invalid filter values

count()​

Count contexts matching filters.

Signature:

cortex.contexts.count(
filter?: CountContextsFilter
): Promise<number>

Parameters:

ParameterTypeRequiredDefaultDescription
memorySpaceIdstringNo—Filter by memory space
userIdstringNo—Filter by user
statusstringNo—One of: 'active', 'completed', 'cancelled', 'blocked'

Example:

// Total contexts
const total = await cortex.contexts.count();

// Count by status
const active = await cortex.contexts.count({ status: "active" });
const completed = await cortex.contexts.count({ status: "completed" });

// Count for a memory space
const agentContexts = await cortex.contexts.count({
memorySpaceId: "finance-agent-space",
});

// Count active contexts for a user
const userActive = await cortex.contexts.count({
userId: "user-123",
status: "active",
});

updateMany()​

Bulk update contexts matching filters.

Signature:

cortex.contexts.updateMany(
filters: UpdateManyFilters,
updates: UpdateManyUpdates
): Promise<UpdateManyResult>

Filter Parameters:

ParameterTypeRequiredDefaultDescription
memorySpaceIdstringNo—Filter by memory space
userIdstringNo—Filter by user
statusstringNo—One of: 'active', 'completed', 'cancelled', 'blocked'
parentIdstringNo—Filter by parent context
rootIdstringNo—Filter by root context

Update Parameters:

ParameterTypeRequiredDefaultDescription
statusstringNo—New status value
dataRecord<string, unknown>No—Data to merge

Returns:

interface UpdateManyResult {
updated: number;
contextIds: string[];
}
Planned Features

The following options are planned for a future release:

  • dryRun?: boolean - Preview what would be updated without making changes
  • wouldUpdate?: number - Return field showing count for dryRun mode

Example:

// Archive completed contexts for a memory space
await cortex.contexts.updateMany(
{
memorySpaceId: "finance-agent-space",
status: "completed",
},
{
data: { archived: true },
},
);

// Boost priority for blocked contexts
await cortex.contexts.updateMany(
{
status: "blocked",
},
{
data: { importance: 95 },
},
);

deleteMany()​

Bulk delete contexts matching filters.

Signature:

cortex.contexts.deleteMany(
filters: DeleteManyFilters,
options?: DeleteManyOptions
): Promise<DeleteManyResult>

Filter Parameters:

ParameterTypeRequiredDefaultDescription
memorySpaceIdstringNo—Filter by memory space
userIdstringNo—Filter by user
statusstringNo—One of: 'active', 'completed', 'cancelled', 'blocked'
completedBeforenumberNo—Unix timestamp (ms)

Options:

ParameterTypeRequiredDefaultDescription
cascadeChildrenbooleanNofalseDelete descendants

Returns:

interface DeleteManyResult {
deleted: number;
contextIds: string[];
}
Planned Features

The following options are planned for a future release:

  • dryRun?: boolean - Preview what would be deleted without making changes
  • requireConfirmation?: boolean - Require explicit confirmation for large deletions
  • confirmationThreshold?: number - Auto-confirm if below this count
  • descendantsDeleted?: number - Return field for cascade deletion count
  • wouldDelete?: number - Return field for dryRun mode

Example:

// Delete old cancelled contexts (completed more than 90 days ago)
const result = await cortex.contexts.deleteMany({
status: "cancelled",
completedBefore: Date.now() - 90 * 24 * 60 * 60 * 1000,
});

console.log(`Deleted ${result.deleted} old contexts`);

// Delete all contexts in a memory space with cascade
await cortex.contexts.deleteMany(
{ memorySpaceId: "test-space" },
{ cascadeChildren: true },
);

export()​

Export contexts to JSON or CSV.

Signature:

cortex.contexts.export(
filters?: ExportFilters,
options?: ExportOptions
): Promise<ExportResult>

Filter Parameters:

ParameterTypeRequiredDefaultDescription
memorySpaceIdstringNo—Filter by memory space
userIdstringNo—Filter by user
statusstringNo—One of: 'active', 'completed', 'cancelled', 'blocked'

Options:

ParameterTypeRequiredDefaultDescription
formatstringYes—One of: 'json', 'csv'
includeChainbooleanNo—Include full hierarchy in JSON
includeVersionHistorybooleanNo—Include version history

Returns:

interface ExportResult {
format: string;
data: string; // JSON string or CSV content
count: number;
exportedAt: number; // Unix timestamp (ms)
}
Planned Features

The following options are planned for a future release:

  • outputPath?: string - Write directly to file (currently returns data string)
  • includeConversations?: boolean - Include linked ACID conversations

Example:

// Export user's workflows (GDPR compliance)
const result = await cortex.contexts.export(
{
userId: "user-123",
},
{
format: "json",
includeChain: true,
includeVersionHistory: true,
},
);

console.log(`Exported ${result.count} contexts`);
// Write to file manually if needed
fs.writeFileSync("exports/user-123-contexts.json", result.data);

Hierarchy Operations​

getChain()​

Get the complete context chain from a context ID.

Signature:

cortex.contexts.getChain(
contextId: string
): Promise<ContextChain>

Returns:

interface ContextChain {
current: Context; // This context
parent?: Context; // Parent context (if not root)
root: Context; // Root of the chain
children: Context[]; // Direct children
siblings: Context[]; // Other children of same parent
ancestors: Context[]; // All ancestors from root to parent
descendants: Context[]; // All recursive descendants
depth: number; // Current depth in hierarchy
totalNodes: number; // Total nodes in the chain
}

Example:

const chain = await cortex.contexts.getChain("ctx-1234567890-child");

console.log("Root:", chain.root.purpose);
console.log("Current:", chain.current.purpose);
console.log("Parent:", chain.parent.purpose);
console.log(
"Siblings:",
chain.siblings.map((s) => s.purpose),
);
console.log(
"Children:",
chain.children.map((c) => c.purpose),
);
console.log("Total workflow nodes:", chain.totalNodes);

getRoot()​

Get the root context of a chain.

Signature:

cortex.contexts.getRoot(
contextId: string
): Promise<Context>

Example:

// From any context in the chain, get the root
const root = await cortex.contexts.getRoot("ctx-1234567890-nested");

console.log("Original purpose:", root.purpose);
console.log("Started by:", root.memorySpaceId);
console.log("Conversation:", root.conversationRef?.conversationId);

getChildren()​

Get all direct children of a context.

Signature:

cortex.contexts.getChildren(
contextId: string,
options?: ChildrenOptions
): Promise<Context[]>

Parameters:

ParameterTypeRequiredDefaultDescription
contextIdstringYes—The parent context ID

Options:

ParameterTypeRequiredDefaultDescription
statusstringNo—Filter by status
recursivebooleanNofalseGet all descendants

Example:

// Get direct children
const children = await cortex.contexts.getChildren("ctx-1234567890-root");

// Get only active children
const activeChildren = await cortex.contexts.getChildren(
"ctx-1234567890-root",
{
status: "active",
},
);

// Get all descendants recursively
const allDescendants = await cortex.contexts.getChildren(
"ctx-1234567890-root",
{
recursive: true,
},
);

findOrphaned()​

Find contexts whose parent no longer exists.

Signature:

cortex.contexts.findOrphaned(): Promise<Context[]>

Example:

// Find orphaned contexts
const orphaned = await cortex.contexts.findOrphaned();

console.log(`Found ${orphaned.length} orphaned contexts`);

// Clean them up
for (const ctx of orphaned) {
await cortex.contexts.delete(ctx.contextId);
}

Advanced Operations​

addParticipant()​

Add an agent to a context's participant list.

Signature:

cortex.contexts.addParticipant(
contextId: string,
participantId: string
): Promise<Context>

Example:

// Add agent to existing workflow
await cortex.contexts.addParticipant(
"ctx-1234567890-abc123",
"legal-agent-space",
);

const ctx = await cortex.contexts.get("ctx-1234567890-abc123");
console.log("Participants:", ctx.participants);
// ['supervisor-agent-space', 'finance-agent-space', 'legal-agent-space']

grantAccess()​

Grant cross-space access to a context (Collaboration Mode). Allows agents from other memory spaces to access this context with specified permissions.

Signature:

cortex.contexts.grantAccess(
contextId: string,
targetMemorySpaceId: string,
scope: string
): Promise<Context>

Parameters:

ParameterTypeRequiredDefaultDescription
contextIdstringYes—The context to grant access to
targetMemorySpaceIdstringYes—Memory space to grant access to
scopestringYes—Access scope (e.g., 'read-only', 'context-only', 'full')

Returns:

  • Context - Updated context with new grantedAccess entry

Example:

// Grant read-only access to partner agent's memory space
await cortex.contexts.grantAccess(
"ctx-1234567890-abc123",
"partner-agent-space",
"read-only",
);

// Grant full context access for collaboration
await cortex.contexts.grantAccess(
"ctx-1234567890-abc123",
"collaborator-space",
"context-only",
);

// Check granted access
const ctx = await cortex.contexts.get("ctx-1234567890-abc123");
console.log("Granted access:", ctx.grantedAccess);
// [{ memorySpaceId: 'partner-agent-space', scope: 'read-only', grantedAt: 1699876543210 }]

Access Scopes:

ParameterTypeRequiredDefaultDescription
read-onlyscopeNo—Can read context data but not modify
context-onlyscopeNo—Can read/write context data but not child contexts
fullscopeNo—Full access including creating child contexts

Use Cases:

  • Multi-team workflows where different agents need varying access levels
  • Partner integrations with limited permissions
  • Temporary access grants for specific tasks

Errors:

  • ContextsValidationError('MISSING_REQUIRED_FIELD') - contextId, targetMemorySpaceId, or scope is empty
  • ContextsValidationError('INVALID_CONTEXT_ID_FORMAT') - contextId format is invalid
  • ConvexError('CONTEXT_NOT_FOUND') - Context doesn't exist (from backend)

removeParticipant()​

Remove an agent from a context's participant list.

Signature:

cortex.contexts.removeParticipant(
contextId: string,
participantId: string
): Promise<Context>

getByConversation()​

Get all contexts originating from a specific conversation.

Signature:

cortex.contexts.getByConversation(
conversationId: string
): Promise<Context[]>

Example:

// Find all workflows triggered by this conversation
const contexts = await cortex.contexts.getByConversation("conv-456");

console.log(`Conversation spawned ${contexts.length} workflows`);
contexts.forEach((ctx) => {
console.log(`- ${ctx.purpose} (${ctx.status})`);
});

Version Operations​

getVersion()​

Get a specific version of a context.

Signature:

cortex.contexts.getVersion(
contextId: string,
version: number
): Promise<ContextVersion | null>

Returns:

interface ContextVersion {
version: number;
status: string;
data?: Record<string, unknown>;
timestamp: number; // epoch milliseconds
updatedBy?: string;
}

Example:

// Get version 1 (original state)
const v1 = await cortex.contexts.getVersion("ctx-1234567890-abc123", 1);

console.log(`v1 status: ${v1.status}`);
console.log(`v1 data:`, v1.data);

getHistory()​

Get all versions of a context.

Signature:

cortex.contexts.getHistory(
contextId: string
): Promise<ContextVersion[]>

Example:

const history = await cortex.contexts.getHistory("ctx-1234567890-abc123");

console.log(`Context has ${history.length} versions:`);
history.forEach((v) => {
console.log(`v${v.version} (${v.timestamp}): status=${v.status}`);
console.log(` Updated by: ${v.updatedBy}`);
});

getAtTimestamp()​

Get context state at a specific point in time.

Signature:

cortex.contexts.getAtTimestamp(
contextId: string,
timestamp: Date
): Promise<ContextVersion | null>

Parameters:

ParameterTypeRequiredDefaultDescription
contextIdstringYes—The context ID
timestampDateYes—The point in time to query

Returns:

interface ContextVersion {
version: number;
status: string;
data?: Record<string, unknown>;
timestamp: number; // epoch milliseconds
updatedBy?: string;
}

Example:

// What was the status on October 20th?
const historical = await cortex.contexts.getAtTimestamp(
"ctx-1234567890-abc123",
new Date("2025-10-20T10:00:00Z"),
);

if (historical) {
console.log(`Status on Oct 20: ${historical.status}`);
console.log(`Version: ${historical.version}`);
}

Filter Reference​

Currently Available Filters​

The following filters are implemented and available across context operations:

// ListContextsFilter - used by list(), search()
interface ListContextsFilter {
memorySpaceId?: string; // Filter by memory space
userId?: string; // Filter by user
status?: "active" | "completed" | "cancelled" | "blocked";
parentId?: string; // Filter by parent context
rootId?: string; // Filter by root context
depth?: number; // Filter by hierarchy depth (0 = root)
limit?: number; // Default: 100, max: 1000
}

// CountContextsFilter - used by count()
interface CountContextsFilter {
memorySpaceId?: string;
userId?: string;
status?: "active" | "completed" | "cancelled" | "blocked";
}

// UpdateManyFilters - used by updateMany()
interface UpdateManyFilters {
memorySpaceId?: string;
userId?: string;
status?: "active" | "completed" | "cancelled" | "blocked";
parentId?: string;
rootId?: string;
}

// DeleteManyFilters - used by deleteMany()
interface DeleteManyFilters {
memorySpaceId?: string;
userId?: string;
status?: "active" | "completed" | "cancelled" | "blocked";
completedBefore?: number; // Unix timestamp (ms)
}

// ExportFilters - used by export()
interface ExportFilters {
memorySpaceId?: string;
userId?: string;
status?: "active" | "completed" | "cancelled" | "blocked";
}

Planned Filter Enhancements​

Planned Features

The following filters are planned for a future release to provide more powerful querying:

// Planned additions to ContextFilters
interface PlannedContextFilters {
// Text search
purposeContains?: string; // Search by purpose text

// Data field queries
"data.importance"?: number | RangeQuery;
"data.tags"?: string[];
data?: Record<string, unknown>;

// Conversation link
"conversationRef.conversationId"?: string;

// Date ranges
createdBefore?: Date;
createdAfter?: Date;
updatedBefore?: Date;
updatedAfter?: Date;
completedAfter?: Date;

// Version filtering
version?: number | RangeQuery;
}

// Range query syntax (planned)
interface RangeQuery {
$gte?: number;
$lte?: number;
$gt?: number;
$lt?: number;
}

Operations and their filter support:

ParameterTypeRequiredDefaultDescription
list()operationNo—memorySpaceId, userId, status, parentId, rootId, depth, limit
search()operationNo—memorySpaceId, userId, status, parentId, rootId, depth, limit
count()operationNo—memorySpaceId, userId, status
updateMany()operationNo—memorySpaceId, userId, status, parentId, rootId
deleteMany()operationNo—memorySpaceId, userId, status, completedBefore
export()operationNo—memorySpaceId, userId, status

Real-World Patterns​

// User request in conversation
const msg = await cortex.conversations.addMessage("conv-789", {
role: "user",
text: "I need the Q4 financial report by Friday",
userId: "user-cfo",
});

// Create root context linked to conversation
const root = await cortex.contexts.create({
purpose: "Build quarterly financial report",
memorySpaceId: "ceo-agent-space",
userId: "user-cfo",
conversationRef: {
conversationId: "conv-789",
messageIds: [msg.id],
},
data: {
quarter: "Q4",
year: 2025,
deadline: new Date("2025-10-30"),
importance: 90,
tags: ["financial", "report", "q4"],
},
});

// Decompose into subtasks (all inherit conversation link)
await cortex.contexts.create({
purpose: "Gather revenue data",
memorySpaceId: "finance-agent-space",
parentId: root.contextId,
userId: "user-cfo",
conversationRef: root.conversationRef, // Same source
});

await cortex.contexts.create({
purpose: "Compile expense reports",
memorySpaceId: "accounting-agent-space",
parentId: root.contextId,
userId: "user-cfo",
conversationRef: root.conversationRef,
});

// All agents can trace back to original user request
const chain = await cortex.contexts.get("ctx-1234567890-finance", {
includeChain: true,
includeConversation: true,
});

console.log("Original request:", chain.triggerMessages[0].content);

Pattern 2: Workflow Status Tracking​

// Check workflow completion
async function checkWorkflowComplete(rootContextId: string) {
const chain = await cortex.contexts.getChain(rootContextId);

// Check if all children completed
const allComplete = chain.children.every((c) => c.status === "completed");

if (allComplete && chain.current.status !== "completed") {
// Mark root as complete
await cortex.contexts.update(rootContextId, {
status: "completed",
completedAt: new Date(),
});

return true;
}

return false;
}

Pattern 3: Context-Based Memory Storage​

Link memories to contexts for workflow knowledge:

// Store memory with context reference using Layer 4 API (recommended)
await cortex.memory.remember({
memorySpaceId: "finance-agent-space",
userId: "user-123",
agentId: "finance-agent",
userMessage: "Please process the refund",
agentResponse: "Approved $500 refund for defective product",
metadata: {
importance: 85,
tags: ["refund", "approval", "finance"],
contextId: "ctx-1234567890-refund", // Link to context!
},
});

// Later: Search memories (contextId tracking is application-level)
const results = await cortex.memory.recall({
memorySpaceId: "finance-agent-space",
userId: "user-123",
query: "refund approval",
});
Note

Context tracking in memories is an application-level pattern. Store the contextId in memory metadata and filter/search as needed.

Pattern 4: Multi-Agent Coordination​

// Supervisor creates workflow
const workflow = await cortex.contexts.create({
purpose: "Launch marketing campaign",
memorySpaceId: "supervisor-agent-space",
data: {
campaignName: "Q4 Sale",
importance: 80,
tags: ["marketing", "campaign", "q4"],
},
});

// Marketing agent checks in
await cortex.contexts.addParticipant(
workflow.contextId,
"marketing-agent-space",
);

// Finance agent checks in
await cortex.contexts.addParticipant(workflow.contextId, "finance-agent-space");

// All agents can see who's involved
const ctx = await cortex.contexts.get(workflow.contextId);
console.log("Team:", ctx.participants);
// ['supervisor-agent-space', 'marketing-agent-space', 'finance-agent-space']

Best Practices​

1. Descriptive Purposes​

// Avoid: Vague
await cortex.contexts.create({
purpose: "Process request",
});

// Better: Specific
await cortex.contexts.create({
purpose: "Process $500 refund for defective product (ticket #456)",
data: {
amount: 500,
ticketId: "TICKET-456",
},
});
// Always link when context comes from user conversation
await cortex.contexts.create({
purpose: "Handle support ticket",
conversationRef: {
conversationId: "conv-456",
messageIds: ["msg-initial-request"],
},
});

// Benefits:
// - Audit trail back to user request
// - Can retrieve full conversation context
// - Compliance and debugging

3. Use Importance for Prioritization​

// Set importance (0-100)
await cortex.contexts.create({
purpose: "Critical security issue",
data: { importance: 100 }, // Maximum priority
});

// Find active contexts and filter by importance (application-level)
const active = await cortex.contexts.search({ status: "active" });
const urgent = active.filter((ctx) => (ctx.data?.importance ?? 0) >= 90);

4. Update Status Promptly​

// When work starts
await cortex.contexts.update(contextId, {
status: "active",
data: { startedAt: new Date() },
});

// When blocked
await cortex.contexts.update(contextId, {
status: "blocked",
data: { blockedReason: "Waiting for API access" },
});

// When complete
await cortex.contexts.update(contextId, {
status: "completed",
completedAt: new Date(),
});

5. Clean Up Old Contexts​

// Archive completed contexts (filter by status, then update)
await cortex.contexts.updateMany(
{ status: "completed" },
{ data: { archived: true } },
);

// Delete old cancelled contexts (completedBefore supported by deleteMany)
await cortex.contexts.deleteMany({
status: "cancelled",
completedBefore: Date.now() - 180 * 24 * 60 * 60 * 1000,
});

Graph-Lite Capabilities​

Contexts form a hierarchical graph (tree structure) for workflow coordination:

Context as Graph Node:

  • Represents a workflow task or sub-task
  • Part of directed acyclic graph (DAG) structure

Edges:

  • parentId → Parent context (upward edge)
  • childIds → Child contexts (downward edges)
  • conversationRef → Originating conversation (source edge)
  • userId → Related user
  • memorySpaceId → Owning memory space

Graph Operations:

// Tree traversal (built-in)
const chain = await cortex.contexts.get(contextId, {
includeChain: true // ← Multi-hop graph walk
});

// Nodes accessed in this graph query:
chain.current // This node
chain.parent // 1-hop up
chain.root // N-hops to root
chain.ancestors // All nodes on path to root
chain.children // 1-hop down
chain.siblings // Lateral (parent's other children)
chain.descendants // All nodes below (recursive)

// Example graph structure:
Root (depth=0)
├── Child-1 (depth=1)
│ ├── Grandchild-1 (depth=2)
│ └── Grandchild-2 (depth=2)
├── Child-2 (depth=1)
└── Child-3 (depth=1)
└── Grandchild-3 (depth=2)

Graph Queries:

// Find all root workflows (depth=0 nodes)
const roots = await cortex.contexts.search({ depth: 0 });

// Find all child nodes of a parent
const children = await cortex.contexts.search({ parentId: "ctx-001" });

// Find entire workflow tree (all nodes with same rootId)
const workflowTree = await cortex.contexts.search({ rootId: "ctx-root" });

// Find workflows triggered by conversation (traverse conversationRef edge)
const workflowsFromConvo = await cortex.contexts.search({
"conversationRef.conversationId": "conv-456",
});

Performance:

  • Context hierarchy traversal: 50-150ms for typical depth (1-5 levels)
  • Entire workflow tree: 100-300ms for <50 contexts
  • Deep hierarchies (>10 levels): Consider graph database for <100ms queries

Learn more: Graph Capabilities


Error Reference​

Client-Side Validation Errors (ContextsValidationError)​

These errors are thrown by the SDK before the request reaches the backend:

ParameterTypeRequiredDefaultDescription
MISSING_REQUIRED_FIELDerrorNo—Required field is missing (null, undefined, or empty string)
WHITESPACE_ONLYerrorNo—Field contains only whitespace (purpose has no actual content)
INVALID_CONTEXT_ID_FORMATerrorNo—Context ID format is invalid (must match ctx-{timestamp}-{random})
INVALID_CONVERSATION_ID_FORMATerrorNo—Conversation ID format invalid (must start with conv-)
INVALID_STATUSerrorNo—Status value is invalid (not one of: active/completed/cancelled/blocked)
INVALID_FORMATerrorNo—Export format is invalid (not one of: json/csv)
INVALID_RANGEerrorNo—Numeric value out of range (depth < 0, limit <= 0 or > 1000, version < 1)
INVALID_TYPEerrorNo—Type is incorrect (data is not an object, etc.)
INVALID_DATEerrorNo—Date is invalid (not a valid Date object)
EMPTY_UPDATESerrorNo—Updates object is empty (must include at least one field to update)
EMPTY_FILTERSerrorNo—Filters object is empty (must include at least one filter)
EMPTY_ARRAYerrorNo—Array is empty (required array has no elements)

Backend Errors (ConvexError)​

These errors are returned from the Convex backend:

ParameterTypeRequiredDefaultDescription
CONTEXT_NOT_FOUNDerrorNo—Context doesn't exist (invalid or non-existent contextId)
PARENT_NOT_FOUNDerrorNo—Parent context doesn't exist (invalid parentId in create())
HAS_CHILDRENerrorNo—Context has children (can't delete without cascade/orphan)

See Also:


Graph Integration​

Context chains integrate with graph databases for advanced queries:

// Create context (graph sync is automatic when CORTEX_GRAPH_SYNC=true)
await cortex.contexts.create(params);

// Query via graph for multi-hop traversal
const hierarchy = await graphAdapter.query(
`
MATCH (root:Context {contextId: $contextId})
MATCH path = (root)<-[:CHILD_OF*0..10]-(descendants:Context)
RETURN descendants
ORDER BY descendants.depth
`,
{ contextId: root.contextId },
);

// Result: Entire hierarchy in single query (<10ms)!
Performance

Graph integration provides 3.8x faster queries for deep hierarchies (7+ levels).

See Graph Operations API for complete graph integration reference.


Next Steps​


Questions? Ask in GitHub Discussions.