Skip to main content

Getting Started with Cortex Memory for Vercel AI SDK

Info
Last Updated: 2026-01-08

Complete guide to adding persistent memory to your Next.js application using Cortex Memory SDK/provider 0.38.0.

Prerequisites​

  • Node.js 24.15.0+ for CLI setup; provider runtime follows its package declaration
  • Next.js 14+ (App Router)
  • Convex account (free tier available)
  • OpenAI API key (or other LLM provider)

Option 1: Run the quickstart demo​

cortex templates init demo --template vercel-ai-quickstart --template-version 1.2.0 --catalog-version 1.1.0 --yes --graph none
cd demo
cp .env.local.example .env.local
npm run convex:dev
npm run dev

Before starting, configure CONVEX_URL, server-only CORTEX_APP_DEPLOYMENT_ADMIN_KEY, CORTEX_APP_SETUP_TOKEN, OPENAI_API_KEY and exact CORTEX_APP_ORIGIN=http://localhost:3000. Obtain the deployment key from your own composed backend setup. Use a separate private first-setup token in the initial setup form. Accounts/sessions live in private app tables; the server resolves allowed tenant/spaces from the opaque HttpOnly eight-hour session, not browser-supplied IDs. Open the same localhost host/port. Follow the canonical quickstart README for private setup, independent template/SDK updates and optional shared graph lifecycle.

Option 2: Add to Existing Project​

The provider is server-side infrastructure. Derive user, tenant and allowed memory space from your verified application session/ownership layer before model or data calls; request IDs and SDK AuthContext are scope selections, not verification. A recognized private Next backend additionally needs the second server-only {deploymentKey} factory argument to initialize native transport. Never expose this key or replace private calls with public-reference casts.

Step 1: Install Dependencies​

Terminal
$ 

npm install @cortexmemory/vercel-ai-provider @cortexmemory/sdk ai convex npm install @ai-sdk/openai

You can also use @ai-sdk/anthropic, @ai-sdk/google, or other providers.

Step 2: Set Up Convex Backend​

Terminal
$ 

npx @cortexmemory/cli init

Follow the wizard to set up Convex with Cortex backend automatically.

Step 2.5: Deploy Convex Schema​

After setting up Convex, deploy the schema:

Terminal
$ 

npm run convex:dev

This initializes the Convex backend and is required before running your app.

Step 3: Configure Environment Variables​

Create .env.local:

# Required
CONVEX_URL=https://your-deployment.convex.cloud
NEXT_PUBLIC_CONVEX_URL=https://your-deployment.convex.cloud
OPENAI_API_KEY=sk-your-key-here

# Optional: Memory Space
MEMORY_SPACE_ID=my-chatbot

# Optional: Enable fact extraction (SDK v0.18.0+)
CORTEX_FACT_EXTRACTION=true
CORTEX_FACT_EXTRACTION_MODEL=gpt-4o-mini

# Optional: Enable graph sync (SDK v0.19.0+)
# CORTEX_GRAPH_SYNC=true
# NEO4J_URI=bolt://localhost:7687
# NEO4J_USERNAME=neo4j
# NEO4J_PASSWORD=your-password

Step 4: Create Memory-Enabled Chat API​

// app/api/chat/route.ts
import { createCortexMemoryAsync } from "@cortexmemory/vercel-ai-provider";
import { openai, createOpenAI } from "@ai-sdk/openai";
import {
streamText,
embed,
convertToModelMessages,
createUIMessageStream,
createUIMessageStreamResponse,
} from "ai";

// Create OpenAI client for embeddings
const openaiClient = createOpenAI({ apiKey: process.env.OPENAI_API_KEY });

