Give your Vapi voice AI agents long-term memory. Make them remember conversations across calls, maintain user profiles, and retrieve relevant context for personalized conversations every time.
Voice AI is powerful, but without memory, every call feels like talking to a stranger. vapi-memory solves this by:
- ✅ Remember conversations across calls - not just within a session
- ✅ Build user profiles with facts, preferences, and history
- ✅ Retrieve relevant context based on what's being discussed
- ✅ Seamless Vapi integration - just 3 lines of code to get started
- ✅ Production-ready - caching, error handling, and monitoring built-in
import { VapiMemory } from 'vapi-memory';
const memory = new VapiMemory({
apiKey: process.env.SUPERMEMORY_API_KEY
});
// Get user context before/during call
const context = await memory.getContext({
userId: '+1234567890',
query: 'User calling for support'
});
// Return personalized assistant with context
return res.json(memory.createAssistantResponse(context, {
name: 'Support Agent',
model: { provider: 'openai', model: 'gpt-4o' }
}));
// Store conversation after call ends
await memory.storeConversation({
callId: 'call_123',
userId: '+1234567890',
transcript: [
{ role: 'user', content: 'I need help with my order' },
{ role: 'assistant', content: 'How can I help you today?' }
]
});bun add vapi-memory
# or
npm install vapi-memoryThis guide shows you exactly how to integrate vapi-memory with your Vapi server to give your voice AI long-term memory.
Configure your Vapi phone number to use your server instead of a fixed assistant:
- Go to Vapi Dashboard
- Select your phone number
- Click "Server URL" or "Assistant"
- Set URL to:
https://your-server.com/api/assistant-selector
Your server will handle assistant-request messages from Vapi and return personalized assistant configurations.
Add this endpoint to your server:
import { VapiMemory } from 'vapi-memory';
const memory = new VapiMemory({
apiKey: process.env.SUPERMEMORY_API_KEY
});
app.post('/api/assistant-selector', async (req, res) => {
const { message, call } = req.body;
// Vapi sends assistant-request when call comes in
if (message?.type === 'assistant-request') {
const phoneNumber = call.from.phoneNumber;
console.log(`📞 Incoming call from: ${phoneNumber}`);
// Get user's context from Supermemory (cached for speed!)
const context = await memory.getContext({
userId: phoneNumber, // Use phone number as user ID
query: 'Incoming call', // Optional query for better results
includeProfile: true, // Get static + dynamic profile
includeRecent: true, // Get recent conversation history
includeSearch: true // Search for relevant memories
});
console.log(`✅ Context retrieved in ${context.metadata.retrievalTime}ms`);
console.log(`📊 Profile: ${context.profile?.static.length || 0} static, ${context.profile?.dynamic.length || 0} dynamic`);
// Return personalized assistant with context baked in
const response = memory.createAssistantResponse(context, {
name: 'Customer Support Agent',
model: {
provider: 'openai',
model: 'gpt-4o'
},
voice: {
provider: '11labs',
voiceId: 'rachel'
}
});
return res.json(response);
}
res.status(200).end();
});Store conversations after calls end to build user profiles over time:
app.post('/api/vapi-webhook', async (req, res) => {
const { type, call, messages } = req.body;
// Vapi sends call-ended when call finishes
if (type === 'call-ended') {
const phoneNumber = call.from.phoneNumber;
console.log(`📞 Call ended: ${call.id}`);
const memory = new VapiMemory({
apiKey: process.env.SUPERMEMORY_API_KEY
});
// Store the full conversation to Supermemory
await memory.storeConversation({
callId: call.id,
userId: phoneNumber,
transcript: messages.map(m => ({
role: m.role,
content: m.content,
timestamp: m.transcript?.transcriptedAt
})),
metadata: {
duration: call.duration,
endedReason: call.endedReason,
timestamp: new Date().toISOString()
}
});
console.log('✅ Conversation stored successfully');
}
res.status(200).end();
});Let your Vapi assistant proactively fetch memories during conversations:
import { VapiToolFactory } from 'vapi-memory';
// Define a memory tool for your Vapi assistant
const memoryTool = {
type: 'function' as const,
name: 'get_user_memories',
description: 'Retrieve relevant memories about the user from their history',
parameters: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'What to search for in user history'
}
},
required: ['query']
}
};
// Add this tool to your assistant in Vapi dashboard
// When assistant needs context, it will call your server endpoint
// Handle tool requests from Vapi
app.post('/api/tools/get_user_memories', async (req, res) => {
const { parameters, call } = req.body;
const memory = new VapiMemory({
apiKey: process.env.SUPERMEMORY_API_KEY
});
// Search for memories based on what user asked
const context = await memory.getContext({
userId: call.from.phoneNumber,
query: parameters.query,
includeSearch: true,
includeRecent: false,
includeProfile: false
});
return res.json({
memories: context.searchResults.join('\n\n'),
count: context.searchResults.length
});
});See examples/vapi-bun-server.ts for a complete, production-ready implementation that includes:
- ✅ Assistant request handler with memory
- ✅ Tool handlers for all 5 memory tools
- ✅ Post-call webhook for conversation storage
- ✅ CORS support for development
- ✅ Error handling and logging
- ✅ Hot-reload for development
Run it:
cp .env.example .env
# Edit .env and add your SUPERMEMORY_API_KEY
bun run examples/vapi-bun-server.tsUser Calls Your Vapi Phone Number
│
▼
Vapi sends assistant-request → Your Server (Step 2)
│
├─ Get Context from Supermemory
│ ├─ Cache check (sub-50ms if cached!)
│ ├─ Profile lookup (static + dynamic facts)
│ ├─ Recent memories (last conversations)
│ └─ Semantic search (relevant to current query)
│
▼
Return personalized assistant → Vapi (within 7.5s!)
│
▼
AI responds with full context about the user
│
├─ "Hi John! I see you're a VIP customer..."
├─ "Your last order was #12345..."
├─ "You love coffee, right?"
└─ "...from your conversation last Tuesday"
│
▼
Call ends → Vapi sends call-ended → Your Server (Step 3)
│
▼
Store full conversation → Supermemory
│
├─ Analyzes conversation
├─ Extracts key facts and preferences
├─ Updates user profile (static + dynamic)
├─ Adds to searchable memory
└─ Caches for instant retrieval next call!
Before going to production, make sure you have:
- Supermemory API Key - Get from supermemory.ai
- Server Endpoint - Deploy your server (Railway, Vercel, AWS Lambda, etc.)
- HTTPS - Required for production Vapi integrations
- Error Handling - Handle API failures gracefully
- Rate Limiting - Don't overload Supermemory or Vapi
- Monitoring - Track memory retrieval times and error rates
- Enable Caching - Profile lookups are cached by default (60s TTL)
- Use Phone Numbers as User IDs - Easy to identify callers
- Set Appropriate Timeouts - Vapi requires response within 7.5s
- Monitor Memory Usage - Keep track of cache hit rates and API calls
- Handle Errors Gracefully - If Supermemory is down, return a basic assistant
Main class for managing Vapi agent memory.
new VapiMemory(options: VapiMemoryOptions)Options:
apiKey(required): Supermemory API keybaseUrl(optional): Custom base URL, default: 'https://api.supermemory.ai'maxTokens(optional): Maximum context tokens, default: 2000searchThreshold(optional): Search relevance threshold (0-1), default: 0.5cacheEnabled(optional): Enable caching, default: truecacheTTL(optional): Cache TTL in ms, default: 60000
Retrieves context for a user including profile, recent memories, and search results.
Request:
userId(required): User identifierquery(optional): Current query for searchcallId(optional): Call ID for per-call memoryincludeProfile(optional): Include user profile, default: trueincludeRecent(optional): Include recent memories, default: trueincludeSearch(optional): Include search results, default: true
Returns:
{
profile?: {
static: string[]; // Long-term facts
dynamic: string[]; // Recent memories
};
recentMemories: string[]; // Latest interactions
searchResults: string[]; // Semantically relevant
totalTokens: number; // Estimated token count
metadata: {
userId: string;
retrievalTime: number; // Time in ms
sources: string[]; // Where data came from
};
}Stores a conversation to build user profile.
Request:
callId(required): Vapi call IDuserId(required): User identifiertranscript(required): Array of message objects withroleandcontentmetadata(optional): Additional metadata (duration, sentiment, etc.)
Creates a Vapi assistant response with formatted context.
Gets complete user profile from Supermemory.
Adds a single memory to user profile.
Builds assistant with memory tools for dynamic retrieval.
Builds assistant with variable-based personalization ({{userName}}, etc.).
Returns cache statistics including hit rate and entry count.
Clears all cached profile data.
Set your Supermemory API key:
cp .env.example .env
# Edit .env and add your actual API key
export SUPERMEMORY_API_KEY="your-api-key-here"- Go to your Vapi dashboard (https://dashboard.vapi.ai)
- Select your phone number
- Set server URL to:
https://your-server.com/api/assistant-selector - Configure your server to handle
assistant-requestmessages (see Step 2 above) - Set webhook URL to:
https://your-server.com/api/vapi-webhook(optional, for call-ended events)
- Create tool endpoints at
/api/tools/{toolName}on your server - Configure tools in Vapi assistant or server
- Tools call your endpoints dynamically during conversations
- Return memories from Supermemory in real-time
# Install dependencies
bun install
# Run tests
bun test
# Build
bun run build
# Watch mode
bun run devvapi-memory/
├── src/
│ ├── VapiMemory.ts # Main library class
│ ├── client/
│ │ └── SupermemoryClient.ts # Supermemory API wrapper
│ ├── builders/
│ │ └── VapiResponseBuilder.ts # Vapi response construction
│ ├── formatters/
│ │ ├── ContextFormatter.ts # Context formatting & deduplication
│ │ └── VariableFormatter.ts # Variable extraction & formatting
│ ├── tools/
│ │ ├── VapiToolFactory.ts # Create Vapi tools
│ │ └── MemoryTools.ts # Pre-built tool sets
│ ├── utils/
│ │ ├── cache.ts # LRU cache implementation
│ │ └── token-counter.ts # Token estimation
│ └── types/
│ └── index.ts # TypeScript definitions
├── examples/
│ ├── basic-usage.ts # Simple usage demonstration
│ ├── vapi-bun-server.ts # Complete Vapi server ⭐
│ ├── server-integration.ts # Server-side assistant selection
│ └── advanced-context.ts # Cache performance demo
├── test/
│ ├── VapiMemory.test.ts # Core library tests
│ ├── TokenCounter.test.ts # Token counting tests
│ ├── LRUCache.test.ts # Cache tests
│ └── ContextFormatter.test.ts # Formatting tests
└── README.md # This file
- Customer Support: Remember previous issues, user preferences, VIP status
- Personal Assistants: Learn user habits, routines, and preferences
- Sales: Recall previous conversations, deals in progress, client history
- Healthcare: Remember patient history, medications, appointments
- Education: Track student progress, previous discussions, learning goals
- Never commit API keys to version control
- Use environment variables for sensitive configuration
- Validate user IDs before querying Supermemory
- Use HTTPS for all API calls
- Enable rate limiting to prevent abuse
MIT - Free to use in personal and commercial projects
Contributions are welcome! Please read our contributing guidelines.
- Fork the repository
- Create a feature branch
- Make your changes
- Write tests
- Submit a pull request
Made with ❤️ for the Vapi community