Skip to main content
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.
Entity IDs vs. graph entities. This page covers the user_id / agent_id / app_id / run_id identifiers used to scope memories. These are different from the graph entities (the people, places, and concepts surfaced in Graph Memory).
Want the long-form tutorial? The Partition Memories by Entity cookbook walks through multi-agent storage, debugging, and cleanup step by step.
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

Configure access

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

  • Writes (client.add) accept any combination of these fields. app_id and run_id are stored on every memory the call produces. user_id and agent_id behave differently on the default extraction path: each extracted fact is attributed to whoever stated it, so a record carries user_id or agent_id, not both. Absent fields default to null.
  • Reads (client.search, client.get_all, exports) accept the same identifiers inside the filters JSON object. Deletes (client.delete_all) scope via query parameters instead; the SDK builds these for you when you pass user_id=..., run_id=..., and so on.
  • Unmentioned entities are not constrained: Passing only {"user_id": "alice"} matches on user_id alone. It does not require agent_id, app_id, or run_id to be null, so records that also have those fields set are still returned.
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.

Choose the right identifier

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

Configure it

The example below adds memories with entity tags:
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.
Passing both user_id and agent_id to client.add does not produce records with both fields set. On the default extraction path, each extracted fact is attributed to the speaker who stated it: facts from user messages are stored with user_id set and agent_id null, facts from assistant messages with agent_id set and user_id null. app_id and run_id are carried on every record either way.As a result, {"AND": [{"user_id": ...}, {"agent_id": ...}]} returns nothing for memories created this way. Use OR to match either scope. Records with both fields populated only come from Direct Import (infer=False), which writes your text verbatim without attribution splitting.
The HTTP equivalent uses POST /v3/memories/add/ 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
2. Retrieve by user scope
3. Retrieve by agent scope
A search that ANDs user_id and agent_id only matches records that have both fields set, which the default extraction path never produces. Use OR when you want either scope to match on its own.
Want to experiment with AND/OR logic, nested operators, or wildcards? The Memory Filters v2 guide walks through every filter pattern with working examples.
4. Audit everything for an app
Wildcards ("*") include only non-null values. Use them when you want “any agent” or “any user” without limiting results to null-only records.
5. Clean up a session
A successful delete returns {"message": "Memories deleted successfully!"}. Run the previous get_all call again to confirm the session memories were removed.

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.

Master Memory Filters

Partition Memories in Practice