Skip to main content

Artifacts Operations API

Info

Added in v0.33.0 - Interactive, versioned document management for AI agents.

Complete API reference for the Artifacts API (cortex.artifacts.*), providing versioned document management with streaming support, undo/redo, and file attachments.

Overview​

The Artifacts API manages interactive documents that AI agents produce. Unlike memories (which store context for retrieval), artifacts store deliverables like code, documents, and visualizations.

Key Features:

  • Versioned Documents: Every update creates a version; full undo/redo support
  • Streaming Generation: Real-time content streaming with pause/resume
  • Multiple Kinds: Code, text, sheets, diagrams, images, HTML, custom
  • File Attachments: Binary file storage via Convex
  • Multi-Tenancy: Built-in tenant isolation
  • Graph Integration: Optional sync to Neo4j/Memgraph

Artifact Type​

interface Artifact {
_id: string; // Convex document ID
artifactId: string; // Unique artifact identifier (art-*)
memorySpaceId: string; // Memory space isolation
tenantId?: string; // Multi-tenancy isolation

// Core content (content and fileRef are mutually exclusive)
title: string;
content?: string; // Text content (undefined for file-based artifacts)
kind: ArtifactKind; // text | code | sheet | image | diagram | html | custom
kindConfig?: ArtifactKindConfig;
description?: string;
tags: string[];

// Version control
version: number; // Current version (1-based)
versionPointer: number; // Points to active version (for undo/redo)
versionHistory: ArtifactVersion[];

// Streaming
streamingState: StreamingState; // draft | streaming | paused | final | error
streamingMetadata?: StreamingMetadata;

// Ownership
userId?: string;
agentId?: string;
participantId?: string; // Hive Mode tracking

// File storage (content and fileRef are mutually exclusive)
fileRef?: ArtifactFileRef; // Attached file (content is undefined when set)

// References
conversationRef?: {
conversationId: string;
messageId?: string;
turnIndex?: number;
};
memoryRefs?: ArtifactMemoryRef[];

// Timestamps
createdAt: number;
updatedAt: number;
lastAccessedAt?: number;
accessCount?: number;

// Soft delete
isDeleted?: boolean;
deletedAt?: number;
deletedBy?: string;

// Custom data
metadata?: Record<string, unknown>;
}
Info

Content vs File Attachments: The content and fileRef fields are mutually exclusive. When you attach a file using uploadFile(), the artifact's content is set to undefined. Text-based artifacts store data in content, while file-based artifacts use fileRef.

Artifact Kinds​

KindDescriptionkindConfig Fields
`text`Plain text, markdown, prose-
`code`Source code with highlighting`language`
`sheet`Tabular data (JSON array)`columns`
`image`Image reference`mimeType`, `dimensions`
`diagram`Mermaid, SVG, visual`diagramType`
`html`HTML/React for preview`framework`
`custom`User-defined`customName`

Streaming States​

StateDescriptionAllowed Transitions
`draft`Initial creation, editable`streaming`
`streaming`Receiving content from AI`paused`, `final`, `error`, `draft`
`paused`Temporarily halted`streaming`, `final`, `draft`
`final`Completed and versioned`draft` (for new edits)
`error`Error during streaming`draft`

Core CRUD Operations​

create()​

Create a new artifact.

const artifact = await cortex.artifacts.create(options: CreateArtifactOptions): Promise<Artifact>;

Parameters:

ParameterTypeRequiredDefaultDescription
memorySpaceIdstringYes—Memory space for isolation
titlestringYes—Artifact title
contentstringYes—Initial content
kindArtifactKindNo—Content type (default: 'text')
kindConfigArtifactKindConfigNo—Kind-specific settings
streamingStateStreamingStateNo—Initial state (default: 'draft')
artifactIdstringNo—Custom ID (auto-generated if omitted)
tenantIdstringNo—Tenant ID (from auth context)
userIdstringNo—Owner user ID
agentIdstringNo—Owner agent ID
participantIdstringNo—Hive Mode participant
descriptionstringNo—Brief description
metadataRecord<string, unknown>No—Custom metadata
tagsstring[]No—Tags for filtering
conversationRefobjectNo—Source conversation reference
memoryRefsArtifactMemoryRef[]No—Source memory references
const artifact = await cortex.artifacts.create({
memorySpaceId: "user-123-workspace",
title: "API Handler",
content: `export async function handler(req, res) {
const data = await fetchData();
res.json(data);
}`,
kind: "code",
kindConfig: { language: "typescript" },
tags: ["api", "backend"],
description: "REST endpoint handler",
});

