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 }) — write
  • rem.recall({ query, namespace, limit }) — read by query
  • rem.dream({ strategies }) — consolidation (9 strategies)
  • rem.list({ namespace, limit }) — enumerate
  • rem.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

bash
curl -X POST https://remlabs.ai/v1/keys
# Returns: {"key": "sk-rem-abc123...", "balance": 0}

Step 2: Store your first memory

bash
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

bash
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)

javascript
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

javascript
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
All integrations included at every tier. Free, Pro, Scale, Enterprise -- you get access to every platform listed above. No add-on pricing.

Quick Start

Three lines to your first memory. No signup form -- generate a key instantly.

1. Get an API key

bash
curl -X POST https://remlabs.ai/v1/keys

Returns {"key": "sk-rem-abc123...", "balance": 0}. Free tier -- no credit card.

2. Store a memory

bash
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

bash
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"}'
Tip: Memory-set is free. Every stored memory is automatically indexed in full-text search, semantic embeddings, and entity extraction -- all at write time.

Authentication

All authenticated endpoints require a Bearer token in the Authorization header. You can also pass the key via the X-API-Key header.

http
# 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

POST /v1/keys

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 as X-Memory-Session header

Base URL

text
https://remlabs.ai/v1/

All endpoints are prefixed with /v1/. The API is also available at https://remlabs.ai/v1/ (proxied) .

