Back to Mem0

Entity-Scoped Memory

docs/platform/features/entity-scoped-memory.mdx

2.0.17.9 KB
Original Source

Mem0's Platform API lets you separate memories for different users, agents, and apps. By tagging each write and query with the right identifiers, you can prevent data from mixing between them, maintain clear audit trails, and control data retention.

<Tip icon="layers"> Want the long-form tutorial? The <Link href="/cookbooks/essentials/entity-partitioning-playbook">Partition Memories by Entity</Link> cookbook walks through multi-agent storage, debugging, and cleanup step by step. </Tip> <Info> **You'll use this when…** - You run assistants for multiple customers who each need private memory spaces - Different agents (like a planner and a critic) need separate context for the same user - Sessions should expire on their own schedule, making debugging and data removal more precise </Info>

Configure access

python
from mem0 import MemoryClient

client = MemoryClient(api_key="m0-...")

Call client.project.get() to verify your connection. It should return your project details including org_id and project_id. If you get a 401 error, generate a new API key in the Mem0 dashboard.

Feature anatomy

DimensionFieldWhen to use itExample value
Useruser_idPersistent persona or account"customer_6412"
Agentagent_idDistinct agent persona or tool"meal_planner"
Applicationapp_idWhite-label app or product surface"ios_retail_demo"
Sessionrun_idShort-lived flow, ticket, or conversation thread"ticket-9241"
  • Writes (client.add) accept any combination of these fields. Absent fields default to null.
  • Reads (client.search, client.get_all, exports, deletes) accept the same identifiers inside the filters JSON object.
  • Implicit null scoping: Passing only {"user_id": "alice"} automatically restricts results to records where agent_id, app_id, and run_id are null. Add wildcards ("*"), explicit lists, or additional filters when you need broader joins.
<Warning> **Common Pitfall**: If you create a memory with `user_id="alice"` but the other fields default to `null`, then search with `{"AND": [{"user_id": "alice"}, {"agent_id": "bot"}]}` will return nothing because you're looking for a memory where `agent_id="bot"`, not `null`. </Warning>

Choose the right identifier

IdentifierPurposeExample Use Cases
user_idStore preferences, profile details, and historical actions that follow a person everywhereDietary restrictions, seat preferences, meeting habits
agent_idKeep an agent's personality, operating modes, or brand voice in one placeTravel agent vs concierge vs customer support personas
app_idTag every write from a partner app or deployment for tenant separationWhite-label deployments, partner integrations
run_idIsolate temporary flows that should reset or expire independentlySupport tickets, chat sessions, experiments

For more detailed examples, see the Partition Memories by Entity cookbook.

Configure it

The example below adds memories with entity tags:

python
messages = [
    {"role": "user", "content": "I teach ninth-grade algebra."},
    {"role": "assistant", "content": "I'll tailor study plans to algebra topics."}
]

client.add(
    messages,
    user_id="teacher_872",
    agent_id="study_planner",
    app_id="district_dashboard",
    run_id="prep-period-2025-09-02"
)

The response will include one or more memory IDs. Check the dashboard → Memories to confirm the entry appears under the correct user, agent, app, and run.

<Warning> Platform writes that include both `user_id` and `agent_id` (or other combinations) are persisted as separate records per entity so we can enforce privacy boundaries. Each record carries exactly one primary entity, which is why `{"AND": [{"user_id": ...}, {"agent_id": ...}]}` never returns results. Plan searches per entity scope or combine scopes with `OR`. </Warning>

The HTTP equivalent uses POST /v1/memories/ with the same identifiers in the JSON body. See the Add Memories API reference for REST details.

See it in action

1. Store scoped memories

python
traveler_messages = [
    {"role": "user", "content": "I prefer boutique hotels and avoid shellfish."},
    {"role": "assistant", "content": "Logged your travel preferences for future itineraries."}
]

client.add(
    traveler_messages,
    user_id="customer_6412",
    agent_id="travel_planner",
    app_id="concierge_portal",
    run_id="itinerary-2025-apr",
    metadata={"category": "preferences"}
)

2. Retrieve by user scope

python
user_scope = {
    "AND": [
        {"user_id": "customer_6412"},
        {"app_id": "concierge_portal"},
        {"run_id": "itinerary-2025-apr"}
    ]
}

user_results = client.search("Any dietary flags?", filters=user_scope)
print(user_results)

3. Retrieve by agent scope

python
agent_scope = {
    "AND": [
        {"agent_id": "travel_planner"},
        {"app_id": "concierge_portal"}
    ]
}

agent_results = client.search("Any dietary flags?", filters=agent_scope)
print(agent_results)
<Tip icon="compass"> Writes can include multiple identifiers, but searches resolve one entity space at a time. Query user scope *or* agent scope in a given call—combining both returns an empty list today. </Tip> <Tip icon="sparkles"> Want to experiment with AND/OR logic, nested operators, or wildcards? The <Link href="/platform/features/v2-memory-filters">Memory Filters v2 guide</Link> walks through every filter pattern with working examples. </Tip>

4. Audit everything for an app

python
app_scope = {
    "AND": [
        {"app_id": "concierge_portal"}
    ],
    "OR": [
        {"user_id": "*"},
        {"agent_id": "*"}
    ]
}

page = client.get_all(filters=app_scope, page=1, page_size=20)
<Info> Wildcards (`"*"`) include only non-null values. Use them when you want "any agent" or "any user" without limiting results to null-only records. </Info>

5. Clean up a session

python
client.delete_all(
    user_id="customer_6412",
    run_id="itinerary-2025-apr"
)
<Info icon="check"> A successful delete returns `{"message": "Memories deleted successfully!"}`. Run the previous `get_all` call again to confirm the session memories were removed. </Info>

Verify the feature is working

  • Run client.search with your filters and confirm only expected memories appear. Mismatched identifiers usually mean a typo in your scoping.
  • Check the Mem0 dashboard filter pills. User, agent, app, and run should all show populated values for your memory entry.
  • Call client.delete_all with a unique run_id and confirm other sessions remain intact (the count in get_all should only drop for that run).

Best practices

  • Use consistent identifier formats (like team-alpha or app-ios-retail) so you can query or delete entire groups later
  • When debugging, print your filters before each call to verify wildcards ("*"), lists, and run IDs are spelled correctly
  • Combine entity filters with metadata filters (categories, created_at) for precise exports or audits
  • Use run_id for temporary sessions like support tickets or experiments, then schedule cleanup jobs to delete them

For a complete walkthrough, see the Partition Memories by Entity cookbook.

<CardGroup cols={2}> <Card title="Master Memory Filters" description="Deep dive into JSON logic, operators, and wildcard behavior." icon="sliders" href="/platform/features/v2-memory-filters" /> <Card title="Partition Memories in Practice" description="Follow the essentials cookbook to implement scoped workflows." icon="book-open" href="/cookbooks/essentials/entity-partitioning-playbook" /> </CardGroup>