console.log(artifact.artifactId); // "art-abc123def456"
console.log(artifact.version); // 1

get()​

Retrieve an artifact by ID.

const artifact = await cortex.artifacts.get(artifactId: string): Promise<Artifact | null>;
const artifact = await cortex.artifacts.get("art-abc123def456");

if (artifact) {
console.log(artifact.title); // "API Handler"
console.log(artifact.content); // The code content
console.log(artifact.version); // Current version number
console.log(artifact.streamingState); // "draft" | "final" | ...
}

update()​

Update an artifact's content. Creates a new version in history.

const artifact = await cortex.artifacts.update(
artifactId: string,
content: string,
options?: UpdateArtifactOptions
): Promise<Artifact>;

Parameters:

ParameterTypeRequiredDefaultDescription
artifactIdstringYes—Artifact ID to update
contentstringYes—New content
options.titlestringNo—New title
options.metadataRecord<string, unknown>No—Metadata updates (merged)
options.tagsstring[]No—New tags (replaces existing)
options.changeSummarystringNo—Description for version history
options.changedBystringNo—User/agent who made the change
const updated = await cortex.artifacts.update(
"art-abc123def456",
`export async function handler(req, res) {
// Added error handling
try {
const data = await fetchData();
res.json(data);
} catch (error) {
res.status(500).json({ error: error.message });
}
}`,
{
changeSummary: "Added error handling",
changedBy: "user-123",
}
);

console.log(updated.version); // 2

delete()​

Delete an artifact. Supports soft delete (default) or hard delete.

const result = await cortex.artifacts.delete(
artifactId: string,
hard?: boolean // Default: false (soft delete)
): Promise<DeleteArtifactResult>;

Returns:

interface DeleteArtifactResult {
deleted: boolean;
artifactId: string;
versionsPurged?: number; // Hard delete only
filesDetached?: number; // Hard delete only
}
// Soft delete (recoverable)
const result = await cortex.artifacts.delete("art-abc123def456");
console.log(result.deleted); // true

// Hard delete (permanent)
const hardResult = await cortex.artifacts.delete("art-abc123def456", true);
console.log(hardResult.versionsPurged); // 5

list()​

List artifacts with filtering and pagination.

const artifacts = await cortex.artifacts.list(filter: ListArtifactsFilter): Promise<Artifact[]>;

Filter Parameters:

ParameterTypeRequiredDefaultDescription
memorySpaceIdstringYes—Memory space to search
tenantIdstringNo—Filter by tenant
userIdstringNo—Filter by user
kindArtifactKindNo—Filter by kind
streamingStateStreamingStateNo—Filter by state
tagsstring[]No—Filter by tags (any match)
tagMatch'any' | 'all'No—Tag matching mode
titleContainsstringNo—Title search (case-insensitive)
createdAfternumberNo—Filter by creation date
createdBeforenumberNo—Filter by creation date
updatedAfternumberNo—Filter by update date
updatedBeforenumberNo—Filter by update date
limitnumberNo—Max results (default: 50, max: 1000)
offsetnumberNo—Pagination offset
sortBy'createdAt' | 'updatedAt'No—Sort field
sortOrder'asc' | 'desc'No—Sort direction
includeDeletedbooleanNo—Include soft-deleted artifacts
const codeArtifacts = await cortex.artifacts.list({
memorySpaceId: "user-123-workspace",
kind: "code",
streamingState: "final",
tags: ["api"],
sortBy: "updatedAt",
sortOrder: "desc",
limit: 20,
});

