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
title: string;
content?: string; // Text content (if inline)
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
fileRef?: ArtifactFileRef; // Attached file reference

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

Artifact Kinds

KindDescriptionkindConfig Fields
textPlain text, markdown, prose-
codeSource code with highlightinglanguage
sheetTabular data (JSON array)columns
imageImage referencemimeType, dimensions
diagramMermaid, SVG, visualdiagramType
htmlHTML/React for previewframework
customUser-definedcustomName

Streaming States

StateDescriptionAllowed Transitions
draftInitial creation, editablestreaming
streamingReceiving content from AIpaused, final, error, draft
pausedTemporarily haltedstreaming, final, draft
finalCompleted and versioneddraft (for new edits)
errorError during streamingdraft

Core CRUD Operations

create()

Create a new artifact.

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

Parameters:

ParameterTypeRequiredDefaultDescription
memorySpaceIdstringYesMemory space for isolation
titlestringYesArtifact title
contentstringYesInitial content
kindArtifactKindNoContent type (default: 'text')
kindConfigArtifactKindConfigNoKind-specific settings
streamingStateStreamingStateNoInitial state (default: 'draft')
artifactIdstringNoCustom ID (auto-generated if omitted)
tenantIdstringNoTenant ID (from auth context)
userIdstringNoOwner user ID
agentIdstringNoOwner agent ID
participantIdstringNoHive Mode participant
descriptionstringNoBrief description
metadataRecord<string, unknown>NoCustom metadata
tagsstring[]NoTags for filtering
conversationRefobjectNoSource conversation reference
memoryRefsArtifactMemoryRef[]NoSource 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
artifactIdstringYesArtifact ID to update
contentstringYesNew content
options.titlestringNoNew title
options.metadataRecord<string, unknown>NoMetadata updates (merged)
options.tagsstring[]NoNew tags (replaces existing)
options.changeSummarystringNoDescription for version history
options.changedBystringNoUser/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
memorySpaceIdstringYesMemory space to search
tenantIdstringNoFilter by tenant
userIdstringNoFilter by user
kindArtifactKindNoFilter by kind
streamingStateStreamingStateNoFilter by state
tagsstring[]NoFilter by tags (any match)
tagMatch'any' | 'all'NoTag matching mode
titleContainsstringNoTitle search (case-insensitive)
createdAfternumberNoFilter by creation date
createdBeforenumberNoFilter by creation date
updatedAfternumberNoFilter by update date
updatedBeforenumberNoFilter by update date
limitnumberNoMax results (default: 50, max: 1000)
offsetnumberNoPagination offset
sortBy'createdAt' | 'updatedAt'NoSort field
sortOrder'asc' | 'desc'NoSort direction
includeDeletedbooleanNoInclude 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
limitnumberNoMax versions to return
offsetnumberNoPagination offset
sortOrder'asc' | 'desc'NoOrder 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
artifactIdstringYesArtifact ID
sessionIdstringYesSession ID from startStreaming
chunkstringYesContent 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
artifactIdstringYesArtifact ID
sessionIdstringYesSession ID
changeSummarystringNoSummary 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`);

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_FOUNDArtifact with given ID does not exist
ARTIFACT_ALREADY_EXISTSArtifact with given ID already exists
ARTIFACT_IS_DELETEDAttempting to modify a deleted artifact
INVALID_ARTIFACT_IDArtifact ID format is invalid
INVALID_ARTIFACT_KINDInvalid kind value
INVALID_STREAMING_STATEInvalid streaming state value
INVALID_STATE_TRANSITIONCannot transition between these states
UNDO_NOT_AVAILABLEAlready at version 1
REDO_NOT_AVAILABLEAlready at latest version
ARTIFACT_VERSION_NOT_FOUNDRequested version does not exist
ARTIFACT_CONTENT_TOO_LARGEContent exceeds size limit
STREAMING_SESSION_INVALIDSession ID is invalid or expired
ARTIFACT_QUOTA_EXCEEDEDTenant 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,
useArtifacts,
ARTIFACT_STREAM_EVENTS,
} from "@cortexmemory/vercel-ai-provider";

// 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