Memory Management Guide
This guide covers best practices for managing memories in your application using the Papr Memory API. We'll explore strategies for organizing, updating, and maintaining your memory store effectively.
Memory Structure
A memory in Papr consists of:
interface Memory {
content: string; // The main content
type: "text" | "code_snippet" | "document"; // Content type
external_user_id?: string; // Preferred user scoping field
metadata?: {
topics?: string[]; // Topics as string array
hierarchical_structures?: string; // Navigation path (e.g., "Business/Planning/Product")
createdAt?: string; // ISO timestamp
location?: string; // Optional location context
emoji_tags?: string[]; // Emoji representations
emotion_tags?: string[]; // Emotional context
conversationId?: string; // For linking related memories
sourceUrl?: string; // Original source if applicable
customMetadata?: Record<string, string | number | boolean | string[]>;
};
memory_policy?: {
acl?: {
read?: string[];
write?: string[];
};
};
context?: Array<{
role: "user" | "assistant";
content: string;
}>;
relationships_json?: Array<{
relation_type: string;
related_item_id?: string;
metadata?: Record<string, unknown>;
}>;
}Organizing Memories
Using Metadata Effectively
- Topics
const memory = {
content: "Discussion about new feature requirements",
type: "text",
metadata: {
topics: ["product development", "feature planning", "user feedback"],
hierarchical_structures: "Product/Features/Requirements",
customMetadata: {
priority: "high",
department: "product",
feature_category: "user_experience"
}
}
};- Emotional Context
const memory = {
content: "Team celebration of successful launch",
type: "text",
metadata: {
emoji_tags: ["🎉", "🚀", "🎯"],
emotion_tags: ["excited", "accomplished", "proud"]
}
};- Conversation Threading
const memory = {
content: "Follow-up on feature discussion",
type: "text",
metadata: {
conversationId: "feat-123-discussion",
sourceUrl: "https://meeting-notes.company.com/123"
}
};- Access Control
const memory = {
content: "Confidential project update",
type: "text",
external_user_id: "user_123",
memory_policy: {
acl: {
read: ["external_user:user_123", "external_user:user_456", "external_user:user_789"],
write: ["external_user:user_123"]
}
}
};Memory Lifecycle Management
1. Creating Memories
Always validate and format your content before creation:
function prepareMemory(content: string, type: string) {
return {
content: content.trim(),
type,
metadata: {
createdAt: new Date().toISOString(),
topics: extractTopics(content), // Your topic extraction logic
hierarchical_structures: determineHierarchy(content) // Your hierarchy logic
}
};
}
const memory = prepareMemory("Meeting notes...", "text");
const response = await fetch('https://memory.papr.ai/v1/memory', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': '<your-api-key>',
'X-Client-Type': 'your_app_name',
'Accept-Encoding': 'gzip'
},
body: JSON.stringify(memory)
});2. Updating Memories
Keep track of memory changes:
const updateMemory = async (memoryId: string, updates: Partial<Memory>) => {
const response = await fetch(`https://memory.papr.ai/v1/memory/${memoryId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-API-Key': '<your-api-key>',
'X-Client-Type': 'your_app_name',
'Accept-Encoding': 'gzip'
},
body: JSON.stringify({
...updates,
metadata: {
...updates.metadata,
updatedAt: new Date().toISOString()
}
})
});
return response.json();
};3. Deleting Memories
Delete Individual Memory
const deleteMemory = async (memoryId: string) => {
const response = await fetch(`https://memory.papr.ai/v1/memory/${memoryId}`, {
method: 'DELETE',
headers: {
'X-API-Key': '<your-api-key>',
'X-Client-Type': 'your_app_name'
}
});
return response.json();
};Delete All Memories for a User
⚠️ WARNING: This operation cannot be undone. All memories for the specified user will be permanently deleted.
const deleteAllMemories = async (externalUserId?: string) => {
const url = new URL('https://memory.papr.ai/v1/memory/all');
if (externalUserId) {
url.searchParams.append('external_user_id', externalUserId);
}
const response = await fetch(url, {
method: 'DELETE',
headers: {
'X-API-Key': '<your-api-key>',
'X-Client-Type': 'your_app_name'
}
});
return response.json();
};
// Delete all memories for a specific user
const result = await deleteAllMemories('user123');
// Delete all memories for the authenticated developer (if no user specified)
const developerResult = await deleteAllMemories();4. Batch Processing
Group related memories for efficient processing:
You can set up webhooks to receive notifications when batch processing completes. This is especially useful for large batches that may take time to process asynchronously.
const batchAddMemories = async (memories: Memory[]) => {
// Group memories by type
const groupedMemories = memories.reduce((acc, memory) => {
acc[memory.type] = acc[memory.type] || [];
acc[memory.type].push(memory);
return acc;
}, {});
// Process each group
for (const [type, typeMemories] of Object.entries(groupedMemories)) {
const response = await fetch('https://memory.papr.ai/v1/memory/batch', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': '<your-api-key>',
'X-Client-Type': 'your_app_name',
'Accept-Encoding': 'gzip'
},
body: JSON.stringify({
memories: typeMemories,
batch_size: 10,
webhook_url: "https://your-app.com/webhooks/batch-complete" // Webhook for batch completion notification
})
});
const result = await response.json();
handleBatchResults(result); // Your result handling logic
}
};
// Example webhook handler implementation
const handleBatchWebhook = async (req, res) => {
const batchData = req.body;
console.log(`Batch ${batchData.batch_id} completed: ${batchData.total_successful} successful, ${batchData.total_failed} failed`);
// Update your application state based on completion
await updateBatchStatus(batchData.batch_id, batchData.status);
res.status(200).send({ received: true });
};Async processing, status, and webhooks
POST /v1/memory can return before background work finishes (graph indexing, enrichment, and related stages). To track progress:
| Mechanism | Use when |
|---|---|
GET /v1/memory/status/{memory_id} | Poll a single memory (queued, quick_saved, processing, completed, failed) |
GET /v1/memory/batch/status/{batch_id} | Poll a batch (batch_id from POST /v1/memory/batch) |
| WebSockets | Live updates at /ws/memory-status/{memory_id} or /ws/memory-status (see API reference) |
Full lifecycle example
import time
from papr_memory import Papr
client = Papr(x_api_key="YOUR_KEY")
# 1. Create memory with webhook
memory = client.memory.add(
content="Q4 planning: Launch new API, hire 2 engineers, $200k budget",
enable_holographic=True,
frequency_schema_id="general",
webhook_url="https://api.myapp.com/papr-webhook",
webhook_secret="my_webhook_secret_key"
)
memory_id = memory.memory_id
print(f"Created: {memory_id}, initial status: quick_saved")
# 2. Poll status if you need it immediately (webhook takes ~seconds)
for i in range(5):
status = client.memory.get_status(memory_id)
print(f"Poll {i+1}: {status.status}")
if status.status == "completed":
break
time.sleep(2)
# 3. Webhook handler receives POST when done
# POST https://api.myapp.com/papr-webhook
# Headers: X-Webhook-Secret, X-Webhook-Signature (HMAC-SHA256)
# Body: {
# "event": "memory.completed",
# "memory_id": "mem_123",
# "status": "completed",
# "completed_at": "2026-04-21T10:30:00Z"
# }When to use each:
- Webhook: Background processing, decoupled systems, batch workflows
- Polling: Need result immediately in same request flow (chat, synchronous APIs)
- WebSocket: Real-time UI updates, progress bars, live dashboards
Webhooks on POST /v1/memory
Optional query parameters webhook_url and webhook_secret register a callback when processing completes. The payload includes fields such as event, memory_id, status, and completed_at. With webhook_secret, Papr sends X-Webhook-Secret and an HMAC X-Webhook-Signature—same pattern as batch and document webhooks. See Document processing for verification patterns.
Holographic embeddings and frequencies
Query parameters enable_holographic and frequency_schema_id on create/update opt into graph-aware (holographic) indexing on write. Domain schemas, built-in cosqa / scifact / general, custom registration, and how that pairs with holographic_config on search are covered in Graph-aware embeddings. Field-level detail remains in the API reference.
Namespace HTTP API
Manage namespaces with POST /v1/namespace (create), GET /v1/namespace (list), GET/PATCH/DELETE /v1/namespace/{namespace_id}, and related routes in the reference. This is separate from passing namespace_id on individual reads and writes.
Scoped deletion for a user
DELETE /v1/user accepts an optional namespace_id query parameter to delete only within that namespace.
Search and Retrieval Strategies
1. Semantic Search
Write detailed queries for better results:
const searchMemories = async (topic: string, context: string) => {
const response = await fetch('https://memory.papr.ai/v1/memory/search', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': '<your-api-key>',
'X-Client-Type': 'your_app_name',
'Accept-Encoding': 'gzip'
},
body: JSON.stringify({
query: `Find detailed discussions about ${topic} that include ${context}. Focus on items with specific examples or decisions made.`,
enable_agentic_graph: true, // Enable intelligent context-aware search
metadata: { // Optional: Filter by metadata
customMetadata: {
priority: "high",
department: "product"
}
}
})
});
return response.json();
};2. Result Processing
Handle search results effectively:
interface SearchResult {
memories: Memory[];
nodes: Node[]; // Graph nodes if available
}
function processSearchResults(results: SearchResult) {
// Group by hierarchical structure
const groupedResults = results.memories.reduce((acc, memory) => {
const hierarchy = memory.metadata?.hierarchical_structures || 'Uncategorized';
acc[hierarchy] = acc[hierarchy] || [];
acc[hierarchy].push(memory);
return acc;
}, {});
// Sort within each group by date
for (const hierarchy in groupedResults) {
groupedResults[hierarchy].sort((a, b) => {
return new Date(b.metadata.createdAt) - new Date(a.metadata.createdAt);
});
}
return groupedResults;
}3. Providing Feedback
Improve future search results by providing feedback:
const submitFeedback = async (searchId: string, isHelpful: boolean, details?: string) => {
const response = await fetch('https://memory.papr.ai/v1/feedback', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': '<your-api-key>',
'X-Client-Type': 'your_app_name'
},
body: JSON.stringify({
search_id: searchId,
feedbackData: {
feedbackType: isHelpful ? 'thumbs_up' : 'thumbs_down',
feedbackSource: 'inline',
feedbackText: details
}
})
});
return response.json();
};
// After receiving search results
const searchResponse = await searchMemories('product roadmap', 'Q2 planning');
const searchId = searchResponse.search_id;
// Later, after user interaction
submitFeedback(searchId, true, "This was very helpful for our planning meeting");Error Handling and Monitoring
Implement robust error handling:
async function safeMemoryOperation<T>(operation: () => Promise<T>): Promise<T> {
const maxRetries = 3;
let lastError: Error;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
lastError = error;
if (error.status === 429) { // Rate limit
const retryAfter = parseInt(error.headers.get('Retry-After') || '5');
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
continue;
}
if (error.status >= 500) { // Server error
await new Promise(resolve => setTimeout(resolve, attempt * 1000));
continue;
}
throw error; // Client error or other - don't retry
}
}
throw lastError;
}
// Usage
try {
const memory = await safeMemoryOperation(() =>
client.memory.add({
content: "Important content",
type: "text"
})
);
console.log(`Memory added: ${memory.data[0].memoryId}`);
} catch (error) {
console.error("Failed to add memory", error);
// Handle specific error codes
if (error.code === 403) {
console.log("Subscription limit reached. Please upgrade.");
} else if (error.code === 413) {
console.log("Content too large. Please reduce size.");
}
}Memory Management Best Practices
Content Organization
- Use consistent hierarchical structures
- Apply meaningful topic tags
- Add emotional context when relevant
Batch Processing
- Group related memories in batches
- Use appropriate batch sizes (10-20 items)
- Handle partial success scenarios
Access Control
- Use external_user_id for easy identification
- Set appropriate read/write access lists
- Review access periodically
Performance Optimization
- Use Accept-Encoding: gzip header
- Implement client-side caching
- Enable agentic graph for better search results
Next Steps
- Learn about Batch Processing
- Explore Search Tuning
- See the complete API Reference