console.log(`Found ${codeArtifacts.length} code artifacts`);

count()​

Count artifacts matching a filter.

const count = await cortex.artifacts.count(filter: CountArtifactsFilter): Promise<number>;
const count = await cortex.artifacts.count({
memorySpaceId: "user-123-workspace",
kind: "code",
streamingState: "final",
});

console.log(`${count} finalized code artifacts`);

Undo/Redo Operations​

undo()​

Undo the last change. Moves the version pointer backward without deleting history.

const result = await cortex.artifacts.undo(artifactId: string): Promise<{
success: boolean;
artifactId: string;
previousVersion: number;
currentVersion: number;
canUndo: boolean;
canRedo: boolean;
}>;
const result = await cortex.artifacts.undo("art-abc123def456");

console.log(result.previousVersion); // 3
console.log(result.currentVersion); // 2
console.log(result.canUndo); // true (can undo more)
console.log(result.canRedo); // true (can redo)
Error: UNDO_NOT_AVAILABLE

Thrown when at version 1 (no previous version exists).


redo()​

Redo a previously undone change. Moves the version pointer forward.

const result = await cortex.artifacts.redo(artifactId: string): Promise<{
success: boolean;
artifactId: string;
previousVersion: number;
currentVersion: number;
canUndo: boolean;
canRedo: boolean;
}>;
const result = await cortex.artifacts.redo("art-abc123def456");

console.log(result.currentVersion); // 3 (back to latest)
console.log(result.canRedo); // false (at latest)
Error: REDO_NOT_AVAILABLE

Thrown when already at the latest version.


getHistory()​

Get the version history of an artifact.

const history = await cortex.artifacts.getHistory(
artifactId: string,
options?: GetArtifactHistoryOptions
): Promise<ArtifactVersion[]>;

Options:

ParameterTypeRequiredDefaultDescription
limitnumberNo—Max versions to return
offsetnumberNo—Pagination offset
sortOrder'asc' | 'desc'No—Order by version (default: 'desc')
const history = await cortex.artifacts.getHistory("art-abc123def456", {
limit: 10,
sortOrder: "desc",
});

for (const version of history) {
console.log(`v${version.version}: ${version.changeSummary || "No note"}`);
console.log(` Changed by: ${version.changedBy || "unknown"}`);
console.log(` At: ${new Date(version.timestamp)}`);
}

getVersion()​

Get a specific version of an artifact.

const version = await cortex.artifacts.getVersion(
artifactId: string,
version: number
): Promise<ArtifactVersion | null>;
const v2 = await cortex.artifacts.getVersion("art-abc123def456", 2);

if (v2) {
console.log(v2.content); // Content at version 2
console.log(v2.timestamp); // When v2 was created
console.log(v2.changeSummary); // "Added error handling"
}

Streaming Operations​

startStreaming()​

Start a streaming session for an artifact. Transitions to streaming state.

const result = await cortex.artifacts.startStreaming(
params: StartStreamingParams
): Promise<{
success: boolean;
sessionId: string;
artifactId: string;
startedAt: number;
previousState: StreamingState;
currentState: StreamingState;
}>;
const { sessionId, artifactId } = await cortex.artifacts.startStreaming({
artifactId: "art-abc123def456",
});

console.log(`Streaming session: ${sessionId}`);
// Now append content chunks...

appendContent()​

Append a content chunk during streaming.

const result = await cortex.artifacts.appendContent(
params: AppendContentParams
): Promise<{
success: boolean;
artifactId: string;
sessionId: string;
chunkIndex?: number;
chunkBytes: number;
totalBytesReceived: number;
contentLength: number;
progress?: number;
timestamp: number;
}>;
ParameterTypeRequiredDefaultDescription
artifactIdstringYes—Artifact ID
sessionIdstringYes—Session ID from startStreaming
chunkstringYes—Content chunk to append
// Stream AI-generated content
for await (const chunk of aiStream) {
const result = await cortex.artifacts.appendContent({
artifactId: "art-abc123def456",
sessionId,
chunk: chunk.text,
});

console.log(`Progress: ${result.progress}%`);
}

