Platform REST API 与 CLI(3/6)

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.

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.

User Profiles are in beta and available on request. To enable them for your organization, contact support@mem0.ai.

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.

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.",
)
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

result = client.get_profile("alice")

if result["status"] == "succeeded":
    print(result["profile"])
else:
    print("not ready:", result["status"])
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:

{
  "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:

client.generate_profile("alice")
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.

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:

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))
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 }));
}

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.

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.

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. 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.

Use a profile in a prompt

The point of the structure is that it drops straight into a prompt:

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.

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), 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:

{
  "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.

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 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.

How users, agents, apps and runs partition memories.



Steer what Mem0 extracts in the first place.

Inspect project memories, review configuration changes, and test extraction from the Mem0 dashboard.

Copilot is an AI assistant in the Mem0 dashboard. Ask it to inspect stored memories, suggest extraction rules and categories, change project settings, or explain the platform SDKs and APIs.

You describe the task in chat. Copilot uses tools to read project data or propose changes. In Review changes mode, you approve each change before it runs.

Start with your project

  1. Sign in to the dashboard and select the organization and project you want to work on.
  2. Open Copilot in the sidebar.
  3. Select Review changes below the message box.
  4. Ask: “Show my current project settings and explain what each one does.”
  5. Expand an activity card, such as Read project config, to see its Input and Result. Check these results when reviewing an answer or confirming a change.

You can ask about settings or SDK usage with an empty project. Suggestions based on stored data need enough project history to analyze.

Inspect stored memories

Start with a project overview, then narrow the question:

Prompt What to inspect
“Summarize what this project has stored and how it is categorized.” The memories and category patterns Copilot reads. Check which records support its summary.
“Analyze the memories for user alice.” Facts stored for alice, their categories, and unwanted information. To investigate a missing fact, also provide the original input.
“Search alice's memories for dietary preferences.” Memories relevant to the query within that user's scope.

Copilot can list memories across the project. Searching by meaning needs a specific User, Agent, App, or Run ID. Select one in Scope or name it in your prompt.

Memory lists are paginated, and suggestions use samples. Check the activity results to see which records were read. Ask for more pages when you need to inspect the rest.

Choose the memory scope

Open Scope below the message box. Select an existing ID or type one and choose it.

Control Identifies
User The end user whose memories you want to inspect, such as alice.
Agent An AI agent associated with the memories.
App An application associated with the memories.
Run A particular execution or session associated with the memories.

A field set to any adds no filter for that entity type. Clear scope removes the selections. Scope gives Copilot default IDs to use; you can request a different entity in a message. Check the activity's Input to confirm which IDs it used. Adding a test memory requires a User ID, even when another entity is selected.

Scope selects memories, not separate settings. Categories, extraction instructions, memory depth, and multilingual settings apply to the selected project. Category and extraction suggestions also analyze project data, regardless of the selected entity.

See Entity-scoped memory for how these IDs organize memories in your application.

Review and apply changes

The mode control below the message box determines when changes run:

  • Review changes: Copilot pauses before changing project configuration or adding a test memory. Read the proposal and choose whether to apply it.
  • Auto-apply: Copilot can change settings and add test memories without asking for approval. Check the selected project and scope before using it.

Ask Copilot to show the current settings before requesting an update. In the approval card, review every listed field. Approving the card applies the whole proposal, including fields you did not edit.

Action Effect
Apply change Approve the proposed write. Inspect the subsequent result to confirm it succeeded.
Edit When offered, edit extraction instructions or category names and descriptions. Choose Apply with edits to submit your revision.
Discard edits Return to the original proposal without applying it.
Decline Reject this proposal without applying it.
Ask for something else Enter feedback and choose Send. This declines the current proposal and sends your feedback as a new message. Review the next proposal before applying it.

Not every field has an inline editor. If a proposal includes both extraction instructions and categories, only the instructions get an editor. The editor cannot apply blank instructions or an empty category list. Use Ask for something else to change other fields or request separate proposals.

A generated prompt profile contains separate prompts for extraction and summaries. If your project uses one, saving extraction instructions through Copilot deactivates it. Review the replacement rules and test the affected behavior before using them in production.

What Copilot cannot do

Copilot cannot perform these actions, even in Auto-apply mode:

  • Create or delete API keys.
  • Directly edit or delete existing memories.
  • Export project data.
  • Invite or remove organization or project members, or change their roles and permissions.
  • Create or delete organizations or projects.
  • Change billing details or subscription plans.

Use the dashboard or the relevant platform API for these tasks. Copilot can explain the steps or point you to documentation, but it cannot carry out the actions.

Copilot supports the managed Mem0 platform. It does not help configure or use the self-hosted open-source library.

Example 1: Stop storing small talk, then test extraction

This walkthrough uses a project where you have noticed greetings or small talk in stored memories. Use a development project when trying configuration changes, since a test user does not isolate project settings.

Select the project, set **User** to `alice`, and ask:

> Analyze the memories for user alice.

Inspect the returned memories. Identify examples of small talk you want to exclude and useful facts you still want to keep.



With **Review changes** selected, ask:

> Update my extraction instructions to ignore greetings and small talk. Keep the existing rules for durable user preferences.

Check for a **Read project config** activity before reviewing the update. If it is missing, ask Copilot to read the current instructions first. If analysis reports insufficient data, ask it to use the rule you provided. You can also set the rule directly through [Custom instructions](https://docs.mem0.ai/platform/features/custom-instructions).



Review the full replacement text. Keep existing rules your application needs. For this test, the relevant rules could look like:
Remember the following:

* The user's dietary preferences and food restrictions.

Don't remember the following:

* Greetings and small talk.
Use **Edit** to adjust the wording, or **Ask for something else** to request a revision.

Choose **Apply change** or **Apply with edits**, then inspect the result to confirm the instructions were saved.



Choose a new **User** ID for this test, such as `copilot-extraction-test-01`, and clear any other entity selections. Ask:

> Add this as a user message for copilot-extraction-test-01: “Hi! How's your day? I prefer vegetarian meals and avoid peanuts.”

Review the test-memory proposal and choose **Apply change**.


  Adding a test memory writes to the selected project. It is not a dry run. Use a dedicated test user so you can find and remove the test data afterward.




Expand **Add test memory** and inspect **Result**. A `PENDING` status means extraction is still running. Use the returned `event_id` with the [Get Event API](https://docs.mem0.ai/api-reference/events/get-event) to check progress. Wait for `SUCCEEDED` before evaluating the output. If the event is `FAILED`, inspect its details before retrying.

Then ask:

> List all memories for user copilot-extraction-test-01. Then search that user's memories for dietary preferences.

Check that the memories retain the vegetarian preference and peanut restriction, and exclude the greeting. Inspect the full list as well as search results: a relevant search can hide unwanted small-talk records.

If the output is wrong, describe the mismatch and review another instruction change. Use a new test user for the next attempt so earlier memories do not affect the result. Remove test data afterward through the dashboard or [memory deletion API](https://docs.mem0.ai/api-reference/memory/delete-memories).

Test with new inputs after changing settings. Updating configuration is not a cleanup operation for existing memories. See Custom instructions for guidance on writing extraction rules.

Example 2: Suggest categories and extraction instructions

These workflows sample the selected project's data. Generating a suggestion does not update settings by itself. Copilot can then propose applying it, which follows the selected review mode. Use Review changes to inspect suggestions before they are saved.

Workflow Data used Example prompt
Custom categories Stored memories, excluding deleted memories. “Suggest custom categories from this project's memories.”
Extraction instructions Successful requests to add conversation data, including requests that produced no memories. “Compare recent add requests with the extracted memories and suggest better extraction instructions.”

Category suggestions identify recurring themes. Review the names, descriptions, and examples. Applying a category list replaces the project's previous list; it does not add to it or re-tag existing memories. See Custom categories for project and per-call behavior.

Extraction suggestions compare conversation inputs with what was extracted. One add request can produce several memories or none, so the number of add requests is different from the number of stored memories. Keep your application's existing requirements when reviewing the proposal.

Both workflows require enough project data. If a suggestion fails because there is too little data, check the activity's Result for the current and required counts. You can still inspect memories, ask SDK/API questions, or provide your own rules instead of requesting a data-based suggestion.

Example 3: Adjust detail and language

Ask Copilot to read the current settings, then request the change you need:

  • Memory depth controls the level of detail: Less, Medium, or More detailed. Try “Show my current memory depth, then propose More detailed memories.” For deciding which facts to retain, use extraction instructions.
  • Multilingual behavior preserves the user's original language. Try “Enable multilingual behavior so memories preserve the language of the input.” Test it with a new conversation in the language your application uses.

Both are project settings. Review the proposed values and test a fresh input after applying them. See Organization and project settings for configuration through the API.

Example 4: Ask SDK and API questions

Include your language and the task, for example:

Show me how to search memories for user alice with the managed Mem0 Python SDK. Link the documentation you used.

Copilot can look up the official platform documentation. Check the Browse mem0 docs and Read docs page activities and open the cited pages. If an answer has no sources, ask for them before using the example. The Platform quickstart covers adding and searching memories in code.

Resume a conversation and check message limits

Use History to reopen your 50 most recently active chats for the selected project. Chats belong to the person who created them; other project members cannot open them. Use New chat to start a separate conversation, or Delete chat in history to remove one. Deleting a chat does not undo settings changes or remove test memories.

Message allowances depend on the organization's plan and are shared across its projects and members. They reset at the start of each calendar month in UTC.

For plans with a limit, a usage notice appears once 80% of the allowance is used. Below that point, no counter is shown. At the limit, new messages are disabled until the allowance resets or the plan is upgraded. Follow the notice's upgrade link to review plan options.

Sending feedback through Ask for something else counts as a new message. Approving or declining a saved proposal without feedback does not use another message. Test additions and searches also use the platform APIs and remain subject to their normal quotas.