--- 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. **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. Profiles cover **users and agents**. An agent profile summarizes how an agent behaves, in the same way a user profile summarizes a person. Profiles are a **Pro-plan feature** — see [Plan availability](#plan-availability) for the details. ## 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. ```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.", }); ``` 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. Only the fields you pass are written. To turn the feature off without touching your schema, send `enabled` alone. ## Read a profile ```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); } ``` 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: ```python Python client.get_profile("support-bot", entity_type="agent") ``` ```typescript TypeScript await client.getProfile({ entityId: "support-bot", entityType: "agent" }); ``` ## 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: ```python Python client.generate_profile("alice") ``` ```typescript TypeScript await client.generateProfile({ entityId: "alice" }); ``` 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: ```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 })); } ``` 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: ```python Python client.regenerate_profiles() ``` ```typescript TypeScript await client.regenerateProfiles(); ``` 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. ## 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. 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. To rebuild every profile from scratch after a schema change, use [regenerate](#apply-a-new-schema-to-existing-entities). ## 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. 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. ## Plan availability | Capability | Free | Starter | Pro | Enterprise | |---|:---:|:---:|:---:|:---:| | **Profiles** (define a schema, generate, read) | — | — | ✓ | ✓ | | **Profiles dashboard** (schema editor, samples, regenerate) | — | — | ✓ | ✓ | Profiles require a **Pro plan or higher**. For an individual entity to get a profile, three things must hold: - the project is on **Pro or above**, - 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`, or an `agent_id` for agent profiles. 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 | 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 | Profile settings are per project. An API key is scoped to one project, so profiles never cross a project boundary. ## 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 future generations. To apply a new schema to entities that already have a profile, call `regenerate_profiles` (rate-limited to once per project per hour). **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. **Can I have profiles for agents, not just users?** Yes. Pass `entity_type="agent"` to `get_profile` / `generate_profile`. An agent profile summarizes an agent the same way a user profile summarizes a person. **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 How users, agents, apps and runs partition memories. Steer what Mem0 extracts in the first place.