Skip to main content

Why Memories exist

Most applications start every conversation from scratch. Memories let your agent carry useful user-specific context from one session to the next, so responses become more personal over time. A memory is one unit of context: a stated preference, an inferred trait, a past conversation, a decision, feedback, or a fact your agent should be able to retrieve later. HydraDB stores every memory in a structural context graph, so related preferences surface together at retrieval time. Use Memories when the context belongs to a specific user, workspace, or session. Use Knowledge when the context should be shared across your database - see Knowledge vs Memories for the side-by-side comparison.

The mental model: user context vs. shared context

Before you ingest anything, ask one question: should every user be able to retrieve this? If the answer is yes, store it as Knowledge. If the answer is no, store it as a Memory. HydraDB can infer various kinds of things for memories. see more Good candidates for Knowledge ingestion include product docs, policy PDFs, internal wikis, Slack exports, and other shared material that should be reusable across users.

What happens when you ingest a memory

When you call POST /context/ingest with type=memory, HydraDB turns your input into queryable user context. The pipeline works like this:
  1. Parse the input - raw text, markdown, or user–assistant pairs.
  2. Optionally infer meaning - if infer: true, HydraDB extracts the underlying preference, trait, or fact.
  3. Process and index the result - the memory is prepared for retrieval.
  4. Make it queryable - later, POST /query can retrieve it with type: "memory" or type: "all".
The most important choice is infer. It decides whether HydraDB should extract the useful memory from raw signal, or store exactly what you send. Ingestion is asynchronous, so memories are not queryable immediately. Poll GET /context/status until the memory reaches at least graph_creation, or wait for indexing_status to become completed if you need full indexing. Alternatively, register a webhook to be notified when indexing finishes instead of polling.

Choose the right input shape

Each memory item should usually provide either text or user_assistant_pairs, not both. Choose the shape based on the content you already have.

Text

Use text for any prose input - plain observations, captured facts, preference statements, meeting notes, memos, or semi-structured records. Set is_markdown: true when the content uses markdown syntax (headings, lists, code blocks) and you want HydraDB to preserve that structure during chunking and embedding. Leave is_markdown unset (or false) for plain prose.

User–assistant pairs

Use user_assistant_pairs when the useful signal comes from dialogue, especially when the preference is implied rather than explicitly stated.

Memory fields

The full schema lives on POST /context/ingest. For type=memory, send a JSON-stringified memories array; each item can use the fields below.

Request-level fields

memories item fields

Provide either text or user_assistant_pairs. Use infer: true for raw signal that needs extraction, and infer: false for memories you have already structured.
Memory metadata encoding: memories is itself a JSON-stringified multipart field. Within each memory item, metadata is currently validated as a JSON-encoded string, while additional_metadata / additional_metadata is validated as an object. If you pass metadata as an object, the API returns 400 INVALID_INPUT.

Scope memories to the right user or workspace

Memories live in their own store, separate from Knowledge. The POST /query endpoint reaches each store through the type parameter:
  • type: "memory" queries the Memories store (vectorstore_status.memories).
  • type: "knowledge" queries the Knowledge store (vectorstore_status.knowledge).
  • type: "all" runs both in parallel and returns a single merged, re-ranked result set.

For personalized responses grounded in shared context, use type: “all” on a single POST /query call. HydraDB runs both stores in parallel and merges results for you. See Query.


HydraDB automatically builds an entity and relationship context graph from your content. Forceful relations are different: they let you explicitly declare that memory items are connected. Use forceful relations when you already know two memories belong together, even if the text does not make that connection obvious. For example, a user’s tone preference and format preference may be separate memory items, but you may want them retrieved together. Set forceful relations with the relations field on any item in the memories array:
Use ids for new integrations.
At query time, linked memories return in the additional_context field of the query response. Forceful relation expansion is enabled by default for thinking-mode queries. Set query_forceful_relations=false to disable it. To make relations work reliably, control the id for the memories you ingest. You need stable IDs in order to link memories together.
Memory items only. Forceful relations work within the same store. Pointing a Memory item at a Knowledge ID, or a Knowledge item at a Memory ID, silently returns nothing at query time.
thinking mode only. Forceful-relation context is only fetched when mode: "thinking" is set on the query request. Passing query_forceful_relations: true with mode: "fast" is not an error - the flag is silently ignored. Set mode: "thinking" to enable forceful-relation expansion.

Minimal working example

This example stores a user preference scoped to one user.
The response returns a id for each memory and an initial status of queued. The payload is wrapped in the standard envelope, so the fields live under data (response.data.results):
Poll GET /context/status with the returned id until indexing_status is completed. You can also use webhooks when available. After indexing, the memory can surface in POST /query when called with type: "memory" or type: "all".

Choose whether HydraDB should infer the memory

The infer flag is the key design decision. It controls how much extraction work your application does before sending content to HydraDB.

Use infer: true when the input is raw signal

With infer: true, you give HydraDB messy or indirect evidence - dialogue, logs, behavior, feedback, or observations - and HydraDB extracts the useful preference, trait, or fact. For example, instead of writing custom logic to decide whether a user prefers dark mode, you can send a stream of UI events. If the user switched to dark mode, kept it for a month, briefly toggled back to light mode, then reverted, HydraDB can infer the useful memory: "user prefers dark mode". Use infer: true for:
  • Dialogue where the preference is implicit.
  • Behavior logs and event streams.
  • Feedback like “the last summary was too long.”
  • Any input where the useful memory needs to be derived from raw context.
custom_instructions only takes effect when infer: true. Use it to guide extraction, for example: "Focus on UI and notification preferences only".

Use infer: false when the input is already the memory

With infer: false, HydraDB stores and indexes exactly what you send. There is no extraction step. Use infer: false for:
  • Facts you already captured, such as "User's plan tier is Pro".
  • Pre-structured notes.
  • Content you want to retrieve exactly as written.
Rule of thumb: infer: false is faster and deterministic. infer: true is better when your input is raw, noisy, or indirect.

Example: infer a preference from behavior

Here is a more realistic example. A user has been toggling dark mode in your app. Instead of extracting the preference yourself, send the raw behavior log and let HydraDB infer the useful memory.
Later, when POST /query runs with type: "memory" and a query like "what UI settings does the user prefer?", HydraDB can return the inferred preference - for example, "prefers dark mode" - rather than the raw event log. If you sent the same input with infer: false, HydraDB would index the event log verbatim. That is useful only when you want to retrieve the log itself.

Common mistakes