Merge remote-tracking branch 'origin/main' into mintlify/af82bda4
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: 'Generate Profiles'
|
||||
description: "Start one generation: sample a few entities, or build one for a single entity."
|
||||
openapi: post /v2/profiles/jobs/
|
||||
---
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: 'Get Generation Job'
|
||||
description: "Read the progress of a generation, and whether it finished."
|
||||
openapi: get /v2/profiles/jobs/{job_id}/
|
||||
---
|
||||
@@ -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, with a status describing whether generation has completed."
|
||||
openapi: get /v2/entities/{entity_type}/{entity_id}/profile/
|
||||
---
|
||||
@@ -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/
|
||||
---
|
||||
@@ -7,6 +7,13 @@ mode: "wide"
|
||||
<Tabs>
|
||||
<Tab title="Python">
|
||||
|
||||
<Update label="2026-09-23" description="v2.2.0">
|
||||
|
||||
**New Features:**
|
||||
- **Client:** Add User Profiles to `MemoryClient` and `AsyncMemoryClient`: `get_profile()`, `generate_profile()`, `get_profile_settings()`, `update_profile_settings()`, `sample_profiles()`, and `get_profile_job()`. A profile is a structured, always-current JSON summary of one user, shaped by a JSON Schema you configure per project and filled by an LLM from that user's memories. Generation is asynchronous. Every job POST carries an `Idempotency-Key`; to retry a lost request without starting a second job, pass the same `idempotency_key` on each attempt ([#7340](https://github.com/mem0ai/mem0/pull/7340))
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026-09-18" description="v2.1.0">
|
||||
|
||||
**Improvements:**
|
||||
@@ -1235,6 +1242,13 @@ See the [OSS v2 to v3 migration guide](https://docs.mem0.ai/migration/oss-v2-to-
|
||||
|
||||
<Tab title="TypeScript">
|
||||
|
||||
<Update label="2026-09-23" description="v3.3.0">
|
||||
|
||||
**New Features:**
|
||||
- **Client:** Add User Profiles to `MemoryClient`: `getProfile()`, `generateProfile()`, `getProfileSettings()`, `updateProfileSettings()`, `sampleProfiles()`, and `getProfileJob()`. A profile is a structured, always-current JSON summary of one user, shaped by a JSON Schema you configure per project and filled by an LLM from that user's memories. Generation is asynchronous. Every job POST carries an `Idempotency-Key`; to retry a lost request without starting a second job, pass the same `idempotencyKey` on each attempt ([#7340](https://github.com/mem0ai/mem0/pull/7340))
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026-09-18" description="v3.2.0">
|
||||
|
||||
**Improvements:**
|
||||
|
||||
@@ -72,6 +72,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",
|
||||
@@ -513,6 +514,17 @@
|
||||
"api-reference/entities/delete-user"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Profiles",
|
||||
"icon": "id-card",
|
||||
"pages": [
|
||||
"api-reference/profiles/get-profile",
|
||||
"api-reference/profiles/get-profile-settings",
|
||||
"api-reference/profiles/update-profile-settings",
|
||||
"api-reference/profiles/generate-profiles",
|
||||
"api-reference/profiles/get-profile-job"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Organizations",
|
||||
"icon": "building",
|
||||
|
||||
@@ -198,6 +198,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 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.
|
||||
@@ -364,6 +365,11 @@ 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 structured profile and branching on its generation status.
|
||||
- [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.
|
||||
- [Generate Profiles](https://docs.mem0.ai/api-reference/profiles/generate-profiles) [Platform]: Use when building profiles now: a sample of ten, or one entity.
|
||||
- [Get Generation Job](https://docs.mem0.ai/api-reference/profiles/get-profile-job) [Platform]: Use when checking how far a generation has got, and whether it finished.
|
||||
|
||||
### Organizations
|
||||
- [Create Organization](https://docs.mem0.ai/api-reference/organization/create-org) [Platform]: Use when setting up a new org.
|
||||
|
||||
+475
-1
@@ -8070,6 +8070,480 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/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.\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"
|
||||
]
|
||||
},
|
||||
"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"
|
||||
]
|
||||
},
|
||||
"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. Project-wide."
|
||||
},
|
||||
"entities": {
|
||||
"type": "object",
|
||||
"description": "Settings for user profiles, under `user`.",
|
||||
"properties": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"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."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"capabilities": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"jobs": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"estimates": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"samples": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"full_rebuild": {
|
||||
"type": "boolean",
|
||||
"description": "Whether a project-wide rebuild (regenerate/backfill) is available. Currently false."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"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",
|
||||
"description": "Only the fields present are written. `schema` and `custom_instructions` nest under `entities.user`; a flat body is rejected.",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Whether profile generation runs for this project. Project-wide."
|
||||
},
|
||||
"entities": {
|
||||
"type": "object",
|
||||
"description": "Settings for user profiles, under `user`.",
|
||||
"properties": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"nullable": true,
|
||||
"description": "JSON Schema describing the profile. Every property needs a description. Send null to clear it."
|
||||
},
|
||||
"custom_instructions": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "Extra guidance for the extraction step. Send null to clear it."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"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. Project-wide."
|
||||
},
|
||||
"entities": {
|
||||
"type": "object",
|
||||
"description": "Settings for user profiles, under `user`.",
|
||||
"properties": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"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."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"capabilities": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"jobs": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"estimates": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"samples": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"full_rebuild": {
|
||||
"type": "boolean",
|
||||
"description": "Whether a project-wide rebuild (regenerate/backfill) is available. Currently false."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "The schema is not a valid profile schema."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v2/profiles/jobs/": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"profiles"
|
||||
],
|
||||
"operationId": "profiles_create_job",
|
||||
"summary": "Generate profiles",
|
||||
"description": "Start one generation. `operation` says what to build:\n\n- `sample` — up to 10 real entities, so a schema can be judged before it is used widely. These are real profiles: they are saved to those entities and count toward usage.\n- `trigger` — one entity, named by `entity_id`.\n\nSend an `Idempotency-Key` header. Replaying the same key returns the same job instead of charging twice. Poll `status_url` from the response until the status is terminal.",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "header",
|
||||
"name": "Idempotency-Key",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"minLength": 8,
|
||||
"maxLength": 128
|
||||
},
|
||||
"description": "Makes a retry safe: the same key returns the same job."
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"operation",
|
||||
"entity_type"
|
||||
],
|
||||
"properties": {
|
||||
"operation": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"sample",
|
||||
"trigger"
|
||||
],
|
||||
"description": "What to generate. Optional only when `entity_id` is set, which means `trigger`."
|
||||
},
|
||||
"entity_type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"user"
|
||||
]
|
||||
},
|
||||
"entity_id": {
|
||||
"type": "string",
|
||||
"description": "One entity, for `trigger`."
|
||||
},
|
||||
"limit": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 10,
|
||||
"description": "How many entities to sample."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"202": {
|
||||
"description": "Job accepted.",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"job_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"status_url": {
|
||||
"type": "string",
|
||||
"description": "Poll this. Building the path yourself breaks on a route change."
|
||||
},
|
||||
"operation": {
|
||||
"type": "string"
|
||||
},
|
||||
"entity_type": {
|
||||
"type": "string"
|
||||
},
|
||||
"entity_count_reserved": {
|
||||
"type": "integer",
|
||||
"description": "Entities reserved against usage for this job."
|
||||
},
|
||||
"event_id": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"replayed": {
|
||||
"type": "boolean",
|
||||
"description": "True when an Idempotency-Key returned an existing job."
|
||||
},
|
||||
"sampled": {
|
||||
"type": "integer",
|
||||
"description": "`sample` only."
|
||||
},
|
||||
"entity_ids": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "`sample` only: the entity ids picked. Read each with `GET /v2/entities/user/{entity_id}/profile/`."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Unknown or missing `operation`, or profiles are not configured."
|
||||
},
|
||||
"402": {
|
||||
"description": "Payment required."
|
||||
},
|
||||
"409": {
|
||||
"description": "A job is already running, or the Idempotency-Key was used for a different request. Branch on `error.code`."
|
||||
},
|
||||
"429": {
|
||||
"description": "Cooldown. `retry_after_seconds` sits inside `error`."
|
||||
},
|
||||
"503": {
|
||||
"description": "`jobs_unavailable` — generation is switched off for this project."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v2/profiles/jobs/{job_id}/": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"profiles"
|
||||
],
|
||||
"operationId": "profiles_get_job",
|
||||
"summary": "Read a generation job",
|
||||
"description": "The job nests under `job`. `total` is null until `enumeration_complete`, and `completed` is `succeeded + failed + skipped`.",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "path",
|
||||
"name": "job_id",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The job.",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"job": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"operation": {
|
||||
"type": "string"
|
||||
},
|
||||
"entity_type": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"QUEUED",
|
||||
"RUNNING",
|
||||
"SUCCEEDED",
|
||||
"PARTIALLY_SUCCEEDED",
|
||||
"FAILED",
|
||||
"CANCELLED"
|
||||
]
|
||||
},
|
||||
"total": {
|
||||
"type": "integer",
|
||||
"nullable": true
|
||||
},
|
||||
"enumeration_complete": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"completed": {
|
||||
"type": "integer"
|
||||
},
|
||||
"succeeded": {
|
||||
"type": "integer"
|
||||
},
|
||||
"failed": {
|
||||
"type": "integer"
|
||||
},
|
||||
"skipped": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "No such job in this project."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
@@ -8988,4 +9462,4 @@
|
||||
}
|
||||
},
|
||||
"x-original-swagger-version": "2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,359 @@
|
||||
---
|
||||
title: Profiles
|
||||
description: "Build a structured, always-current summary of each user from their memories, shaped by a JSON Schema you define."
|
||||
---
|
||||
|
||||
# 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>
|
||||
|
||||
<Note>
|
||||
User Profiles are in **beta** and available on request. To enable them for your
|
||||
organization, contact [support@mem0.ai](mailto:support@mem0.ai).
|
||||
</Note>
|
||||
|
||||
## 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": 3
|
||||
}
|
||||
```
|
||||
|
||||
`generation_count` is how many times this profile has been (re)generated — `0` before the first generation completes.
|
||||
|
||||
### 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.
|
||||
|
||||
## 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.
|
||||
|
||||
Sampling is asynchronous: the call returns a job as soon as it is queued. Poll `status_url` until the job is terminal, then read each sampled entity's profile:
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
import time
|
||||
|
||||
job = client.sample_profiles(limit=5)
|
||||
|
||||
# Poll until the sample job reaches a terminal state (job status is UPPERCASE).
|
||||
TERMINAL = {"SUCCEEDED", "PARTIALLY_SUCCEEDED", "FAILED", "CANCELLED"}
|
||||
deadline = time.time() + 120
|
||||
while True:
|
||||
status = client.get_profile_job(job["status_url"])["job"]
|
||||
if status["status"] in TERMINAL:
|
||||
break
|
||||
if time.time() > deadline:
|
||||
raise TimeoutError("Sample job did not finish in time")
|
||||
time.sleep(3)
|
||||
|
||||
print(status["status"], status["succeeded"], "of", status["total"])
|
||||
|
||||
# The create response lists the sampled entities; read each one's saved profile.
|
||||
for entity_id in job.get("entity_ids", []):
|
||||
print(client.get_profile(entity_id))
|
||||
```
|
||||
|
||||
```typescript TypeScript
|
||||
const job = await client.sampleProfiles({ limit: 5 });
|
||||
|
||||
// Poll until the sample job reaches a terminal state (job status is UPPERCASE).
|
||||
const TERMINAL = ["SUCCEEDED", "PARTIALLY_SUCCEEDED", "FAILED", "CANCELLED"];
|
||||
const deadline = Date.now() + 120_000;
|
||||
let status;
|
||||
while (true) {
|
||||
status = (await client.getProfileJob(job.statusUrl)).job;
|
||||
if (TERMINAL.includes(status.status)) break;
|
||||
if (Date.now() > deadline)
|
||||
throw new Error("Sample job did not finish in time");
|
||||
await new Promise((resolve) => setTimeout(resolve, 3000));
|
||||
}
|
||||
|
||||
console.log(status.status, status.succeeded, "of", status.total);
|
||||
|
||||
// The create response lists the sampled entities; read each one's saved profile.
|
||||
for (const entityId of job.entityIds ?? []) {
|
||||
console.log(await client.getProfile({ entityId }));
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
These are real generations. The profiles are saved to those entities and count toward your usage, so sampling is not wasted work and not a free dry run. A sample covers up to 10 entities and cannot be repeated immediately.
|
||||
|
||||
## Apply a new schema to existing entities
|
||||
|
||||
A new schema shapes the next generation. Profiles that already exist keep their values until their entity is generated again.
|
||||
|
||||
Each entity picks the new schema up as it sends more memories, and you can generate one now with `generate_profile`.
|
||||
|
||||
<Note>
|
||||
Rebuilding every profile in a project at once is not available yet. Refresh profiles one entity at a time with `generate_profile`, or let each one update on its own as its entity sends more memories.
|
||||
</Note>
|
||||
|
||||
## When profiles update
|
||||
|
||||
You never call an "update profile" endpoint — Mem0 keeps each profile current for you. Two things drive it:
|
||||
|
||||
- **Automatically, as memories accumulate.** Mem0 refreshes an entity's profile after roughly every **10 messages** it receives, folding the new memories into the existing profile. There is no schedule to wait for and no extra call to make: the same `add` you already do keeps the profile moving.
|
||||
- **On demand.** Call `generate_profile` to build or refresh a profile immediately — useful for a brand-new entity that has not yet crossed the automatic threshold.
|
||||
|
||||
Generation is **asynchronous and incremental**. A refresh runs in the background a short while after its trigger, so a read taken immediately after an `add` may still show the previous profile (or `pending`). Branch on `status` rather than assuming the latest memory is already reflected.
|
||||
|
||||
<Note>
|
||||
Updates are **incremental**, not a full rebuild each time — Mem0 merges what it newly learns into the stored profile and keeps the fields your schema still defines. After a schema change, existing profiles pick it up as their entities send more memories, or when you call `generate_profile` — see [Apply a new schema to existing entities](#apply-a-new-schema-to-existing-entities).
|
||||
</Note>
|
||||
|
||||
## 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.
|
||||
|
||||
<Note>
|
||||
A schema has a size budget of roughly **10,000 tokens** of serialized JSON — the whole schema is sent to the model on every generation, so a handful of verbose fields can cost more than many terse ones. Oversized schemas are rejected on save.
|
||||
</Note>
|
||||
|
||||
## Availability
|
||||
|
||||
The feature is in beta and enabled per organization on request — see the note at the top of this page.
|
||||
|
||||
Once it is on, an entity gets a profile when two more things hold:
|
||||
|
||||
- profiles are **enabled** with a schema for the project (see [Define the schema](#define-the-schema)), and
|
||||
- the memory is scoped to an entity — a `user_id`.
|
||||
|
||||
On a project where profiles are turned off, a read returns `status: not_enabled` rather than an error, so you can call it unconditionally and branch on the status.
|
||||
|
||||
## Settings reference
|
||||
|
||||
| Argument | 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 |
|
||||
|
||||
`enabled` is project-wide. `schema` and `custom_instructions` apply to user
|
||||
profiles, so the stored settings nest them under `entities`:
|
||||
|
||||
```json
|
||||
{
|
||||
"enabled": true,
|
||||
"entities": {
|
||||
"user": {
|
||||
"schema": { "type": "object", "properties": { "...": {} } },
|
||||
"custom_instructions": "Prefer durable traits over one-off remarks."
|
||||
}
|
||||
},
|
||||
"capabilities": { "full_rebuild": false }
|
||||
}
|
||||
```
|
||||
|
||||
That is what a read returns and what a write accepts. The SDKs take the fields
|
||||
flat and nest them for you, so a schema you write with
|
||||
`update_profile_settings` comes back unchanged from `get_profile_settings`.
|
||||
|
||||
<Note>
|
||||
Profile settings are per project. An API key is scoped to one project, so profiles never cross a project boundary.
|
||||
</Note>
|
||||
|
||||
## FAQ
|
||||
|
||||
**Do I need to change my `add` or `search` calls to use profiles?**
|
||||
No. Profiles are built from the memories you already add. You define a schema once and read the profile when you need it — your ingestion and retrieval code is unchanged.
|
||||
|
||||
**Why is `profile` empty even though the entity has memories?**
|
||||
Generation is asynchronous and needs enough to work with. Branch on `status`: `pending` means it is still building, and `insufficient_data` means there are not yet enough memories to fill the schema. Read again shortly, or call `generate_profile` to build one now.
|
||||
|
||||
**Is sampling free?**
|
||||
No. `sample_profiles` runs real generations against real memories and **keeps** the profiles it produces, so it counts toward your usage like any other generation. It exists to check a schema on a few entities before you commit to it — not as a zero-cost dry run.
|
||||
|
||||
**Does changing the schema rewrite existing profiles?**
|
||||
No. A schema change applies to the next generation. An existing profile keeps its values until its entity is generated again, which happens as that entity sends more memories, or when you call `generate_profile` for it.
|
||||
|
||||
**What happens to a field I remove from the schema?**
|
||||
It stops being maintained. On an entity's next generation, fields your schema no longer defines are pruned from the stored profile — so keep a field in the schema for as long as you want its value kept.
|
||||
|
||||
**How current is a profile?**
|
||||
It refreshes automatically as memories accumulate (about every 10 messages for an entity), plus any on-demand `generate_profile` calls. Because refreshes run in the background, expect a short delay after the triggering `add` rather than an instant update.
|
||||
|
||||
## 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>
|
||||
Reference in New Issue
Block a user