mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-08-02 21:04:08 +00:00
Fixes across existing pages: stale claude-3-sonnet -> current models, deprecated ai/react + toAIStreamResponse -> current AI SDK APIs, five divergent search signatures unified to client.search.memories (v4) / client.search.documents (v3), singular containerTag in v4 contexts, from-zep containerTag type error, canonical processing-status enum, corrected 'v4 has no SDK' claim, internal links re-pointed at final destinations. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
409 lines
11 KiB
Text
409 lines
11 KiB
Text
---
|
||
title: "User Profiles"
|
||
sidebarTitle: "User Profiles"
|
||
description: "Fetch and use automatically maintained user context"
|
||
icon: "user"
|
||
---
|
||
|
||
User profiles are extremely short summaries of context about an entity (Usually a user, but can be anything) which includes both the *static* facts about them, as well as a few recent episodes.
|
||
|
||
> You can think of these as a dynamic compaction that's done by supermemory in real-time.
|
||
|
||
This profile should be injected into the agent context for truly personalized experiences. To read more, visit [User profiles - Concept](/concepts/user-profiles)
|
||
|
||
Get a user's profile — their static facts and dynamic context — with a single API call.
|
||
|
||
<Tip>
|
||
Profiles are built automatically as you [ingest content](/add-memories). No setup required.
|
||
</Tip>
|
||
|
||
## Quick Start
|
||
|
||
<Tabs>
|
||
<Tab title="TypeScript">
|
||
```typescript
|
||
import Supermemory from 'supermemory';
|
||
|
||
const client = new Supermemory();
|
||
|
||
const { profile } = await client.profile({
|
||
containerTag: "user_123"
|
||
});
|
||
|
||
console.log(profile.static); // Long-term facts
|
||
console.log(profile.dynamic); // Recent context
|
||
```
|
||
</Tab>
|
||
<Tab title="Python">
|
||
```python
|
||
from supermemory import Supermemory
|
||
|
||
client = Supermemory()
|
||
|
||
result = client.profile(container_tag="user_123")
|
||
|
||
print(result.profile.static) # Long-term facts
|
||
print(result.profile.dynamic) # Recent context
|
||
```
|
||
</Tab>
|
||
<Tab title="cURL">
|
||
```bash
|
||
curl -X POST "https://api.supermemory.ai/v4/profile" \
|
||
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"containerTag": "user_123"}'
|
||
```
|
||
</Tab>
|
||
</Tabs>
|
||
|
||
**Response:**
|
||
```json
|
||
{
|
||
"profile": {
|
||
"static": [
|
||
"User is a software engineer",
|
||
"User specializes in Python and React",
|
||
"User prefers dark mode interfaces"
|
||
],
|
||
"dynamic": [
|
||
"User is working on Project Alpha",
|
||
"User recently started learning Rust",
|
||
"User is debugging authentication issues"
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Profile + Search
|
||
|
||
Get profile and search results in one call by adding the `q` parameter:
|
||
|
||
<Tabs>
|
||
<Tab title="TypeScript">
|
||
```typescript
|
||
const result = await client.profile({
|
||
containerTag: "user_123",
|
||
q: "deployment errors"
|
||
});
|
||
|
||
// Profile data
|
||
const { static: facts, dynamic: context } = result.profile;
|
||
|
||
// Search results (only if q was provided)
|
||
const memories = result.searchResults?.results || [];
|
||
```
|
||
</Tab>
|
||
<Tab title="Python">
|
||
```python
|
||
result = client.profile(
|
||
container_tag="user_123",
|
||
q="deployment errors"
|
||
)
|
||
|
||
# Profile data
|
||
facts = result.profile.static
|
||
context = result.profile.dynamic
|
||
|
||
# Search results
|
||
memories = result.search_results.results if result.search_results else []
|
||
```
|
||
</Tab>
|
||
</Tabs>
|
||
|
||
---
|
||
|
||
## Parameters
|
||
|
||
| Parameter | Type | Required | Description |
|
||
|-----------|------|----------|-------------|
|
||
| `containerTag` | string | Yes | User/project identifier |
|
||
| `q` | string | No | Search query (includes search results in response) |
|
||
| `threshold` | 0-1 | No | Filter search results by relevance score |
|
||
| `filters` | object | No | Metadata filters applied to profile and search results |
|
||
| `include` | string[] | No | Sections to return — any of `"static"`, `"dynamic"`, `"buckets"`. Omit to return all |
|
||
| `buckets` | string[] | No | Restrict the `buckets` section to specific keys. Omit for all configured buckets |
|
||
|
||
---
|
||
|
||
## Building Prompts
|
||
|
||
The most common pattern — inject profile into your LLM's system prompt:
|
||
|
||
```typescript
|
||
async function chat(userId: string, message: string) {
|
||
const { profile } = await client.profile({ containerTag: userId });
|
||
|
||
const systemPrompt = `You are assisting a user.
|
||
|
||
ABOUT THE USER:
|
||
${profile.static?.join('\n') || 'No profile yet.'}
|
||
|
||
CURRENT CONTEXT:
|
||
${profile.dynamic?.join('\n') || 'No recent activity.'}
|
||
|
||
Personalize responses to their expertise and preferences.`;
|
||
|
||
return llm.chat({
|
||
messages: [
|
||
{ role: "system", content: systemPrompt },
|
||
{ role: "user", content: message }
|
||
]
|
||
});
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Full Context Pattern
|
||
|
||
Get profile + query-specific memories in one call:
|
||
|
||
```typescript
|
||
async function getContext(userId: string, query: string) {
|
||
const result = await client.profile({
|
||
containerTag: userId,
|
||
q: query,
|
||
threshold: 0.6
|
||
});
|
||
|
||
return `
|
||
User Background:
|
||
${result.profile.static.join('\n')}
|
||
|
||
Current Context:
|
||
${result.profile.dynamic.join('\n')}
|
||
|
||
Relevant Memories:
|
||
${result.searchResults?.results.map(m => m.memory).join('\n') || 'None'}
|
||
`;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Profile Buckets
|
||
|
||
Buckets are **custom topical categories** for a profile — an axis that sits alongside
|
||
`static` and `dynamic`. Where static/dynamic split facts by how long-lived they are,
|
||
buckets group them by subject (e.g. `preferences`, `goals`, `work`). As content is
|
||
ingested, a classifier assigns each memory to the buckets it matches, so you can pull
|
||
just the slice of context a given surface needs.
|
||
|
||
Every org starts with a built-in `preferences` bucket. You can define your own at the
|
||
organization or space level in your console settings; space-level buckets are
|
||
**add-only** — a container tag inherits all org buckets and may add more, but cannot
|
||
disable them.
|
||
|
||
### Requesting buckets
|
||
|
||
Pass `include: ["buckets"]` to return bucket-organized memories, and optionally
|
||
`buckets` to limit the response to specific keys. `include` also lets you skip
|
||
sections you don't need — `["buckets"]` alone omits `static` and `dynamic`.
|
||
|
||
<Tabs>
|
||
<Tab title="fetch">
|
||
```typescript
|
||
const res = await fetch("https://api.supermemory.ai/v4/profile", {
|
||
method: "POST",
|
||
headers: {
|
||
"Authorization": `Bearer ${API_KEY}`,
|
||
"Content-Type": "application/json"
|
||
},
|
||
body: JSON.stringify({
|
||
containerTag: "user_123",
|
||
include: ["buckets"],
|
||
buckets: ["preferences", "goals"] // optional — omit for all buckets
|
||
})
|
||
});
|
||
|
||
const { profile } = await res.json();
|
||
console.log(profile.buckets.preferences);
|
||
console.log(profile.buckets.goals);
|
||
```
|
||
</Tab>
|
||
<Tab title="cURL">
|
||
```bash
|
||
curl -X POST "https://api.supermemory.ai/v4/profile" \
|
||
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"containerTag": "user_123",
|
||
"include": ["buckets"],
|
||
"buckets": ["preferences", "goals"]
|
||
}'
|
||
```
|
||
</Tab>
|
||
</Tabs>
|
||
|
||
**Response:**
|
||
```json
|
||
{
|
||
"profile": {
|
||
"buckets": {
|
||
"preferences": [
|
||
"[Summary] Prefers concise, technical answers and dark-mode tooling",
|
||
"[Recent] Switched their editor to Zed"
|
||
],
|
||
"goals": [
|
||
"[Recent] Wants to ship the billing revamp this quarter"
|
||
]
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
<Note>
|
||
**`[Recent]` and `[Summary]` labels.** To keep profiles dense, an entity's older
|
||
memories are periodically aggregated into a short synthesis. Entries prefixed
|
||
`[Summary]` are that aggregated context; entries prefixed `[Recent]` were ingested
|
||
since the last aggregation and aren't summarized yet. The `dynamic` section uses the
|
||
same `[Recent]` prefix (plus a `[YYYY-MM-DD]` date). Strip the prefixes if you only
|
||
want raw text, or keep them to signal recency to your model.
|
||
</Note>
|
||
|
||
### List bucket definitions
|
||
|
||
To see which buckets are configured for a container tag (org buckets merged with any
|
||
space-level additions), call `/v4/profile/buckets`:
|
||
|
||
<Tabs>
|
||
<Tab title="fetch">
|
||
```typescript
|
||
const res = await fetch("https://api.supermemory.ai/v4/profile/buckets", {
|
||
method: "POST",
|
||
headers: {
|
||
"Authorization": `Bearer ${API_KEY}`,
|
||
"Content-Type": "application/json"
|
||
},
|
||
body: JSON.stringify({ containerTag: "user_123" })
|
||
});
|
||
|
||
const { buckets } = await res.json();
|
||
// [{ key: "preferences", description: "..." }, ...]
|
||
```
|
||
</Tab>
|
||
<Tab title="cURL">
|
||
```bash
|
||
curl -X POST "https://api.supermemory.ai/v4/profile/buckets" \
|
||
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"containerTag": "user_123"}'
|
||
```
|
||
</Tab>
|
||
</Tabs>
|
||
|
||
**Response:**
|
||
```json
|
||
{
|
||
"buckets": [
|
||
{
|
||
"key": "preferences",
|
||
"description": "Explicit first-person preferences the person directly stated."
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
| Field | Type | Description |
|
||
|-------|------|-------------|
|
||
| `buckets[].key` | string | Stable slug, also stored on each memory. Lowercase alphanumeric with `-`/`_`, 1–64 chars |
|
||
| `buckets[].description` | string | What belongs in the bucket — guides the ingestion classifier |
|
||
|
||
<Tip>
|
||
Bucket descriptions steer classification. A precise description ("Explicit
|
||
first-person preferences only — exclude inferred traits") yields cleaner buckets than
|
||
a vague one. `static` and `dynamic` are reserved and can't be used as bucket keys.
|
||
</Tip>
|
||
|
||
---
|
||
|
||
## Framework Examples
|
||
|
||
<Accordion title="Express.js Middleware">
|
||
```typescript
|
||
async function withProfile(req, res, next) {
|
||
if (!req.user?.id) return next();
|
||
|
||
try {
|
||
const { profile } = await client.profile({
|
||
containerTag: req.user.id
|
||
});
|
||
req.userProfile = profile;
|
||
} catch (e) {
|
||
req.userProfile = null;
|
||
}
|
||
next();
|
||
}
|
||
|
||
app.use(withProfile);
|
||
|
||
app.post('/chat', (req, res) => {
|
||
// req.userProfile available in all routes
|
||
});
|
||
```
|
||
</Accordion>
|
||
|
||
<Accordion title="Next.js API Route">
|
||
```typescript
|
||
// app/api/chat/route.ts
|
||
export async function POST(req: NextRequest) {
|
||
const { userId, message } = await req.json();
|
||
|
||
const { profile } = await client.profile({
|
||
containerTag: userId
|
||
});
|
||
|
||
const response = await generateResponse(message, profile);
|
||
return NextResponse.json({ response });
|
||
}
|
||
```
|
||
</Accordion>
|
||
|
||
<Accordion title="AI SDK Integration">
|
||
```typescript
|
||
import { withSupermemory } from "@supermemory/tools/ai-sdk"
|
||
import { openai } from "@ai-sdk/openai"
|
||
|
||
// Profiles automatically injected
|
||
const model = withSupermemory(openai("gpt-4o"), {
|
||
containerTag: "user-123",
|
||
customId: "conv-1",
|
||
})
|
||
|
||
const result = await generateText({
|
||
model,
|
||
messages: [{ role: "user", content: "Help with my project" }]
|
||
});
|
||
```
|
||
|
||
See [AI SDK Integration](/integrations/ai-sdk) for details.
|
||
</Accordion>
|
||
|
||
---
|
||
|
||
## Response Schema
|
||
|
||
```typescript
|
||
interface ProfileResponse {
|
||
profile: {
|
||
static?: string[]; // Long-term facts
|
||
dynamic?: string[]; // Recent context
|
||
buckets?: Record<string, string[]>; // Topical buckets, keyed by bucket key
|
||
};
|
||
searchResults?: { // Only if q parameter provided
|
||
results: SearchResult[];
|
||
total: number;
|
||
timing: number;
|
||
};
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Next Steps
|
||
|
||
- [User Profiles Concept](/concepts/user-profiles) — Understand static vs dynamic
|
||
- [Ingesting Content](/add-memories) — Build profiles by adding content
|
||
- [AI SDK Integration](/integrations/ai-sdk) — Automatic profile injection
|