pauseStreaming()​

Pause an active streaming session. Preserves current content.

const result = await cortex.artifacts.pauseStreaming(
params: StreamingSessionParams
): Promise<{
success: boolean;
artifactId: string;
sessionId: string;
pausedAt: number;
previousState: string;
currentState: string;
bytesReceived: number;
contentPreserved: boolean;
}>;
const paused = await cortex.artifacts.pauseStreaming({
artifactId: "art-abc123def456",
sessionId,
});

console.log(`Paused with ${paused.bytesReceived} bytes received`);

resumeStreaming()​

Resume a paused streaming session.

const result = await cortex.artifacts.resumeStreaming(
params: StreamingSessionParams
): Promise<{
success: boolean;
artifactId: string;
sessionId: string;
resumedAt: number;
previousState: string;
currentState: string;
bytesReceived: number;
}>;
const resumed = await cortex.artifacts.resumeStreaming({
artifactId: "art-abc123def456",
sessionId,
});

// Continue appending content...

cancelStreaming()​

Cancel an active streaming session. Returns to draft state.

const result = await cortex.artifacts.cancelStreaming(
params: StreamingSessionParams
): Promise<{
success: boolean;
artifactId: string;
sessionId: string;
cancelledAt: number;
previousState: StreamingState;
currentState: string;
contentPreserved: boolean;
bytesReceived: number;
contentLength: number;
}>;
const cancelled = await cortex.artifacts.cancelStreaming({
artifactId: "art-abc123def456",
sessionId,
});

console.log(`Cancelled. Content preserved: ${cancelled.contentPreserved}`);

finalizeStreaming()​

Complete a streaming session. Creates a new version and transitions to final.

const result = await cortex.artifacts.finalizeStreaming(
params: FinalizeStreamingParams
): Promise<{
success: boolean;
artifactId: string;
sessionId: string;
finalizedAt: number;
previousState: string;
currentState: string;
contentLength: number;
bytesReceived: number;
totalDurationMs: number;
versionCreated: boolean;
version: number;
}>;
ParameterTypeRequiredDefaultDescription
artifactIdstringYes—Artifact ID
sessionIdstringYes—Session ID
changeSummarystringNo—Summary for version history
const final = await cortex.artifacts.finalizeStreaming({
artifactId: "art-abc123def456",
sessionId,
changeSummary: "AI-generated implementation",
});

console.log(`Finalized as version ${final.version}`);
console.log(`Total duration: ${final.totalDurationMs}ms`);

Status Management​

setStreamingState()​

Directly set the streaming state. Use with caution - prefer streaming methods for proper transitions.

const result = await cortex.artifacts.setStreamingState(
artifactId: string,
streamingState: StreamingState
): Promise<{
success: boolean;
artifactId: string;
previousState: StreamingState;
currentState: StreamingState;
updatedAt: number;
}>;
// Mark as error state
await cortex.artifacts.setStreamingState("art-abc123def456", "error");

// Reset to draft for editing
await cortex.artifacts.setStreamingState("art-abc123def456", "draft");

File Operations​

uploadFile()​

Upload a file and attach it to an artifact.

const result = await cortex.artifacts.uploadFile(params: {
artifactId: string;
file: Blob;
filename: string;
mimeType: string;
metadata?: Record<string, unknown>;
}): Promise<{
success: boolean;
artifactId: string;
fileRef: {
storageId: string;
mimeType: string;
size: number;
checksum?: string;
originalFilename?: string;
};
version: number;
updatedAt: number;
}>;
const imageBlob = await fetchImageBlob();

const result = await cortex.artifacts.uploadFile({
artifactId: "art-abc123def456",
file: imageBlob,
filename: "diagram.png",
mimeType: "image/png",
});