export async function POST(req: Request) {
const { messages, conversationId: providedConversationId } = await req.json();

// Generate conversation ID if not provided (new chat)
const conversationId =
providedConversationId ||
`conv-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;

// Normalize messages for AI SDK 7 compatibility
// This ensures messages have the `parts` array format expected by convertToModelMessages
const normalizedMessages = messages.map((msg: any) => {
const role = msg.role === "agent" ? "assistant" : msg.role;
const parts = msg.parts || (msg.content ? [{ type: "text", text: msg.content }] : []);
return { ...msg, role, parts };
});

// Convert UIMessage[] to ModelMessage[] for streamText
const modelMessagesResult = convertToModelMessages(normalizedMessages);
const modelMessages =
modelMessagesResult instanceof Promise
? await modelMessagesResult
: modelMessagesResult;

// Create memory-augmented model with async initialization
// Use createCortexMemoryAsync for graph memory support
const cortexMemory = await createCortexMemoryAsync({
convexUrl: process.env.CONVEX_URL!,
memorySpaceId: process.env.MEMORY_SPACE_ID || "default-chat",

// User identification
userId: "demo-user", // Replace with real user ID
userName: "User",

// REQUIRED in SDK v0.17.0+
agentId: "my-assistant",
agentName: "My AI Assistant",

// Conversation ID for chat history isolation
conversationId,

// Embedding provider (required for semantic deduplication)
embeddingProvider: {
generate: async (text: string) => {
const result = await embed({
model: openaiClient.embedding("text-embedding-3-small"),
value: text,
});
return result.embedding;
},
},

// Optional: Enable enhanced features
enableFactExtraction: process.env.CORTEX_FACT_EXTRACTION === "true",
enableGraphMemory: process.env.CORTEX_GRAPH_SYNC === "true",
});

// Stream response with memory integration
const result = await streamText({
model: cortexMemory(openai("gpt-4o-mini")),
messages: modelMessages,
system: `You are a helpful assistant with long-term memory.
You remember everything users tell you and can recall it naturally.`,
});

// Use createUIMessageStreamResponse for advanced streaming features
return createUIMessageStreamResponse({
stream: createUIMessageStream({
execute: async ({ writer }) => {
writer.merge(result.toUIMessageStream());
},
}),
});
}

Step 5: Create Chat UI​

// app/page.tsx
'use client';
import { useChat } from 'ai/react';

export default function ChatPage() {
const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat();

return (
<div className="p-4 max-w-2xl mx-auto">
<h1 className="text-2xl mb-4">Chat with Memory</h1>

<div className="space-y-4 mb-4">
{messages.map(m => (
<div
key={m.id}
className={`p-3 rounded ${
m.role === 'user' ? 'bg-blue-100' : 'bg-gray-100'
}`}
>
<strong>{m.role === 'user' ? 'You' : 'Assistant'}:</strong> {m.content}
</div>
))}

{isLoading && (
<div className="p-3 rounded bg-gray-100 animate-pulse">
Thinking...
</div>
)}
</div>

<form onSubmit={handleSubmit} className="flex gap-2">
<input
value={input}
onChange={handleInputChange}
placeholder="Type a message..."
className="flex-1 p-2 border rounded"
disabled={isLoading}
/>
<button
type="submit"
className="px-4 py-2 bg-blue-500 text-white rounded disabled:opacity-50"
disabled={isLoading}
>
Send
</button>
</form>
</div>
);
}

Step 6: Test Memory​

  1. Start your app:
Terminal
$ 

npm run dev

  1. Open http://localhost:3000

  2. Test memory persistence:

    • Say: "Hi, my name is Alice and I work at Acme Corp"
    • Agent: "Nice to meet you, Alice! Acme Corp sounds great."
    • Refresh the page
    • Ask: "What do you know about me?"
    • Agent: "I remember you're Alice and you work at Acme Corp!" ✨

Configuration Options​

Required Options​

ParameterTypeRequiredDefaultDescription
convexUrlstringYes—Your Convex deployment URL
memorySpaceIdstringYes—Namespace for memory isolation
userIdstringYes—User identifier
agentIdstringYes—Agent identifier (required in v0.17.0+)
conversationIdstringYes—Conversation identifier for chat isolation
embeddingProviderobjectYes—Embedding provider for semantic deduplication (required for belief revision)

Optional Options​

ParameterTypeRequiredDefaultDescription
userNamestringNo—User display name. Default: "User"
agentNamestringNo—Agent display name. Default: agentId
enableFactExtractionbooleanNo—Enable LLM fact extraction. Default: false
enableGraphMemorybooleanNo—Sync to graph database. Default: false
memorySearchLimitnumberNo—Max memories to retrieve. Default: 20
minMemoryRelevancenumberNo—Min similarity score (0-1). Default: 0.7
debugbooleanNo—Enable debug logging. Default: false
Info

Use createCortexMemoryAsync (with await) instead of createCortexMemory when you need graph memory support or want automatic configuration from environment variables.

Dynamic User Resolution​

For authentication systems:

import { auth } from "@clerk/nextjs";
import { createCortexMemoryAsync } from "@cortexmemory/vercel-ai-provider";

export async function POST(req: Request) {
const { userId } = await auth();

if (!userId) {
return new Response("Unauthorized", { status: 401 });
}

const cortexMemory = await createCortexMemoryAsync({
convexUrl: process.env.CONVEX_URL!,
memorySpaceId: "app",
userId,
userName: "User",
agentId: "app-assistant",
conversationId: `conv-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
});

// ... use cortexMemory
}

Multi-Tenant Setup​

Different memory space per team/tenant:

import { createCortexMemoryAsync } from "@cortexmemory/vercel-ai-provider";
import { openai } from "@ai-sdk/openai";
import { streamText } from "ai";

export async function POST(req: Request) {
const { teamId, messages, conversationId } = await req.json();

const cortexMemory = await createCortexMemoryAsync({
convexUrl: process.env.CONVEX_URL!,
memorySpaceId: `team-${teamId}`, // Isolated per team
userId: currentUser.id,
userName: currentUser.name,
agentId: "team-assistant",
conversationId: conversationId || `conv-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
});

const result = await streamText({
model: cortexMemory(openai("gpt-4o-mini")),
messages,
});

return result.toDataStreamResponse();
}

Deploy to Vercel​

Terminal
$ 

vercel

Set environment variables in Vercel dashboard:

  • CONVEX_URL
  • NEXT_PUBLIC_CONVEX_URL
  • OPENAI_API_KEY
  • MEMORY_SPACE_ID (optional)
  • CORTEX_FACT_EXTRACTION (optional)
  • CORTEX_GRAPH_SYNC (optional)

Troubleshooting​

"agentId is required"​

Add agentId to your configuration (required in SDK v0.17.0+):

const cortexMemory = await createCortexMemoryAsync({
// ... other config
agentId: "my-assistant", // Add this!
});

AI SDK v6 Message Format​

If you encounter issues with message formatting, ensure you normalize messages for AI SDK 7 compatibility:

// Normalize messages to ensure they have the `parts` array format
const normalizedMessages = messages.map((msg: any) => {
const role = msg.role === "agent" ? "assistant" : msg.role;
const parts = msg.parts || (msg.content ? [{ type: "text", text: msg.content }] : []);
return { ...msg, role, parts };
});

// Convert to ModelMessage[] format
const modelMessages = await convertToModelMessages(normalizedMessages);

Memory not persisting​

  1. Check that Convex is running: npx convex dev
  2. Verify CONVEX_URL is set correctly
  3. Check browser console for errors

Search returns no results​

  1. Make sure you've had at least one conversation first
  2. If using semantic search, set up embeddingProvider
  3. Check that memorySpaceId matches between requests

See the Troubleshooting Guide for more solutions.

Next Steps​