Mem0’s Python SDK exposes a memory_type parameter on add(). The underlying MemoryType enum defines three values, but only one of them is wired up. This page states plainly which is which so you don’t build against a type that doesn’t exist yet.
Python OSS only (Memory/AsyncMemory). Pass memory_type="procedural_memory" and agent_id to add(). Not available on the Platform MemoryClient, and not available in the TypeScript SDK (OSS or Platform).
Semantic memory
semantic_memory
Not implemented
Defined in the MemoryType enum but never read anywhere else in the codebase. Passing it to add() raises a validation error. There is no evidence in this repo of a roadmap date for this.
Episodic memory
episodic_memory
Not implemented
Same as above: defined, never wired into the extraction pipeline, rejected by validation, no documented roadmap.
Only procedural_memory is a real, working value. Calling memory.add(messages, memory_type="semantic_memory") (or episodic_memory) is rejected and tells you to pass procedural_memory instead. Sync Memory.add() raises Mem0ValidationError; AsyncMemory.add() raises a plain ValueError.
Procedural memory stores step-by-step task knowledge (how an agent performs a workflow) rather than facts about a user. It requires agent_id:
from mem0 import Memorymemory = Memory()memory.add( [ {"role": "user", "content": "Book a flight from SFO to NYC"}, {"role": "assistant", "content": "1. Search flights. 2. Filter by price. 3. Confirm booking."}, ], agent_id="travel-agent", memory_type="procedural_memory",)
Omit memory_type entirely and Mem0 stores the messages as an ordinary memory: there is no semantic/episodic pathway for it to fall into. Any other explicit value is rejected by validation rather than quietly falling back to an ordinary memory.
Outside of the procedural_memory special case, Mem0 does not sort memories into named types. Every memory is scoped by the identifiers you pass in, and the same identifiers are used to retrieve it later:
user_id: ties a memory to a specific person or account.
agent_id: ties a memory to a specific agent or assistant persona.
run_id: ties a memory to a specific session, task, or conversation thread.
app_id (Platform only): ties a memory to a specific application or tenant, in addition to the three above. See Entity-Scoped Memory.
At least one identifier is required on add(). Passing more than one narrows the scope further (for example, user_id + run_id together).
from mem0 import Memorymemory = Memory()memory.add( "I'm Alex and I prefer boutique hotels.", user_id="alex", run_id="trip-planning-2025",)results = memory.search( "Any hotel preferences?", filters={"user_id": "alex", "run_id": "trip-planning-2025"},)
Use run_id when you want a set of memories to stay tied to one session or task; use user_id alone for anything that should persist across every session for that person.
When infer=True (the default) on add(), Mem0 runs a single pipeline rather than routing through separate type-specific paths:
Context gathering: pulls the most recent messages already stored for the same user_id/agent_id/run_id scope.
Existing memory retrieval: embeds the new messages and runs a vector search against memories already in that same scope, to find candidates that might need to change.
Extraction: a single LLM call compares the new messages against the retrieved candidates and decides, per fact, whether to ADD, UPDATE, DELETE, or leave a memory alone.
Alongside this, both OSS and Platform extract named entities (people, places, organizations) from memory text and use shared entities between memories to boost related results at search time. On Platform, that entity graph is also queryable directly; see Graph Memory. In OSS, entities only affect ranking, there is no separate graph to query.
Avoid storing secrets or unredacted PII in memories: they are retrievable by design. Encrypt or hash sensitive values before calling add().