console.log(`Uploaded: ${result.fileRef.storageId}`);
console.log(`Size: ${result.fileRef.size} bytes`);
Warning

Uploading a file sets the artifact's content to undefined. The content and fileRef fields are mutually exclusive—an artifact contains either inline text content or a file reference, not both.


getFileUrl()​

Get a signed URL for the artifact's attached file.

const url = await cortex.artifacts.getFileUrl(
artifactId: string
): Promise<string | null>;
const url = await cortex.artifacts.getFileUrl("art-abc123def456");

if (url) {
// Display image or download file
window.open(url);
}

detachFile()​

Detach the file from an artifact. Optionally delete from storage.

const result = await cortex.artifacts.detachFile(
artifactId: string,
deleteFile?: boolean // Default: false
): Promise<{
success: boolean;
artifactId: string;
previousFileRef: ArtifactFileRef;
fileDeleted: boolean;
version: number;
updatedAt: number;
}>;
// Detach but keep file in storage
const result = await cortex.artifacts.detachFile("art-abc123def456");

// Detach and delete file
const result = await cortex.artifacts.detachFile("art-abc123def456", true);
console.log(`File deleted: ${result.fileDeleted}`);

Error Codes​

CodeDescription
`ARTIFACT_NOT_FOUND`Artifact with given ID does not exist
`ARTIFACT_ALREADY_EXISTS`Artifact with given ID already exists
`ARTIFACT_IS_DELETED`Attempting to modify a deleted artifact
`INVALID_ARTIFACT_ID`Artifact ID format is invalid
`INVALID_ARTIFACT_KIND`Invalid kind value
`INVALID_STREAMING_STATE`Invalid streaming state value
`INVALID_STATE_TRANSITION`Cannot transition between these states
`UNDO_NOT_AVAILABLE`Already at version 1
`REDO_NOT_AVAILABLE`Already at latest version
`ARTIFACT_VERSION_NOT_FOUND`Requested version does not exist
`ARTIFACT_CONTENT_TOO_LARGE`Content exceeds size limit
`STREAMING_SESSION_INVALID`Session ID is invalid or expired
`ARTIFACT_QUOTA_EXCEEDED`Tenant artifact quota exceeded

Complete Streaming Example​

import { Cortex } from "@cortexmemory/sdk";

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

async function generateCodeArtifact(prompt: string, memorySpaceId: string) {
// 1. Create artifact in draft state
const artifact = await cortex.artifacts.create({
memorySpaceId,
title: "Generated Code",
content: "",
kind: "code",
kindConfig: { language: "typescript" },
streamingState: "draft",
});

// 2. Start streaming session
const { sessionId } = await cortex.artifacts.startStreaming({
artifactId: artifact.artifactId,
});

try {
// 3. Stream content from AI
const aiStream = await generateWithAI(prompt);

for await (const chunk of aiStream) {
await cortex.artifacts.appendContent({
artifactId: artifact.artifactId,
sessionId,
chunk: chunk.text,
});
}

// 4. Finalize successfully
const result = await cortex.artifacts.finalizeStreaming({
artifactId: artifact.artifactId,
sessionId,
changeSummary: `Generated from: ${prompt}`,
});

return await cortex.artifacts.get(artifact.artifactId);
} catch (error) {
// Cancel on error
await cortex.artifacts.cancelStreaming({
artifactId: artifact.artifactId,
sessionId,
});
throw error;
}
}

Vercel AI SDK Integration​

The Vercel AI Provider exports tools and hooks for artifact management:

import {
createArtifactTools,
ARTIFACT_STREAM_EVENTS,
} from "@cortexmemory/vercel-ai-provider";
import { useArtifacts } from "@cortexmemory/vercel-ai-provider/react";

// Create AI tools
const tools = createArtifactTools({
storage: cortex.artifacts,
writer: dataStreamWriter,
});

// React hook for state management
const { artifactList, activeArtifact, handleDataPart } = useArtifacts();

See Vercel AI SDK Integration for complete documentation.


Next Steps​