@usememra/sdk
v4.5.0
Published
Official TypeScript SDK for the Memra memory API
Readme
@usememra/sdk
Official TypeScript SDK for the Memra memory API — persistent, searchable, privacy-first memory for AI agents and LLM applications. EU-native, hosted in Helsinki.
Zero dependencies. Works everywhere fetch does: Node.js 18+, Deno, Bun, Cloudflare Workers, Vercel Edge.
npm install @usememra/sdkQuickstart
import { MemraClient } from '@usememra/sdk';
const client = new MemraClient({ apiKey: 'memra_live_xxx' });
// Remember something
const memory = await client.memories.add({
content: 'User prefers dark mode and TypeScript',
tenantId: 'user_123', // your end-user or agent
projectId: 'proj_1', // your app
});
// Recall it later — semantic search, not keyword matching
const results = await client.memories.recall({
query: 'what UI settings does the user like?',
tenantId: 'user_123',
projectId: 'proj_1',
});
console.log(results.data[0].content); // 'User prefers dark mode and TypeScript'Configuration
const client = new MemraClient({
apiKey: 'memra_live_xxx', // Required -- your Memra API key
baseUrl: 'https://usememra.com/api/v1', // Optional -- defaults to production API
timeout: 10000, // Optional -- per-request timeout in ms (default 10s)
maxRetries: 3, // Optional -- retries for 429/5xx/network errors (0 disables)
});The SDK retries 429 and 5xx responses automatically, honoring the server's Retry-After header, and attaches an Idempotency-Key to every mutating request so a retried write never creates a duplicate.
Read-your-writes
Every write returns a revision token. Pass it to recall and the search is guaranteed to see that write — no more "I just saved that, why can't I find it?" races in agent loops:
const memory = await client.memories.add({
content: 'Deploy target changed to eu-north-1',
tenantId: 'agent_7',
projectId: 'proj_1',
});
const results = await client.memories.recall({
query: 'where do we deploy?',
tenantId: 'agent_7',
projectId: 'proj_1',
waitForRevision: memory.revision, // blocks until this write is indexed
});Writes also report embeddingStatus ('pending' | 'complete' | 'failed') so you know where the async embedding pipeline stands.
Contradiction detection
When a new memory contradicts something already stored, the create response tells you — with the conflicting memory's ID, a preview, and a confidence score:
const memory = await client.memories.add({
content: 'User switched from VS Code to Zed',
tenantId: 'user_123',
projectId: 'proj_1',
});
for (const conflict of memory.conflicts ?? []) {
console.log(`Conflicts with ${conflict.memoryId} (${conflict.confidence}): ${conflict.preview}`);
// Resolve it: supersede keeps the audit trail and retires the stale fact
await client.memories.supersede(conflict.memoryId, 'User now uses Zed');
}Token-budget recall
Building an LLM prompt with a fixed context budget? Ask Memra to fit results into it. The server packs (and if needed compresses) the most relevant memories to stay under maxTokens:
const results = await client.memories.recall({
query: 'everything relevant to this support ticket',
tenantId: 'user_123',
projectId: 'proj_1',
maxTokens: 800,
});
console.log(results.meta.tokenBudget); // 800
console.log(results.meta.tokensUsed); // e.g. 762
results.data.forEach((m) => {
if (m.contentIsCompressed) {
// content was summarized to fit the budget
}
});Staleness-aware results
Every recalled memory carries freshness signals, so agents can decide what to trust:
for (const m of results.data) {
console.log(m.stalenessScore); // 0 (fresh) .. 100 (critical)
console.log(m.stalenessStatus); // 'fresh' | 'aging' | 'stale' | 'critical'
console.log(m.lastConfirmed); // ISO timestamp or null
}Feedback loop
Tell Memra which recalled memories your agent actually used. Used memories rank higher next time and their staleness clocks reset:
// Option A: dedicated endpoint after the fact
await client.memories.feedback({
tenantId: 'user_123',
projectId: 'proj_1',
memoryIds: usedInPrompt.map((m) => m.id),
});
// Option B: inline on the next recall
await client.memories.recall({
query: 'follow-up question',
tenantId: 'user_123',
projectId: 'proj_1',
usedIds: usedInPrompt.map((m) => m.id),
});Entity graph
Memra extracts entities (people, orgs, tools, places) from memories as they're written. Query the graph to see what a namespace knows about, then pivot into the memories that mention an entity:
// Who/what does this user's memory know about?
const { entities } = await client.entities.list({
tenantId: 'user_123',
projectId: 'proj_1',
entityType: 'org', // optional filter
});
// [{ name: 'Acme Corp', type: 'org', isPii: false, memoryCount: 12 }, ...]
// All memories mentioning an entity (metadata; fetch content via memories.get)
const { memories, total } = await client.entities.memories('Acme Corp', {
tenantId: 'user_123',
projectId: 'proj_1',
});PII entities are surfaced under stable IDs — never raw values.
Filtering recall
await client.memories.recall({
query: 'open action items',
tenantId: 'user_123',
projectId: 'proj_1',
type: 'task', // memory type filter
minImportance: 5, // importance floor (0-10)
notTags: ['archived', 'done'], // exclude by tag
since: '2026-06-01T00:00:00Z', // created at/after
until: '2026-07-01T00:00:00Z', // created at/before
limit: 10,
});Resources
| Resource | Methods | Description |
|----------|---------|-------------|
| client.memories | add, list, get, update, delete, deleteTenant, batch, recall, feedback, supersede, chain, promote, refresh | Memory CRUD, semantic search, batch ops, feedback, trust/health |
| client.entities | list, memories | Entity graph reads |
| client.projects | create, list, get, delete | Project management |
| client.privacy | exportData, namespaceExport, createErasureRequest, getErasureRequest | Data export and erasure |
| client.usage | get | Account usage metrics |
More memory operations
// Batch create (up to 100)
const batch = await client.memories.batch([
{ content: 'Memory 1', tenantId: 'user_123', projectId: 'proj_1' },
{ content: 'Memory 2', tenantId: 'user_123', projectId: 'proj_1' },
]);
// Update, delete, list
await client.memories.update('mem_id', { importance: 9 });
await client.memories.delete('mem_id');
const list = await client.memories.list({ tenantId: 'user_123', limit: 20 });
// Supersession chain -- how a fact evolved over time
const chain = await client.memories.chain('mem_id');Privacy & data control
Memra is privacy-first and EU-native (all data stays on EU infrastructure). The SDK gives first-class access to export and erasure:
const exportData = await client.privacy.exportData(); // full account export
const nsData = await client.privacy.namespaceExport('tenant_123'); // per-tenant export
await client.privacy.createErasureRequest('mem_id'); // request erasure
await client.privacy.getErasureRequest('mem_id'); // check statusErasure is thorough: flat files, database index rows, Redis cache entries, and audit log entries are all purged.
Error handling
All API errors throw typed exceptions:
import { MemraAuthError, MemraNotFoundError, MemraQuotaError } from '@usememra/sdk';
try {
await client.memories.get('nonexistent_id');
} catch (error) {
if (error instanceof MemraNotFoundError) {
// 404 -- resource not found
} else if (error instanceof MemraQuotaError) {
// 429 -- rate limit or quota exceeded (already retried with Retry-After)
}
}| Error Class | HTTP Status | When |
|-------------|-------------|------|
| MemraAuthError | 401 | Invalid or missing API key |
| MemraNotFoundError | 404 | Resource not found |
| MemraValidationError | 422 | Invalid request parameters |
| MemraQuotaError | 429 | Rate limit or quota exceeded |
| MemraServerError | 5xx | Server error |
TypeScript
Full type definitions with IDE autocomplete, dual ESM + CJS builds:
import type { Memory, RecallResult, RecallParams, Entity, MemoryConflict } from '@usememra/sdk';Versioning
The SDK version tracks the Memra platform version: @usememra/sdk 4.5.x targets Memra API v4.5.
License
MIT
