feat(profiles): SDK methods and docs for entity profiles

Adds the profile surface to both SDKs and documents it.

Python (MemoryClient + AsyncMemoryClient) and TypeScript gain:
get/generate profile, get/update settings, sample, regenerate.

A profile's keys come from the customer's own JSON Schema, so the TS
client keeps `profile` opaque and passes `schema` through untouched in
both directions. Camel-casing them would return field names that do not
match the schema the customer wrote.

Reads use the v2 envelope, where a known entity with no profile yet is a
200 carrying a status rather than a 404, so an empty state is
distinguishable from an error.

Verified against a live environment running this API: settings
round-trip, a partial update leaves the schema intact, generation
produces a profile under the configured schema, and sample, regenerate,
cooldown and the 400/404 paths all answer as documented.
This commit is contained in:
Pratik
2026-09-10 16:04:09 -07:00
parent d873892dad
commit 623e33f2db
17 changed files with 1668 additions and 4 deletions
@@ -0,0 +1,5 @@
---
title: 'Get Profile Settings'
description: "Retrieve the profile schema, custom instructions, and enabled flag for the current project."
openapi: get /v2/profiles/settings/
---
@@ -0,0 +1,5 @@
---
title: 'Get Profile'
description: "Retrieve the structured profile for a user or agent, with a status describing whether generation has completed."
openapi: get /v2/entities/{entity_type}/{entity_id}/profile/
---
@@ -0,0 +1,5 @@
---
title: 'Regenerate Profiles'
description: "Rebuild the profile of every entity in the project, applying the current schema to entities that already have one."
openapi: post /v2/profiles/regenerate/
---
@@ -0,0 +1,5 @@
---
title: 'Sample Profiles'
description: "Generate profiles for a small set of real entities to evaluate a schema before applying it across the project."
openapi: post /v2/profiles/samples/
---
@@ -0,0 +1,5 @@
---
title: 'Generate Profile'
description: "Generate or refresh the profile for a single user or agent immediately, instead of waiting for the message threshold."
openapi: post /v2/profiles/trigger/
---
@@ -0,0 +1,5 @@
---
title: 'Update Profile Settings'
description: "Set the JSON Schema, custom instructions, or enabled flag that control profile generation for the project."
openapi: post /v2/profiles/settings/
---
+13
View File
@@ -71,6 +71,7 @@
"pages": [
"platform/features/v2-memory-filters",
"platform/features/entity-scoped-memory",
"platform/features/user-profiles",
"platform/features/graph-memory",
"platform/features/async-client",
"platform/features/multimodal-support",
@@ -511,6 +512,18 @@
"api-reference/entities/delete-user"
]
},
{
"group": "Profiles",
"icon": "id-card",
"pages": [
"api-reference/profiles/get-profile",
"api-reference/profiles/trigger-profile",
"api-reference/profiles/get-profile-settings",
"api-reference/profiles/update-profile-settings",
"api-reference/profiles/sample-profiles",
"api-reference/profiles/regenerate-profiles"
]
},
{
"group": "Organizations",
"icon": "building",
+7
View File
@@ -197,6 +197,7 @@ If the user is on a pre-current major (Python < 2, TS < 3, or a Platform call st
### Features - Essential
- [V2 Memory Filters](https://docs.mem0.ai/platform/features/v2-memory-filters) [Platform]: Use when compound filters (AND/OR on metadata, entity, time) are needed at search.
- [Entity-Scoped Memory](https://docs.mem0.ai/platform/features/entity-scoped-memory) [Platform]: Use when partitioning memories by user, agent, app, or run.
- [Profiles](https://docs.mem0.ai/platform/features/user-profiles) [Platform]: Use when a structured always-current summary of a user or agent is needed in one read, instead of searching their memories.
- [Graph Memory](https://docs.mem0.ai/platform/features/graph-memory) [Platform]: Use when connecting facts across memories through shared entities for entity-centric or multi-hop questions.
- [Async Client](https://docs.mem0.ai/platform/features/async-client) [Platform]: Use when the app issues many concurrent Mem0 calls and needs non-blocking I/O.
- [Multimodal Support](https://docs.mem0.ai/platform/features/multimodal-support) [Platform]: Use when storing images or PDFs as memory input.
@@ -362,6 +363,12 @@ All API Reference docs describe Mem0 Platform REST endpoints (requires API key).
### Entities
- [Get Users](https://docs.mem0.ai/api-reference/entities/get-users) [Platform]: Use when listing users, agents, or apps known to a project.
- [Delete User](https://docs.mem0.ai/api-reference/entities/delete-user) [Platform]: Use when removing an entity and all its memories.
- [Get Profile](https://docs.mem0.ai/api-reference/profiles/get-profile) [Platform]: Use when reading a user's or agent's structured profile and branching on its generation status.
- [Generate Profile](https://docs.mem0.ai/api-reference/profiles/trigger-profile) [Platform]: Use when a profile is needed before the entity reaches the automatic message threshold.
- [Get Profile Settings](https://docs.mem0.ai/api-reference/profiles/get-profile-settings) [Platform]: Use when checking the project's profile schema, instructions, or enabled flag.
- [Update Profile Settings](https://docs.mem0.ai/api-reference/profiles/update-profile-settings) [Platform]: Use when defining or changing the JSON Schema that shapes profiles for a project.
- [Sample Profiles](https://docs.mem0.ai/api-reference/profiles/sample-profiles) [Platform]: Use when validating a profile schema against a few real entities before applying it project-wide.
- [Regenerate Profiles](https://docs.mem0.ai/api-reference/profiles/regenerate-profiles) [Platform]: Use when a new schema must be applied to entities that already have a profile.
### Organizations
- [Create Organization](https://docs.mem0.ai/api-reference/organization/create-org) [Platform]: Use when setting up a new org.
+372 -1
View File
@@ -8070,6 +8070,377 @@
}
}
}
},
"/v2/entities/{entity_type}/{entity_id}/profile/": {
"get": {
"tags": [
"profiles"
],
"operationId": "profiles_read",
"summary": "Get an entity's profile",
"description": "Return the memory profile for one user or agent.\n\nGeneration is asynchronous, so a known entity that has no profile yet is a normal 200 carrying a `status`. A 404 means only that no such entity exists.",
"parameters": [
{
"name": "entity_type",
"in": "path",
"required": true,
"schema": {
"type": "string",
"enum": [
"user",
"agent"
]
},
"description": "The kind of entity that carries the profile."
},
{
"name": "entity_id",
"in": "path",
"required": true,
"schema": {
"type": "string"
},
"description": "The entity's id, as supplied when the memory was added."
}
],
"responses": {
"200": {
"description": "The profile envelope.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"profile": {
"type": "object",
"additionalProperties": true,
"description": "The generated profile, shaped by the project's schema. Empty unless status is succeeded."
},
"status": {
"type": "string",
"enum": [
"succeeded",
"pending",
"failed",
"not_enabled",
"insufficient_data"
],
"description": "Generation state. Branch on this rather than on an empty profile."
},
"entity_type": {
"type": "string",
"enum": [
"user",
"agent"
]
},
"entity_id": {
"type": "string"
},
"updated_at": {
"type": "string",
"format": "date-time",
"nullable": true
},
"generation_count": {
"type": "integer"
}
}
}
}
}
},
"400": {
"description": "Unsupported entity type."
},
"404": {
"description": "No such entity in this project."
}
}
}
},
"/v2/profiles/settings/": {
"get": {
"tags": [
"profiles"
],
"operationId": "profiles_settings_read",
"summary": "Get profile settings",
"description": "Return the profile settings for the project the API key is scoped to.",
"responses": {
"200": {
"description": "Current settings.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether profile generation runs for this project."
},
"schema": {
"type": "object",
"additionalProperties": true,
"nullable": true,
"description": "JSON Schema describing the profile. Every property needs a description."
},
"custom_instructions": {
"type": "string",
"nullable": true,
"description": "Extra guidance for the extraction step."
}
}
}
}
}
}
}
},
"post": {
"tags": [
"profiles"
],
"operationId": "profiles_settings_update",
"summary": "Update profile settings",
"description": "Update the project's profile settings. Only the fields present in the body are written, so one setting can change without re-sending the others.",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether profile generation runs for this project."
},
"schema": {
"type": "object",
"additionalProperties": true,
"nullable": true,
"description": "JSON Schema describing the profile. Every property needs a description."
},
"custom_instructions": {
"type": "string",
"nullable": true,
"description": "Extra guidance for the extraction step."
}
}
}
}
}
},
"responses": {
"200": {
"description": "Settings as stored after the update.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether profile generation runs for this project."
},
"schema": {
"type": "object",
"additionalProperties": true,
"nullable": true,
"description": "JSON Schema describing the profile. Every property needs a description."
},
"custom_instructions": {
"type": "string",
"nullable": true,
"description": "Extra guidance for the extraction step."
}
}
}
}
}
},
"400": {
"description": "The schema is not a valid profile schema."
}
}
}
},
"/v2/profiles/trigger/": {
"post": {
"tags": [
"profiles"
],
"operationId": "profiles_trigger",
"summary": "Generate one entity's profile now",
"description": "Generate or refresh the profile for a single entity immediately.\n\nProfiles are otherwise built once an entity crosses an internal message threshold, so a new entity has none for its first few memories. Returns 202: poll the read endpoint and branch on `status`.",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"entity_id"
],
"properties": {
"entity_type": {
"type": "string",
"enum": [
"user",
"agent"
],
"default": "user"
},
"entity_id": {
"type": "string"
}
}
}
}
}
},
"responses": {
"202": {
"description": "Generation queued.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"entity_type": {
"type": "string"
},
"entity_id": {
"type": "string"
},
"profile_id": {
"type": "string"
},
"status": {
"type": "string"
}
}
}
}
}
},
"400": {
"description": "Unsupported entity type, or profiles are not enabled and configured."
},
"404": {
"description": "No such entity in this project."
}
}
}
},
"/v2/profiles/samples/": {
"post": {
"tags": [
"profiles"
],
"operationId": "profiles_samples",
"summary": "Sample profiles to check a schema",
"description": "Generate profiles for a handful of real entities so a schema can be judged before it is applied project-wide.\n\nThese are real generations and the results are kept, so the work counts toward a later regenerate.",
"requestBody": {
"required": false,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 10,
"description": "How many entities to sample."
}
}
}
}
}
},
"responses": {
"202": {
"description": "Sampling queued.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"sampled": {
"type": "integer"
},
"results": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": true
}
}
}
}
}
}
},
"400": {
"description": "Profiles are not enabled and configured."
},
"429": {
"description": "A sample run was already started for this project very recently."
}
}
}
},
"/v2/profiles/regenerate/": {
"post": {
"tags": [
"profiles"
],
"operationId": "profiles_regenerate",
"summary": "Rebuild every profile in the project",
"description": "Rebuild the profile of every entity in the project. This is how a new schema reaches entities that already have a profile.\n\nReturns 202 immediately; the rebuild runs in the background.",
"responses": {
"202": {
"description": "Regenerate queued.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"status": {
"type": "string"
},
"message": {
"type": "string"
},
"project_id": {
"type": "string"
},
"existing_profile_count": {
"type": "integer"
}
}
}
}
}
},
"400": {
"description": "Profiles are not enabled and configured."
},
"409": {
"description": "A regenerate is already running for this project."
},
"429": {
"description": "A regenerate already ran for this project within the last hour."
}
}
}
}
},
"components": {
@@ -8988,4 +9359,4 @@
}
},
"x-original-swagger-version": "2.0"
}
}
+272
View File
@@ -0,0 +1,272 @@
---
title: Profiles
description: "Build a structured, always-current summary of each user or agent from their memories, shaped by a JSON Schema you define."
icon: "id-card"
---
# Profiles
Memories are individual facts. A profile is the summary of all of them for one entity: a single structured object, shaped by a JSON Schema you define, that Mem0 keeps current as new memories arrive.
Search answers "what did this user say about X". A profile answers "who is this user", in one read, with no query to write.
<Info>
**Use profiles when…**
- You want to personalize a first response, before the user says anything in this session.
- You need a compact object to drop into a prompt instead of a list of memories.
- You want the same fields for every user, so your code can rely on their shape.
</Info>
Profiles cover **users and agents**. An agent profile summarizes how an agent behaves, in the same way a user profile summarizes a person.
## How it works
1. You define a **schema**: the fields a profile should contain, each with a description.
2. Mem0 builds each entity's profile from their memories, and rebuilds it as new memories arrive.
3. You read the profile whenever you need it.
Generation is **asynchronous**. A profile is not ready the instant an entity's first memory lands, so a read tells you where it is with a `status` rather than failing.
## Define the schema
The schema is JSON Schema. Every property needs a `description` — that is what tells the model how to fill the field, so a vague description gives a vague profile.
<CodeGroup>
```python Python
from mem0 import MemoryClient
client = MemoryClient()
client.update_profile_settings(
enabled=True,
schema={
"type": "object",
"properties": {
"communication_style": {
"type": "string",
"description": "How the user prefers to be addressed: terse, detailed, formal, casual",
},
"expertise_areas": {
"type": "array",
"items": {"type": "string"},
"description": "Subjects the user demonstrates working knowledge of",
},
"current_goals": {
"type": "array",
"items": {"type": "string"},
"description": "What the user is actively trying to accomplish",
},
},
},
custom_instructions="Prefer durable traits over one-off remarks.",
)
```
```typescript TypeScript
import MemoryClient from "mem0ai";
const client = new MemoryClient({ apiKey: "your-api-key" });
await client.updateProfileSettings({
enabled: true,
schema: {
type: "object",
properties: {
communication_style: {
type: "string",
description:
"How the user prefers to be addressed: terse, detailed, formal, casual",
},
expertise_areas: {
type: "array",
items: { type: "string" },
description: "Subjects the user demonstrates working knowledge of",
},
current_goals: {
type: "array",
items: { type: "string" },
description: "What the user is actively trying to accomplish",
},
},
},
customInstructions: "Prefer durable traits over one-off remarks.",
});
```
</CodeGroup>
<Note>
Your schema's property names reach the API exactly as you write them. The SDKs do not rewrite them, so a profile always comes back with the field names you chose.
</Note>
Only the fields you pass are written. To turn the feature off without touching your schema, send `enabled` alone.
## Read a profile
<CodeGroup>
```python Python
result = client.get_profile("alice")
if result["status"] == "succeeded":
print(result["profile"])
else:
print("not ready:", result["status"])
```
```typescript TypeScript
const result = await client.getProfile({ entityId: "alice" });
if (result.status === "succeeded") {
console.log(result.profile);
} else {
console.log("not ready:", result.status);
}
```
</CodeGroup>
A response looks like this:
```json
{
"profile": {
"communication_style": "terse",
"expertise_areas": ["distributed systems", "postgres"],
"current_goals": ["cut p99 latency", "migrate off the legacy queue"]
},
"status": "succeeded",
"entity_type": "user",
"entity_id": "alice",
"updated_at": "2026-02-08T10:30:00Z",
"generation_count": 4
}
```
### Always branch on `status`
`profile` is empty unless `status` is `succeeded`. Check the status rather than the emptiness of the object, so a profile that is merely still building is not mistaken for a user you know nothing about.
| `status` | Meaning | What to do |
|---|---|---|
| `succeeded` | Profile is built and current | Use it |
| `pending` | Generation is queued or running | Read again shortly |
| `insufficient_data` | Not enough memories to say anything yet | Fall back to defaults |
| `not_enabled` | Profiles are off for this project | Enable them in settings |
| `failed` | The last generation did not complete | Retry, or trigger a new one |
A `404` means only that no such entity exists in your project.
For an agent, pass the entity type:
<CodeGroup>
```python Python
client.get_profile("support-bot", entity_type="agent")
```
```typescript TypeScript
await client.getProfile({ entityId: "support-bot", entityType: "agent" });
```
</CodeGroup>
## Generate a profile on demand
Profiles are built once an entity has accumulated enough messages, so a brand-new user has none during their first few interactions. Trigger one directly to close that gap:
<CodeGroup>
```python Python
client.generate_profile("alice")
```
```typescript TypeScript
await client.generateProfile({ entityId: "alice" });
```
</CodeGroup>
The call returns as soon as the work is queued. Poll the read endpoint and branch on `status`.
## Test a schema before applying it
A schema that reads well can still produce disappointing profiles. Sample a few real entities and inspect the output before committing to it:
<CodeGroup>
```python Python
result = client.sample_profiles(limit=5)
for row in result["results"]:
print(client.get_profile(row["entity_id"]))
```
```typescript TypeScript
const result = await client.sampleProfiles({ limit: 5 });
for (const row of result.results) {
console.log(await client.getProfile({ entityId: row.entityId }));
}
```
</CodeGroup>
These are real generations against real memories, and the results are kept — sampling is not wasted work. Sampling is limited to 10 entities per call and cannot be repeated immediately.
## Apply a new schema to existing entities
Changing the schema does not retroactively rewrite profiles that already exist. To rebuild every profile in the project:
<CodeGroup>
```python Python
client.regenerate_profiles()
```
```typescript TypeScript
await client.regenerateProfiles();
```
</CodeGroup>
This returns immediately and runs in the background; a large project takes a while. It is limited to one run per project per hour, so sample first and regenerate once you are satisfied.
## Use a profile in a prompt
The point of the structure is that it drops straight into a prompt:
```python
result = client.get_profile(user_id)
if result["status"] == "succeeded":
profile = result["profile"]
system_prompt = f"""You are helping {user_id}.
Communication style: {profile.get("communication_style", "unknown")}
Areas of expertise: {", ".join(profile.get("expertise_areas", []))}
Current goals: {", ".join(profile.get("current_goals", []))}
Match their style and do not explain what they already know."""
else:
system_prompt = "You are a helpful assistant."
```
## Writing a schema that works
- **Describe every field.** The description is the instruction; without it the model guesses.
- **Prefer durable traits.** "Prefers dark mode" ages well; "is annoyed today" does not.
- **Keep it small.** Ten focused fields beat forty speculative ones, and cost less to generate.
- **Say what the field is not.** A description that rules out the near-miss interpretation is worth more than one that only states the obvious.
- **Sample before you commit.** It is the only way to see what your descriptions actually produce.
## Settings reference
| Field | Type | Description |
|---|---|---|
| `enabled` | boolean | Whether profile generation runs for the project |
| `schema` | object | JSON Schema describing the profile. Every property needs a `description` |
| `custom_instructions` | string | Extra guidance applied during extraction |
<Note>
Profile settings are per project. An API key is scoped to one project, so profiles never cross a project boundary.
</Note>
## Related
<CardGroup cols={2}>
<Card title="Entity-Scoped Memory" icon="users" href="/platform/features/entity-scoped-memory">
How users, agents, apps and runs partition memories.
</Card>
<Card title="Custom Instructions" icon="pen" href="/platform/features/custom-instructions">
Steer what Mem0 extracts in the first place.
</Card>
</CardGroup>