REM Labs Documentation
One API for memory across every model and every tool. Works with ChatGPT, Claude, Obsidian, Slack, and every agent framework you're already running.
Canonical SDK surface
Five methods, same shape across Python, Node, CLI, and MCP. Every example on this page uses these names.
rem.store({ key, value, namespace })— writerem.recall({ query, namespace, limit })— read by queryrem.dream({ strategies })— consolidation (9 strategies)rem.list({ namespace, limit })— enumeraterem.forget({ key })— delete (GDPR)
5-Minute Quickstart
Zero to working memory in five minutes. No signup form, just a key.
Step 1: Get an API key
curl -X POST https://remlabs.ai/v1/keys
# Returns: {"key": "sk-rem-abc123...", "balance": 0}
Step 2: Store your first memory
curl -X POST https://remlabs.ai/v1/memory-set \ -H "Authorization: Bearer YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"key": "user_pref", "value": "Prefers dark mode and TypeScript"}'
Step 3: Search memories
curl -X POST https://remlabs.ai/v1/memory-search \ -H "Authorization: Bearer YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "what theme does the user prefer"}'
Step 4: Use the SDK (optional)
import { REM } from '@remlabs/memory' const rem = new REM({ apiKey: 'sk-rem-...' }) await rem.store({ key: 'user_pref', value: 'Prefers dark mode' }) const results = await rem.recall({ query: 'user preferences' }) await rem.forget({ key: 'user_pref' })
That's it. Memories are automatically indexed for full-text, semantic, and entity search at write time.
1 command to start
$ npx @remlabs/memory
That's it. Runs the server, opens the console, gives you an API key.
3-line quick start
import { REM } from '@remlabs/memory' const rem = new REM({ apiKey: 'rem_...' }) await rem.store({ key: 'user_pref', value: 'Prefers dark mode' })
Canonical SDK methods
Five methods. That's the whole API surface. store / recall / dream / list / forget — same shape across Python, Node, CLI, and MCP.
| SDK | REST | What it does |
|---|---|---|
| rem.store({ key, value, namespace }) | POST /v1/memory/set | Write a memory |
| rem.recall({ query, namespace, limit }) | POST /v1/memory/search | Read with natural-language query |
| rem.dream({ strategies }) | POST /v1/dream/start | Run consolidation (9 strategies) |
| rem.list({ namespace, limit }) | GET /v1/memory/list | Enumerate memories |
| rem.forget({ key }) | POST /v1/memory/delete | Delete a memory (GDPR) |
Supported Platforms
REM is the universal memory layer. It works with every major AI tool, framework, and platform.
AI Assistants
- ChatGPT -- import conversation history
- Claude -- import conversation history
- Any OpenAI-compatible API -- works with any model provider
Note-taking & Knowledge
- Obsidian -- plugin + vault sync
- Notion -- API integration
- Apple Notes -- import
Developer Frameworks
- LangChain -- drop-in memory provider
- CrewAI -- shared crew memory
- AutoGen -- agent memory backend
- Vercel AI SDK -- middleware integration
- OpenClaw / OpenFang -- native support
Communication
- Slack -- bot integration
- Discord -- bot integration
- Gmail -- email integration
Productivity
- Linear -- project context
- GitHub -- repo + issue context
- Todoist -- task integration
Browser & Editor
- Chrome extension -- capture from any webpage
- VS Code extension -- code context memory
Protocol & API
- MCP server -- works with Claude, Cursor, and any MCP host
- REST API -- universal HTTP access
- WebSocket -- real-time memory streaming
Quick Start
Three lines to your first memory. No signup form -- generate a key instantly.
1. Get an API key
curl -X POST https://remlabs.ai/v1/keys
Returns {"key": "sk-rem-abc123...", "balance": 0}. Free tier -- no credit card.
2. Store a memory
curl -X POST https://remlabs.ai/v1/memory-set \ -H "Authorization: Bearer YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"key": "user_pref", "value": "Prefers dark mode and TypeScript"}'
3. Search it
curl -X POST https://remlabs.ai/v1/memory-search \ -H "Authorization: Bearer YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "what theme does the user prefer"}'
Authentication
All authenticated endpoints require a Bearer token in the Authorization header.
You can also pass the key via the X-API-Key header.
# Option A: Authorization header (recommended) Authorization: Bearer sk-rem-abc123... # Option B: X-API-Key header X-API-Key: sk-rem-abc123...
Generating a key
No authentication required. Returns a new API key with 0 balance (free tier). Keys follow the format
sk-rem-{24 hex chars} with 96+ bits of entropy.
Optional: Memory 2FA
For high-security workloads, you can enable two-factor authentication on memory operations. When enabled, you must create a session and verify it via email before any memory read/write:
POST /v1/memory/2fa/enable-- enable 2FA (requires email)POST /v1/memory/session/create-- start a session (sends verification code)POST /v1/memory/session/verify-- verify the code, then include the session ID asX-Memory-Sessionheader
Base URL
https://remlabs.ai/v1/
All endpoints are prefixed with /v1/. The API is also available at https://remlabs.ai/v1/ (proxied)
.
/v1/). Breaking changes are announced 90 days in advance via the changelog and email to all registered API keys. Non-breaking additions (new fields, new endpoints) ship without version bumps.
| SDK Method | REST Endpoint | Description |
|---|---|---|
rem.store({ key, value, namespace }) | POST /v1/memory/set | Write a memory |
rem.recall({ query, namespace, limit }) | POST /v1/memory/search | Read by natural-language query |
rem.dream({ strategies }) | POST /v1/dream/start | Run consolidation |
rem.list({ namespace, limit }) | GET /v1/memory/list | Enumerate memories |
rem.forget({ key }) | POST /v1/memory/delete | Delete a memory |
The aliases /v1/remember, /v1/recall, and /v1/forget also work and route to the same handlers.
POST /v1/memory-set
Store or update a memory. Automatically indexes for full-text search, generates vector embeddings, extracts structured facts, calculates importance scores, and detects duplicates.
| Parameter | Type | Description |
|---|---|---|
| key | string required | Unique identifier for this memory within the namespace |
| value | string | object | The memory content. Strings and JSON objects are both accepted. Automatically compressed above 512 bytes. |
| namespace | string default: "default" | Logical grouping for memories. Use namespaces to isolate users, projects, or agents. |
| tags | string[] | string | Tags for filtering. Array of strings or comma-separated string. |
| ttl_seconds | number optional | Time-to-live in seconds. Memory auto-expires after this duration. 0 or omit for permanent. |
| ttl | number optional | Alias for ttl_seconds. Time-to-live in seconds. Memory auto-expires after this duration. 0 = permanent (default). |
| type | string optional | Memory type: user, feedback, project, or reference. Auto-detected types also include fact, preference, insight, and procedure. Types affect Dream Engine consolidation priority. Defaults to project. |
{
"key": "user_pref",
"namespace": "default",
"status": "stored",
"version": 1,
"type": "project",
"importance": 0.45,
"facts_extracted": 2
}
"status": "merged" and "merged_from".
Upsert Behavior (Update/Amend)
Calling POST /v1/memory-set with the same key overwrites the existing value (upsert).
The previous value is preserved in version history. The version field in the response increments
with each update. No separate "update" endpoint is needed -- memory-set handles both create and update.
Idempotent Writes
Writing the same key performs an upsert -- no duplicates are created. If the value is identical
to the existing value, the write is a no-op and the version does not increment. This means
memory-set is safe to retry on network errors without risking duplicate data.
Storing with the same key overwrites the previous value (upsert). To prevent duplicate writes from network retries, use a deterministic key based on content hash.
TTL and Expiry
Memories with TTL auto-expire. Expired memories are pruned in background cleanup cycles (every 10 minutes). Until pruned, expired memories are excluded from search and recall results.
Deduplication
The Dream Engine automatically detects and merges near-duplicate memories during consolidation. Exact duplicates (same key + namespace) are handled by upsert.
Contradictory Memories
When you store a memory that contradicts a previous one (e.g., "User lives in NYC" then "User lives in SF"),
both versions are stored with timestamps. The latest value wins on recall. Full version history is available
via memory-get with "include_history": true. The Memory Synthesis
validate strategy can detect and flag contradictions automatically.
When contradictory information is stored (e.g., "prefers dark mode" then "prefers light mode"),
the latest value wins on recall. Both versions are preserved in history. The Dream Engine's
validate strategy can detect and flag contradictions for manual review.
Memory Confidence Scoring
Search results include a score field (0.0 to 1.0) representing retrieval confidence.
The score combines relevance signals from whichever search mode is used (keyword match, cosine similarity,
entity extraction, recency). Use min_score on search endpoints to filter low-confidence results.
The fact extraction system also assigns per-fact confidence scores.
Custom Metadata
The metadata field on memory-set accepts arbitrary JSON. Store any structured data
alongside the memory value -- user IDs, source URLs, agent names, session IDs, or application-specific context.
Metadata is returned with search results and can be used for client-side filtering.
{
"key": "meeting_notes",
"value": "Q2 planning: launch SDK by June",
"metadata": {
"source": "slack",
"channel": "#product",
"author": "user_42",
"priority": "high"
}
}
Sandbox / Test Mode
The demo key available from /playground operates in sandbox mode. Sandbox data is isolated
from production namespaces and is periodically reset. Use it for testing and prototyping without
affecting your production memories. Generate a production key via POST /v1/keys or the console
when you're ready to go live.
POST /v1/memory-get
Retrieve a single memory by its exact key.
| Parameter | Type | Description |
|---|---|---|
| key | string required | The exact key to retrieve |
| namespace | string default: "default" | Namespace to look in |
{
"key": "user_pref",
"namespace": "default",
"value": "Prefers dark mode and TypeScript",
"found": true,
"tags": ["preferences"],
"version": 1,
"type": "user",
"created": "2026-04-12T10:30:00.000Z",
"updated": "2026-04-12T10:30:00.000Z"
}
If the key does not exist or has expired, returns "found": false and "value": null.
POST /v1/memory/ask · the /ask endpoint
The retrieval endpoint that scored 94.6% on LongMemEval (473/500, byte-exact upstream GPT-4o judge).
Runs the full 6-stage retrieval pipeline and returns a synthesized answer with citations — not a raw ranked list.
Use this when you want REM to compose an answer. Use /v1/memory-search when you want the raw candidates.
- Fact lookup (knowledge graph)
- Multi-query expansion
- Vector search (chunked, HNSW)
- Keyword fallback (FTS5 AND-first)
- Full-context fallback
- LLM reranking
| Parameter | Type | Description |
|---|---|---|
| question | string | Natural-language question. Not a search query — an actual question. |
| namespace | string default: "default" | Namespace to search within. Use * to span all namespaces the key can read. |
{
"answer": "The user committed to shipping the self-host quickstart by Q2 W3.",
"citations": [
{ "key": "standup_2026_04_09", "score": 0.91 },
{ "key": "okr_q2", "score": 0.87 }
],
"latency_ms": 420
}
The benchmark harness in benchmarks/longmemeval.js calls this endpoint directly.
If you want to reproduce 94.6%, call /v1/memory/ask, not /v1/memory-search.
POST /v1/memory-search
Low-level retrieval primitive. Search memories by natural language query. Uses TF-IDF word scoring with stemming,
partial matching, and phrase bonuses. Results are ranked by relevance score. For the 94.6% pipeline, use /v1/memory/ask instead.
| Parameter | Type | Description |
|---|---|---|
| query | string | Natural language search query. Empty query returns all memories. |
| namespace | string default: "default" | Namespace to search within |
| limit | number default: 50 | Maximum results to return |
| offset | number default: 0 | Pagination offset. Use with limit to paginate through large result sets. |
| type | string optional | Filter by type: user, feedback, project, reference |
| tags | string[] optional | Filter results to memories with any of these tags |
| source | string optional | Filter by source (e.g., slack, obsidian, chatgpt) |
| date_from | string optional | Only return memories created after this date (ISO 8601 or YYYY-MM-DD) |
| date_to | string optional | Only return memories created before this date (ISO 8601 or YYYY-MM-DD) |
| min_score | number default: 0 | Minimum retrieval confidence threshold (0.0 to 1.0). Only return results above this score. |
score field (0.0 to 1.0) indicating retrieval confidence. Filter with min_score parameter to only return high-confidence results. A score of 0.7+ generally indicates a strong match.
{
"results": [
{
"key": "user_pref",
"value": "Prefers dark mode and TypeScript",
"tags": ["preferences"],
"type": "user",
"score": 0.8421,
"updated": "2026-04-12T10:30:00.000Z"
}
],
"count": 1,
"query": "what theme does the user prefer"
}
Honest Retrieval
When confidence is below the threshold, REM Labs returns fewer results rather than hallucinating.
Approximately 8% of queries result in empty or reduced results -- this is intentional. The system
says "I don't know" rather than guess. Configure the threshold with the min_score parameter.
limit and offset parameters for pagination. Streaming responses are not currently supported.
POST /v1/memory-delete
Delete a memory by key. The final value is recorded in version history before deletion. Protected (locked) memories cannot be deleted.
| Parameter | Type | Description |
|---|---|---|
| key | string required | Key to delete |
| namespace | string default: "default" | Namespace containing the key |
{
"deleted": true,
"key": "user_pref",
"namespace": "default"
}
POST /v1/memory-list
List all keys in a namespace with optional tag filter and pagination.
| Parameter | Type | Description |
|---|---|---|
| namespace | string default: "default" | Namespace to list |
| tag | string optional | Filter by tag |
| include_meta | boolean default: false | Include size, tags, timestamps per key |
| limit | number default: 100 | Max keys to return |
| offset | number default: 0 | Pagination offset |
POST /v1/memory-stats
Get namespace analytics: total count, size in bytes, oldest and newest timestamps, TTL count.
| Parameter | Type | Description |
|---|---|---|
| namespace | string default: "default" | Namespace to analyze |
GET /v1/memory/health
Returns health metrics for your memory store. Use this to monitor freshness, staleness, and overall consistency.
| Parameter | Type | Description |
|---|---|---|
| namespace | string default: "default" | Namespace to check |
{
"total_memories": 1247,
"fresh_7d": 89,
"stale_30d": 234,
"consistency_score": 78,
"health": "needs_attention"
}
"healthy" (score 80+), "needs_attention" (score 50--80), "degraded" (score below 50).
GET /v1/memory/conflicts
Returns potential contradictory memories -- keys with 3 or more historical versions that may contain conflicting information.
| Parameter | Type | Description |
|---|---|---|
| namespace | string default: "default" | Namespace to scan for conflicts |
{
"conflicts": [
{ "key": "user_budget", "versions": 4 },
{ "key": "project_deadline", "versions": 3 }
],
"count": 2
}
POST /v1/memory/bulk-delete
Delete memories matching a pattern, tag, or age. Useful for cleanup, GDPR compliance, or removing stale data in bulk.
| Parameter | Type | Description |
|---|---|---|
| namespace | string default: "default" | Target namespace |
| pattern | string optional | Delete memories where key or value matches this pattern |
| tag | string optional | Delete memories containing this tag |
| older_than_days | number optional | Delete memories not updated in N days |
{
"ok": true,
"deleted": 47
}
Semantic Search (Vector)
Search using semantic embeddings with cosine similarity.
Embeddings are generated automatically on memory-set.
| Parameter | Type | Description |
|---|---|---|
| query | string required | Natural language query -- embedded at query time |
| namespace | string default: "default" | Namespace to search |
| limit | number default: 10 | Max results |
| min_score | number default: 0 | Minimum relevance threshold |
Full-Text Search
Full-text search with stemming and Unicode tokenization. Best for exact term matching where embeddings may miss. Automatically maintained as a write-through index.
| Parameter | Type | Description |
|---|---|---|
| query | string required | Search terms. Precision-first with intelligent query expansion. |
| namespace | string default: "default" | Namespace to search |
| limit | number default: 20 | Max results |
Temporal Search
Every memory includes created and updated timestamps. Query temporal data with
date_from and date_to filters on any search endpoint, or use the dedicated temporal
search for advanced time-aware queries. The system tracks how facts evolve over time -- store
conflicting information and the latest version wins on recall, but full history is available via
the versioning API (memory-get with "include_history": true).
Automatically extracts dates from memory content
(ISO, US format, natural language like "last Tuesday", "yesterday"). Supports
before_date, after_date, and as_of queries for temporal reasoning.
| Parameter | Type | Description |
|---|---|---|
| query | string optional | Text query to filter |
| namespace | string default: "default" | Namespace to search |
| before_date | string optional | Only memories from before this date (YYYY-MM-DD) |
| after_date | string optional | Only memories from after this date |
| as_of | string optional | Return facts that were true as of this date |
| limit | number default: 50 | Max results |
Tag Search
Find memories by one or more tags.
| Parameter | Type | Description |
|---|---|---|
| tags | string[] | Array of tags to match (OR logic) |
| tag | string | Single tag shortcut (alternative to tags array) |
| namespace | string default: "default" | Namespace to search |
| limit | number default: 100 | Max results |
Fact Search
Query the auto-extracted entity knowledge graph. Facts are structured as
Subject-Relation-Value triples (e.g., user | education | Computer Science)
with confidence scores and bitemporal versioning.
| Parameter | Type | Description |
|---|---|---|
| query | string required | Search relations and values (e.g., "education", "location") |
| namespace | string default: "default" | Namespace to search |
| limit | number default: 50 | Max results |
memory-set call with no additional configuration.
Multi-Signal Fusion
Combine results from multiple search strategies (semantic, full-text, keyword, fact) using multi-signal fusion scoring.
| Parameter | Type | Description |
|---|---|---|
| query | string required | Natural language query |
| namespace | string default: "default" | Namespace to search |
| limit | number default: 10 | Max results |
memory-rrf for production retrieval. It fuses semantic vectors,
full-text search, keyword scoring, and fact extraction into a single ranked result set using
multi-signal fusion.
Import
Import conversation history from other AI platforms. Memories are parsed, timestamped, tagged, and stored in your namespace. Up to 5,000 messages per import.
ChatGPT Export
{
"source": "chatgpt",
"data": // Your conversations.json from ChatGPT export,
"namespace": "imported"
}
Claude Export
{
"source": "claude",
"data": // Your Claude conversation export,
"namespace": "imported"
}
File Upload (Drag-and-Drop)
Upload files directly: JSON, CSV, plain text, and Obsidian markdown vaults. Files are parsed, chunked, and stored as individual memories. Use the Import page in the console for a drag-and-drop UI.
Supported formats:
- JSON -- array of objects or key-value pairs
- CSV -- first column as key, second as value
- Plain text -- split by paragraphs or custom delimiter
- Obsidian -- markdown notes with frontmatter metadata preserved
Memory Synthesis
Neuroscience-inspired memory consolidation. The synthesis pipeline processes your stored memories through 9 strategies to surface insights, compress redundancy, discover cross-domain patterns, and generate forecasts.
Run a Dream
| Parameter | Type | Description |
|---|---|---|
| topic | string optional | Focus the dream on a specific topic |
| namespace | string default: "default" | Namespace to dream about |
| strategies | string[] optional | Choose specific strategies (see below) |
9 Dream Strategies
| Strategy | What it does |
|---|---|
| synthesize | Merge related memories into coherent summaries |
| pattern_extract | Discover recurring patterns across memories |
| insight_generate | Surface non-obvious connections and insights |
| compress | Reduce redundancy while preserving key information |
| associate | Build cross-domain links between distant memories |
| validate | Check consistency and flag contradictions |
| evolve | Update outdated memories with newer information |
| forecast | Generate predictions based on memory trends |
| reflect | Meta-cognitive analysis of memory quality and gaps |
Additional Dream endpoints:
POST /v1/dream/subscribe-- schedule recurring dreams on a cronGET /v1/dream/review-- review pending dream suggestionsPOST /v1/dream/deploy-- apply dream suggestions to memoryGET /v1/dream/insights-- browse generated insightsPOST /v1/dream/share/:id-- share a dream report publicly
Multiplayer Memory
Shared memory spaces for teams. Create a space, invite collaborators by API key, and read/write shared memories with role-based access control.
Create a shared space
| Parameter | Type | Description |
|---|---|---|
| name | string required | Space name |
| description | string optional | Space description |
Roles
| Role | Read | Write | Invite |
|---|---|---|---|
| owner | Yes | Yes | Yes |
| admin | Yes | Yes | Yes |
| developer | Yes | Yes | No |
| viewer | Yes | No | No |
Shared space operations
POST /v1/memory/share/invite-- invite by API key:{ space_id, invite_key, role }POST /v1/memory/share/set-- write shared memory:{ space_id, key, value }POST /v1/memory/share/get-- read shared memory:{ space_id, key }POST /v1/memory/share/search-- search shared space:{ space_id, query }GET /v1/memory/share/list-- list spaces you belong toGET /v1/memory/share/:space_id-- space infoGET /v1/memory/share/members/:space_id-- list membersGET /v1/memory/share/activity/:space_id-- activity logPOST /v1/memory/share/:space_id/retention-- change retention policy
Collaborator System
Alternatively, use namespace-level collaboration with invite codes:
POST /v1/memory/collaborator/invite-- create invite:{ namespace, role, email }POST /v1/memory/collaborator/accept-- accept invite:{ code }GET /v1/memory/collaborators-- list collaboratorsDELETE /v1/memory/collaborator/:id-- revoke accessGET /v1/memory/access-log-- audit log of namespace accesses
Knowledge Graph
REM automatically extracts entities and relationships from stored memories into a queryable
knowledge graph. Every memory-set call triggers entity extraction -- no additional
configuration needed. Use POST /v1/memory/graph-query to traverse relationships.
Entities are structured as subject-relation-object triples with confidence scores.
In addition to the auto-extracted graph (via Fact Search), you can manually curate triples using the endpoints below:
Add a triple
{
"subject": "TypeScript",
"predicate": "is_preferred_by",
"object": "user",
"confidence": 0.95
}
Query the graph
Filter by any combination of subject, predicate, object, or free-text query.
Graph Walk
Traverse the graph from a starting entity, following edges up to a specified depth. Returns the full subgraph of connected entities.
Graph Query (Auto-Extracted)
Query the auto-extracted entity graph from your stored memories. Returns entities and relationships
discovered during memory-set processing. Filter by subject, predicate,
object, or free-text query.
Additional graph endpoints:
DELETE /v1/knowledge/triple/:id-- remove a tripleGET /v1/knowledge/stats-- graph statistics (triple count, unique subjects, top predicates)GET /v1/knowledge/connections/:entity-- all connections for an entityPOST /v1/knowledge/path-- find shortest path between two entitiesPOST /v1/knowledge/subgraph-- extract a subgraph around a topic
Inbound Webhooks
Receive data from external services into your memory system. Each API key has a unique webhook inbox addressable by key prefix.
Send a webhook
Public endpoint -- no authentication required. The key_prefix is the first 12 characters of
the target API key. POST any JSON payload and it will be stored in the inbox.
Read webhooks
Authenticated. Returns the last 50 webhooks received for your API key.
Outbound Webhook Events
Register webhooks to react to memory events in real time. Your callback URL will receive a POST request whenever the subscribed event fires.
{
"url": "https://your-app.com/webhook",
"events": ["memory.created", "memory.updated", "memory.deleted", "dream.completed"]
}
Supported events:
memory.created-- fired when a new memory is storedmemory.updated-- fired when an existing memory is overwritten (upsert)memory.deleted-- fired when a memory is deleteddream.completed-- fired when a Memory Synthesis (dream) run finishes
Pub/Sub Channels
Agent-to-agent communication via named channels.
POST /v1/channels/publish-- publish:{ channel, message }GET /v1/channels/:name-- read messages (default: last hour)GET /v1/channels-- list all channels
Node.js SDK
npm install @remlabs/memory # private beta -- request access in Discord
import { RemMemory } from '@remlabs/memory'; const mem = new RemMemory({ apiKey: process.env.REM_API_KEY }); // Store await mem.set('user_pref', 'Prefers dark mode', { namespace: 'myapp', tags: ['preferences'] }); // Search const results = await mem.search('user preferences', { namespace: 'myapp', limit: 5 }); // Get const memory = await mem.get('user_pref'); // Delete await mem.delete('user_pref');
Python SDK
pip install remlabs-memory
from remlabs import RemMemory import os mem = RemMemory(api_key=os.environ["REM_API_KEY"]) # Store mem.set("user_pref", "Prefers dark mode", namespace="myapp") # Search results = mem.search("user preferences", namespace="myapp", limit=5) # Get memory = mem.get("user_pref") # Delete mem.delete("user_pref")
cURL Examples
# Generate API key (no auth needed) curl -X POST https://remlabs.ai/v1/keys # Store memory curl -X POST https://remlabs.ai/v1/memory-set \ -H "Authorization: Bearer sk-rem-..." \ -H "Content-Type: application/json" \ -d '{"key":"meeting","value":"Q2 planning: launch memory SDK by June","tags":["work","q2"]}' # Semantic search curl -X POST https://remlabs.ai/v1/memory-search-semantic \ -H "Authorization: Bearer sk-rem-..." \ -H "Content-Type: application/json" \ -d '{"query":"product launch timeline","namespace":"default","limit":5}' # Multi-signal fusion search (best accuracy) curl -X POST https://remlabs.ai/v1/memory-rrf \ -H "Authorization: Bearer sk-rem-..." \ -H "Content-Type: application/json" \ -d '{"query":"what did we decide about the SDK","limit":10}' # Add knowledge triple curl -X POST https://remlabs.ai/v1/knowledge/add \ -H "Authorization: Bearer sk-rem-..." \ -H "Content-Type: application/json" \ -d '{"subject":"SDK","predicate":"launches_in","object":"June 2026","confidence":0.9}' # Run Memory Synthesis curl -X POST https://remlabs.ai/v1/dream/run \ -H "Authorization: Bearer sk-rem-..." \ -H "Content-Type: application/json" \ -d '{"topic":"product strategy","strategies":["synthesize","forecast"]}'
LangChain Integration
from langchain.memory import ConversationBufferMemory from remlabs.integrations.langchain import RemLabsMemory memory = RemLabsMemory( api_key="sk-rem-...", namespace="langchain-agent", search_type="rrf" # uses multi-signal fusion for best recall ) # Use as drop-in LangChain memory chain = ConversationChain(llm=llm, memory=memory)
CrewAI Integration
from remlabs.integrations.crewai import RemLabsCrewMemory # Each crew member gets its own namespace memory = RemLabsCrewMemory( api_key="sk-rem-...", crew_namespace="research-crew" ) # Agents share a multiplayer space for cross-agent memory
Vercel AI SDK
import { RemMemory } from '@remlabs/memory'; import { streamText } from 'ai'; const mem = new RemMemory({ apiKey: process.env.REM_API_KEY }); export async function POST(req: Request) { const { messages } = await req.json(); const lastMsg = messages[messages.length - 1].content; // Retrieve relevant context const context = await mem.search(lastMsg, { limit: 5 }); // Inject into system prompt const result = await streamText({ model: openai('gpt-4o'), system: `You have memory:\n${context.results.map(r => r.value).join('\n')}`, messages }); // Store the exchange await mem.set(`msg_${Date.now()}`, lastMsg, { tags: ['conversation'] }); return result.toDataStreamResponse(); }
Obsidian Integration
Full bidirectional sync between your Obsidian vault and REM Labs. 7 backend endpoints power import, export, change detection, and search across your entire vault. Notes, wikilinks, tags, and frontmatter are extracted into the REM knowledge graph automatically.
Import: Obsidian → REM
Sync your vault to REM. Each note's title, content, path, tags, wikilinks, and frontmatter are extracted and stored as structured memories with full knowledge graph integration.
| Parameter | Type | Description |
|---|---|---|
| notes | array required | Array of note objects (up to 200 per request) |
| notes[].title | string required | Note title (filename without .md) |
| notes[].content | string required | Full markdown content of the note |
| notes[].path | string | Vault-relative path (e.g. Projects/launch-plan.md) |
| notes[].tags | string[] | Tags from the note (e.g. ["project", "launch"]) |
| notes[].links | string[] | Wikilinks referenced in the note (e.g. ["Meeting Notes", "Q2 Goals"]) |
| notes[].frontmatter | object | YAML frontmatter as key-value pairs. Preserved as memory metadata. |
# Import notes from your Obsidian vault curl -X POST https://remlabs.ai/v1/memory/sync/obsidian/import \ -H "Authorization: Bearer sk-rem-..." \ -H "Content-Type: application/json" \ -d '{ "notes": [ { "title": "Launch Plan", "content": "# Launch Plan\nShip SDK by June...", "path": "Projects/launch-plan.md", "tags": ["project", "launch"], "links": ["Meeting Notes", "Q2 Goals"], "frontmatter": { "status": "active", "created": "2026-03-15" } } ] }'
{
"imported": 1,
"skipped": 0,
"errors": [],
"namespace": "obsidian"
}
Export: REM → Obsidian
Generate Obsidian-compatible markdown files from all your memories. Frontmatter, tags, and wikilinks are reconstructed so the export drops straight into your vault.
# Export all memories as Obsidian markdown curl -X POST https://remlabs.ai/v1/memory/sync/obsidian/export \ -H "Authorization: Bearer sk-rem-..." \ -H "Content-Type: application/json" \ -d '{"namespace":"obsidian"}'
Sync Status
Check the current state of your Obsidian sync -- last sync time, notes imported, pending changes, and connection health.
curl https://remlabs.ai/v1/integrations/obsidian/status \
-H "Authorization: Bearer sk-rem-..."
Search within Obsidian
Search memories scoped to your Obsidian namespace. Uses the same multi-signal fusion engine as the core search API.
curl -X POST https://remlabs.ai/v1/integrations/obsidian/search \ -H "Authorization: Bearer sk-rem-..." \ -H "Content-Type: application/json" \ -d '{"query":"product launch timeline","limit":10}'
Bidirectional Sync
Changes are detected via SHA-256 content hashing. When you sync, only notes that have actually changed are sent -- unchanged content is skipped automatically. This keeps syncs fast even for large vaults.
Obsidian Plugin
Install the REM Labs plugin directly in Obsidian for one-click vault sync.
- Install -- search "REM Labs" in Obsidian Community Plugins, or download from
- Configure -- paste your API key in the plugin settings
- "Sync Vault" command -- syncs all notes to REM. Run from the command palette (
Ctrl/Cmd+P→ "REM: Sync Vault") - Knowledge graph extraction -- tags and
[[wikilinks]]are extracted into REM's knowledge graph as entities and relationships - Smart change detection -- unchanged notes are skipped automatically via SHA-256 hashing, so repeated syncs are fast
| Endpoint | Method | Description |
|---|---|---|
/v1/memory/sync/obsidian/import | POST | Import notes from Obsidian vault (up to 200 per request) |
/v1/memory/sync/obsidian/export | POST | Export memories as Obsidian-compatible markdown |
/v1/integrations/obsidian/status | GET | Check sync status and connection health |
/v1/integrations/obsidian/search | POST | Search within Obsidian namespace |
Rate Limits
| Tier | Requests/min | Memory ops/min | Burst |
|---|---|---|---|
| Free | 60 | 30 | 10 concurrent |
| Pro | 600 | 300 | 50 concurrent |
| Scale | 1,000 | 500 | 100 concurrent |
| Business | 5,000 | 2,500 | 150 concurrent |
| Enterprise | 10,000 | 5,000 | 200 concurrent |
Rate limit headers are included in every response:
X-RateLimit-Limit-- max requests per windowX-RateLimit-Remaining-- remaining requestsX-RateLimit-Reset-- window reset timestamp (Unix seconds)
Idempotency-Key header with any POST request
to safely retry without duplicating side effects. Cached for 5 minutes.
Error Codes
HTTP Status Codes
| Status | Meaning | When it happens |
|---|---|---|
| 400 | Bad Request | Missing required fields, invalid JSON, malformed parameters |
| 401 | Unauthorized | Missing or invalid API key, expired session |
| 403 | Forbidden | Insufficient permissions (locked memory, read-only role, wrong namespace) |
| 404 | Not Found | Unknown endpoint or resource does not exist |
| 429 | Rate Limited | Too many requests -- check Retry-After header for when to retry |
| 500 | Internal Error | Unexpected server error -- contact dev@remlabs.ai |
Application Error Codes
| Code | HTTP Status | Description |
|---|---|---|
| auth_required | 401 | Missing or invalid API key |
| insufficient_credits | 402 | Not enough credits for this operation |
| rate_limited | 429 | Too many requests -- retry after the window resets |
| missing_required_field | 400 | A required parameter is missing (see required field) |
| api_not_found | 404 | Unknown endpoint slug (see suggestions field for similar) |
| no_handler | 501 | Endpoint exists but handler is not implemented |
| memory_protected | 403 | Cannot delete a locked memory -- unlock it first |
| memory_session_required | 401 | Memory 2FA is enabled -- create and verify a session first |
| space_not_found | 404 | Shared memory space does not exist |
| not_a_member | 403 | You are not a member of this shared space |
| read_only | 403 | Viewers cannot write to shared spaces |
| internal_error | 500 | Unexpected server error -- contact support |
All errors follow the format:
{
"error": {
"code": "auth_required",
"message": "Set Authorization: Bearer <key>",
"demo_key": "sk-rem-demo-key-12345678",
"signup": "POST /v1/auth/signup"
}
}
Bulk Import
Import multiple memories in a single request. Accepts an array of memory objects.
Each item follows the same schema as memory-set. Up to 1,000 items per batch.
{
"memories": [
{ "key": "pref_1", "value": "Prefers dark mode", "tags": ["preferences"] },
{ "key": "pref_2", "value": "Uses TypeScript", "tags": ["preferences"] },
{ "key": "meeting_1", "value": "Launch SDK by June", "tags": ["work"] }
],
"namespace": "default"
}
Returns a summary with imported, merged, and failed counts.
Each item is processed with the same indexing pipeline as individual memory-set calls
(FTS, embeddings, entity extraction).
POST /v1/memory/upload endpoint also accepts arrays for file-based imports.
Memory Export
Export all memories from a namespace as a JSON or CSV snapshot. Useful for backups, migration, and GDPR data portability requests.
| Parameter | Type | Description |
|---|---|---|
| namespace | string default: "default" | Namespace to export |
| format | string default: "json" | Export format: json or csv |
Returns the full namespace contents including keys, values, tags, metadata, and timestamps.
Multi-Tenancy
Use namespaces to isolate memories per user, agent, or project. Each API key automatically gets a unique namespace prefix. Create sub-namespaces for per-user isolation: namespace: 'user_42'
- API key isolation: Data is partitioned by API key hash at the database level. No cross-key access is possible.
- Per-user scoping: Pass
namespace: 'user_42'(or any user identifier) to isolate memories per end-user. This is how you build multi-user apps -- each user's memories are invisible to others within the same API key. - Namespace isolation: Within a key, namespaces provide logical separation. Searches, lists, and stats are scoped to a single namespace.
- Cross-namespace access: Use
namespace: "*"on search endpoints to search across all namespaces within your key. - Multiplayer spaces: For controlled cross-key sharing, use shared spaces with role-based access.
// Scope memories per end-user await rem.store({ key: 'pref', value: 'Prefers dark mode', namespace: 'user_42' }) await rem.recall({ query: 'preferences', namespace: 'user_42' }) // Each user is fully isolated await rem.store({ key: 'pref', value: 'Prefers light mode', namespace: 'user_99' }) // user_42 never sees user_99's memories
Self-Hosting
REM Labs is fully self-hostable. The server runs on Node.js with embedded storage for zero-dependency persistence.
Docker deployment
Contact us for your Docker image access.
Enterprise customers receive Docker images, Kubernetes Helm charts, and deployment guides. Email dev@remlabs.ai to get started.
Environment variables
| Variable | Required | Description |
|---|---|---|
| PORT | No | Server port (default: 3000) |
| DB_PATH | No | Database path (default: .data/remlabs.db) |
| EMBEDDING_API_KEY | No | Embedding provider key for semantic search (recommended for best recall) |
| LLM_API_KEY | No | LLM provider key for AI-powered features |
| EMAIL_API_KEY | No | For email notifications and Memory 2FA |
| INTERNAL_SECRET | No | JWT signing secret (auto-generated if missing) |
EMBEDDING_API_KEY for best semantic search quality.
Without an embedding provider, semantic search falls back to keyword scoring only.
Full-text search and entity extraction work without any API keys.
Docker & Kubernetes
Enterprise customers receive pre-built Docker images and Kubernetes Helm charts. The server is designed for single-process deployment with SQLite WAL mode for efficient concurrent reads. For high-write workloads, mount a persistent volume at DB_PATH.
Contact dev@remlabs.ai for Docker image access, Helm charts, and deployment guides.
Hybrid Memory (Local + Cloud)
Run REM locally on your Mac, PC, or phone for instant access. The cloud syncs automatically for backup and anywhere-access. Your memories live where you need them.
Setup
1. Install locally:
curl -fsSL https://get.remlabs.ai | sh
2. Configure cloud sync:
Set these variables in your .env file:
REM_CLOUD_URL=https://remlabs.ai REM_API_KEY=your-key
3. Sync starts automatically. Local memories push to cloud, cloud memories pull to local. No cron jobs or manual triggers needed.
Sync API
| Endpoint | Method | Description |
|---|---|---|
/v1/sync/status | GET | Check sync state (last sync time, pending count, connection health) |
/v1/sync/push | POST | Push local memories to cloud |
/v1/sync/pull | POST | Pull cloud memories to local. Pass since timestamp to fetch only recent changes. |
/v1/sync/diff | GET | See what changed since last sync |
Conflict resolution
When the same key is updated on both local and cloud, the latest timestamp wins. Both versions are preserved in memory history for auditing.
Phone access
Install REM as a PWA on your phone (Add to Home Screen from Safari/Chrome). Access your memories from anywhere, even when self-hosted — the cloud layer provides the bridge.
Migrate from Competitors
Already using another memory provider? The REM CLI can import your entire memory corpus in one command. All migrations are non-destructive — your data in the source system is never modified or deleted.
From Mem0
Exports all memories from your Mem0 account and imports them into REM Labs with full metadata, tags, and timestamps preserved. Handles both v1 and v2 Mem0 schemas automatically.
rem migrate --from=mem0 --api-key=YOUR_MEM0_KEY
From Zep
Exports conversations and extracted facts from your Zep instance. Supports both Zep Cloud and self-hosted deployments. Conversations are mapped to REM memory entries with source attribution.
rem migrate --from=zep --url=YOUR_ZEP_URL
From Letta
Exports agent memories from your Letta (formerly MemGPT) server. Pulls both core and archival memory blocks and converts them into searchable REM memories.
rem migrate --from=letta --server=YOUR_LETTA_URL
rem migrate --help for all options, or email dev@remlabs.ai — migration tool source available in private beta.
Changelog
- 8 parallel retrieval modes with neural reranking and multi-signal fusion
- Memory Synthesis with 9 consolidation strategies
- Knowledge Graph with entity extraction and graph walks
- Multiplayer memory with shared namespaces and RBAC
- Node.js SDK, Python SDK, CLI with device-code auth
- 40+ integrations: ChatGPT, Claude, LangChain, CrewAI, Obsidian, Slack, Notion
- Self-hostable via Docker
Questions? Join the community or email dev@remlabs.ai.
API status: remlabs.ai/v1/health
Get started with the AI memory API in under 5 minutes. Memory API for chatbots, agents, and any AI application.
The continuity layer, specified.
Every endpoint, every stage, every number. Same system that delivers +15.33pp on SWE-bench Lite (n=150, p<0.05) and 94.6% on LongMemEval.
The /ask pipeline — 6 stages
Storage
Memory lifecycle
- Importance scoring — 0–1 at write using named entities, dates, numbers, proper nouns.
- Time-based decay —
effective_importance = importance * (0.95 ^ days_since_update). - Deduplication — Jaccard overlap > 0.85 merges into the canonical memory.
- Archive / prune — below threshold = archived (queryable with
include_archived=true), never deleted. - Temporal versioning — every fact has
valid_from/valid_until; point-in-time queries walk the chain.
Dream Engine — 9 strategies
Background consolidation. Strategies: synthesize, pattern_extract, insight_generate, compress, associate, validate, evolve, forecast, reflect. Tournament refinement rejects slop. forecast + evolve at >60% confidence become procedural skills surfaced in /explore#skills.
For the full architecture spec including federation, reactivity, and complete API surface, see /docs#api-reference or the API page.