API Versioning: The API is versioned via URL path (/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.
Interactive Playground: Try any endpoint live at remlabs.ai/playground — no signup required for demo mode.
SDK methods vs REST endpoints — The SDK uses friendly method names that map to canonical REST endpoints:
SDK MethodREST EndpointDescription
rem.store({ key, value, namespace })POST /v1/memory/setWrite a memory
rem.recall({ query, namespace, limit })POST /v1/memory/searchRead by natural-language query
rem.dream({ strategies })POST /v1/dream/startRun consolidation
rem.list({ namespace, limit })GET /v1/memory/listEnumerate memories
rem.forget({ key })POST /v1/memory/deleteDelete 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.

POST /v1/memory-set
ParameterTypeDescription
keystring requiredUnique identifier for this memory within the namespace
valuestring | objectThe memory content. Strings and JSON objects are both accepted. Automatically compressed above 512 bytes.
namespacestring default: "default"Logical grouping for memories. Use namespaces to isolate users, projects, or agents.
tagsstring[] | stringTags for filtering. Array of strings or comma-separated string.
ttl_secondsnumber optionalTime-to-live in seconds. Memory auto-expires after this duration. 0 or omit for permanent.
ttlnumber optionalAlias for ttl_seconds. Time-to-live in seconds. Memory auto-expires after this duration. 0 = permanent (default).
typestring optionalMemory 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.
Size limits: Maximum value size: 100KB per memory. For larger documents, chunk into multiple memories. The API auto-chunks content over 2KB into separate indexed entries.
json response
{
  "key": "user_pref",
  "namespace": "default",
  "status": "stored",
  "version": 1,
  "type": "project",
  "importance": 0.45,
  "facts_extracted": 2
}
Deduplication: If a memory with high keyword overlap (>85% Jaccard) already exists, the new value is merged into the existing key instead of creating a duplicate. The response will include "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.

json
{
  "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.

POST /v1/memory-get
ParameterTypeDescription
keystring requiredThe exact key to retrieve
namespacestring default: "default"Namespace to look in
json response
{
  "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.

POST /v1/memory/ask
6-stage pipeline:
  1. Fact lookup (knowledge graph)
  2. Multi-query expansion
  3. Vector search (chunked, HNSW)
  4. Keyword fallback (FTS5 AND-first)
  5. Full-context fallback
  6. LLM reranking
ParameterTypeDescription
questionstringNatural-language question. Not a search query — an actual question.
namespacestring default: "default"Namespace to search within. Use * to span all namespaces the key can read.
json response
{
  "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.

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.

POST /v1/memory-search
ParameterTypeDescription
querystringNatural language search query. Empty query returns all memories.
namespacestring default: "default"Namespace to search within
limitnumber default: 50Maximum results to return
offsetnumber default: 0Pagination offset. Use with limit to paginate through large result sets.
typestring optionalFilter by type: user, feedback, project, reference
tagsstring[] optionalFilter results to memories with any of these tags
sourcestring optionalFilter by source (e.g., slack, obsidian, chatgpt)
date_fromstring optionalOnly return memories created after this date (ISO 8601 or YYYY-MM-DD)
date_tostring optionalOnly return memories created before this date (ISO 8601 or YYYY-MM-DD)
min_scorenumber default: 0Minimum retrieval confidence threshold (0.0 to 1.0). Only return results above this score.
Confidence scoring: Results include a 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.
json response
{
  "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.

Why empty results are a feature: Unlike systems that always return something (even when irrelevant), REM Labs practices honest retrieval. An empty result set means "no confident match" -- your application can handle this gracefully rather than presenting hallucinated context to the LLM.
Pagination: For large result sets, use 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.

POST /v1/memory-delete
ParameterTypeDescription
keystring requiredKey to delete
namespacestring default: "default"Namespace containing the key
json response
{
  "deleted": true,
  "key": "user_pref",
  "namespace": "default"
}

POST /v1/memory-list

List all keys in a namespace with optional tag filter and pagination.

POST /v1/memory-list
ParameterTypeDescription
namespacestring default: "default"Namespace to list
tagstring optionalFilter by tag
include_metaboolean default: falseInclude size, tags, timestamps per key
limitnumber default: 100Max keys to return
offsetnumber default: 0Pagination offset

POST /v1/memory-stats

Get namespace analytics: total count, size in bytes, oldest and newest timestamps, TTL count.

POST /v1/memory-stats
ParameterTypeDescription
namespacestring 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.

GET /v1/memory/health
ParameterTypeDescription
namespacestring default: "default"Namespace to check
json response
{
  "total_memories": 1247,
  "fresh_7d": 89,
  "stale_30d": 234,
  "consistency_score": 78,
  "health": "needs_attention"
}
Health values: "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.

GET /v1/memory/conflicts
ParameterTypeDescription
namespacestring default: "default"Namespace to scan for conflicts
json response
{
  "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.

POST /v1/memory/bulk-delete
ParameterTypeDescription
namespacestring default: "default"Target namespace
patternstring optionalDelete memories where key or value matches this pattern
tagstring optionalDelete memories containing this tag
older_than_daysnumber optionalDelete memories not updated in N days
json response
{
  "ok": true,
  "deleted": 47
}

Search using semantic embeddings with cosine similarity. Embeddings are generated automatically on memory-set.

POST /v1/memory-search-semantic
ParameterTypeDescription
querystring requiredNatural language query -- embedded at query time
namespacestring default: "default"Namespace to search
limitnumber default: 10Max results
min_scorenumber default: 0Minimum relevance threshold

Full-text search with stemming and Unicode tokenization. Best for exact term matching where embeddings may miss. Automatically maintained as a write-through index.

POST /v1/memory-fts5-search
ParameterTypeDescription
querystring requiredSearch terms. Precision-first with intelligent query expansion.
namespacestring default: "default"Namespace to search
limitnumber default: 20Max results

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.

POST /v1/memory-search-temporal
ParameterTypeDescription
querystring optionalText query to filter
namespacestring default: "default"Namespace to search
before_datestring optionalOnly memories from before this date (YYYY-MM-DD)
after_datestring optionalOnly memories from after this date
as_ofstring optionalReturn facts that were true as of this date
limitnumber default: 50Max results

Find memories by one or more tags.

POST /v1/memory-tag-search
ParameterTypeDescription
tagsstring[]Array of tags to match (OR logic)
tagstringSingle tag shortcut (alternative to tags array)
namespacestring default: "default"Namespace to search
limitnumber default: 100Max results

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.

POST /v1/memory-fact-search
ParameterTypeDescription
querystring requiredSearch relations and values (e.g., "education", "location")
namespacestring default: "default"Namespace to search
limitnumber default: 50Max results
Auto-extracted relations: education, school, occupation, employer, location, commute, pet_type, pet_name, previous_location, hobby, preference, family, and more. Entity extraction runs on every 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.

POST /v1/memory-rrf
ParameterTypeDescription
querystring requiredNatural language query
namespacestring default: "default"Namespace to search
limitnumber default: 10Max results
Best practice: Use 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

POST /v1/memory/import-ai
json
{
  "source": "chatgpt",
  "data": // Your conversations.json from ChatGPT export,
  "namespace": "imported"
}

Claude Export

json
{
  "source": "claude",
  "data": // Your Claude conversation export,
  "namespace": "imported"
}

File Upload (Drag-and-Drop)

POST /v1/memory/upload

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

POST /v1/dream/run
ParameterTypeDescription
topicstring optionalFocus the dream on a specific topic
namespacestring default: "default"Namespace to dream about
strategiesstring[] optionalChoose specific strategies (see below)

9 Dream Strategies

StrategyWhat it does
synthesizeMerge related memories into coherent summaries
pattern_extractDiscover recurring patterns across memories
insight_generateSurface non-obvious connections and insights
compressReduce redundancy while preserving key information
associateBuild cross-domain links between distant memories
validateCheck consistency and flag contradictions
evolveUpdate outdated memories with newer information
forecastGenerate predictions based on memory trends
reflectMeta-cognitive analysis of memory quality and gaps

Additional Dream endpoints:

  • POST /v1/dream/subscribe -- schedule recurring dreams on a cron
  • GET /v1/dream/review -- review pending dream suggestions
  • POST /v1/dream/deploy -- apply dream suggestions to memory
  • GET /v1/dream/insights -- browse generated insights
  • POST /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

POST /v1/memory/share/create
ParameterTypeDescription
namestring requiredSpace name
descriptionstring optionalSpace description

Roles

RoleReadWriteInvite
ownerYesYesYes
adminYesYesYes
developerYesYesNo
viewerYesNoNo

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 to
  • GET /v1/memory/share/:space_id -- space info
  • GET /v1/memory/share/members/:space_id -- list members
  • GET /v1/memory/share/activity/:space_id -- activity log
  • POST /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 collaborators
  • DELETE /v1/memory/collaborator/:id -- revoke access
  • GET /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

POST /v1/knowledge/add
json
{
  "subject": "TypeScript",
  "predicate": "is_preferred_by",
  "object": "user",
  "confidence": 0.95
}

Query the graph

POST /v1/knowledge/query

Filter by any combination of subject, predicate, object, or free-text query.

Graph Walk

POST /v1/knowledge/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)

POST /v1/memory/graph-query

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 triple
  • GET /v1/knowledge/stats -- graph statistics (triple count, unique subjects, top predicates)
  • GET /v1/knowledge/connections/:entity -- all connections for an entity
  • POST /v1/knowledge/path -- find shortest path between two entities
  • POST /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

POST /v1/webhooks/inbox/:key_prefix

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

GET /v1/webhooks/inbox

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.

POST /v1/webhooks/register
json
{
  "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 stored
  • memory.updated -- fired when an existing memory is overwritten (upsert)
  • memory.deleted -- fired when a memory is deleted
  • dream.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

bash
npm install @remlabs/memory
# private beta -- request access in Discord
javascript
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

bash
pip install remlabs-memory
python
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

bash
# 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

python
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

python
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

typescript
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.

POST /v1/memory/sync/obsidian/import
ParameterTypeDescription
notesarray requiredArray of note objects (up to 200 per request)
notes[].titlestring requiredNote title (filename without .md)
notes[].contentstring requiredFull markdown content of the note
notes[].pathstringVault-relative path (e.g. Projects/launch-plan.md)
notes[].tagsstring[]Tags from the note (e.g. ["project", "launch"])
notes[].linksstring[]Wikilinks referenced in the note (e.g. ["Meeting Notes", "Q2 Goals"])
notes[].frontmatterobjectYAML frontmatter as key-value pairs. Preserved as memory metadata.
bash
# 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" }
    }
  ]
}'
json response
{
  "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.

POST /v1/memory/sync/obsidian/export
bash
# 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.

GET /v1/integrations/obsidian/status
bash
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.

POST /v1/integrations/obsidian/search
bash
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
Large vaults: The import endpoint accepts up to 200 notes per request. For vaults with more than 200 notes, the plugin batches automatically. You can also use the Import UI to drag-and-drop your entire vault folder.
EndpointMethodDescription
/v1/memory/sync/obsidian/importPOSTImport notes from Obsidian vault (up to 200 per request)
/v1/memory/sync/obsidian/exportPOSTExport memories as Obsidian-compatible markdown
/v1/integrations/obsidian/statusGETCheck sync status and connection health
/v1/integrations/obsidian/searchPOSTSearch within Obsidian namespace

Rate Limits

TierRequests/minMemory ops/minBurst
Free603010 concurrent
Pro60030050 concurrent
Scale1,000500100 concurrent
Business5,0002,500150 concurrent
Enterprise10,0005,000200 concurrent

Rate limit headers are included in every response:

  • X-RateLimit-Limit -- max requests per window
  • X-RateLimit-Remaining -- remaining requests
  • X-RateLimit-Reset -- window reset timestamp (Unix seconds)
Idempotency: Include an Idempotency-Key header with any POST request to safely retry without duplicating side effects. Cached for 5 minutes.

Error Codes

HTTP Status Codes

StatusMeaningWhen it happens
400Bad RequestMissing required fields, invalid JSON, malformed parameters
401UnauthorizedMissing or invalid API key, expired session
403ForbiddenInsufficient permissions (locked memory, read-only role, wrong namespace)
404Not FoundUnknown endpoint or resource does not exist
429Rate LimitedToo many requests -- check Retry-After header for when to retry
500Internal ErrorUnexpected server error -- contact dev@remlabs.ai

Application Error Codes

CodeHTTP StatusDescription
auth_required401Missing or invalid API key
insufficient_credits402Not enough credits for this operation
rate_limited429Too many requests -- retry after the window resets
missing_required_field400A required parameter is missing (see required field)
api_not_found404Unknown endpoint slug (see suggestions field for similar)
no_handler501Endpoint exists but handler is not implemented
memory_protected403Cannot delete a locked memory -- unlock it first
memory_session_required401Memory 2FA is enabled -- create and verify a session first
space_not_found404Shared memory space does not exist
not_a_member403You are not a member of this shared space
read_only403Viewers cannot write to shared spaces
internal_error500Unexpected server error -- contact support

All errors follow the format:

json
{
  "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.

POST /v1/memory/import
json
{
  "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).

Batch sizing: For bulk imports, send up to 1,000 items per request. For larger datasets, batch into sequential requests. The 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.

POST /v1/memory/snapshot
ParameterTypeDescription
namespacestring default: "default"Namespace to export
formatstring 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.
javascript
// 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

Self-hosting is available for enterprise customers. Contact dev@remlabs.ai for deployment support.

REM Labs is fully self-hostable. The server runs on Node.js with embedded storage for zero-dependency persistence.

Docker deployment

bash
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

VariableRequiredDescription
PORTNoServer port (default: 3000)
DB_PATHNoDatabase path (default: .data/remlabs.db)
EMBEDDING_API_KEYNoEmbedding provider key for semantic search (recommended for best recall)
LLM_API_KEYNoLLM provider key for AI-powered features
EMAIL_API_KEYNoFor email notifications and Memory 2FA
INTERNAL_SECRETNoJWT signing secret (auto-generated if missing)
Production checklist: Set 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:

bash
curl -fsSL https://get.remlabs.ai | sh

2. Configure cloud sync:

Set these variables in your .env file:

env
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

EndpointMethodDescription
/v1/sync/statusGETCheck sync state (last sync time, pending count, connection health)
/v1/sync/pushPOSTPush local memories to cloud
/v1/sync/pullPOSTPull cloud memories to local. Pass since timestamp to fetch only recent changes.
/v1/sync/diffGETSee 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.

bash
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.

bash
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.

bash
rem migrate --from=letta --server=YOUR_LETTA_URL
Need help migrating? Run rem migrate --help for all options, or email dev@remlabs.ai — migration tool source available in private beta.

Changelog

v1.0 April 2026 Launch
  • 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.

Full API Reference · Interactive Playground · AI Agent Memory Use Cases · Pricing · LongMemEval Benchmark · Enterprise · Blog
Architecture · v4

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

Stage 1
Fact lookup
Direct knowledge-graph query against extracted (subject, relation, value) triples.
Stage 2
Multi-query expansion
2–3 search variants with synonym substitution and paraphrase.
Stage 3
Vector search
Cosine similarity on 256-d chunked embeddings; chunks map back to parents.
Stage 4
Keyword fallback
FTS5 AND-first — the lever that brought us to 94.6%.
Stage 5
Full-context fallback
Low-confidence trigger — load the entire namespace and let the LLM find it.
Stage 6
LLM rerank
Best-effort — the 94.6% number does not depend on it.

Storage

SQLite + WAL
Single-file durable store with concurrent readers. Managed Postgres available as drop-in.
256-d vectors (text-embedding-3-small)
600-char overlapping chunks, parent-aggregated scoring.
FTS5 AND-first
Porter stemming. Every query term must be present (precision-biased), with OR-fallback after.
Knowledge-graph triples
Regex-based fact extraction at ingest. (subject, relation, value) tuples drive Stage 1.

Memory lifecycle

  1. Importance scoring — 0–1 at write using named entities, dates, numbers, proper nouns.
  2. Time-based decayeffective_importance = importance * (0.95 ^ days_since_update).
  3. Deduplication — Jaccard overlap > 0.85 merges into the canonical memory.
  4. Archive / prune — below threshold = archived (queryable with include_archived=true), never deleted.
  5. 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.