Memory

hm_store

Save a node to memory, with its relationships, in one atomic write

hm_store saves one piece of knowledge — a person, decision, fact, preference, project — as a node in your memory graph. After the write, HyperMemory enriches the node in the background: it expands the description, extracts structured data and detects the people, organizations and other entities it mentions.

Call hm_recall first to check whether the memory already exists. If it does, update it with hm_update instead of creating a near-duplicate.

Parameters

ParameterTypeDefaultDescription
keystringrequiredStable identifier, up to 512 bytes. Use the form {type}_{name}, e.g. decision_jwt_auth, person_alice, tech_redis.
descriptionstringrequiredWhat the memory says. Aim for 80–300 characters that lead with the point, in the words someone would search with later (the hard limit is 16 KiB). Keep commit hashes, IDs, file paths, hosts and ports in data.
node_typestring—One canonical node type (see Nodes). Common aliases such as tech, org and doc are accepted. If omitted or unknown, the type comes from the key’s prefix (decision_… → decision), falling back to fact. Free-form types are never stored.
dataobject—Structured fields: dates, statuses, IDs, URLs, numbers. Up to 64 KiB.
relationshipsarray—Up to 99 relationships written in the same transaction. See below.
if_exists"update" | "keep" | "error""update"What to do if key already exists. update changes only the fields you send; keep leaves the node untouched (relationships are still added); error refuses with node_exists.
search_textstring—Extra keywords for keyword search, without putting them in the description. Up to 32 KiB.
assetsstring[]—Up to 32 references to attached assets.
session_idstring"default"The conversation’s id. Nodes written and recalled in one conversation are grouped so hm_get_chat_context can return them together.

An existing node is never replaced wholesale: with the default if_exists: "update", fields you leave out keep their current values. Any write sets the node’s enrichment_status back to pending so it is enriched again.

Relationships

Each entry in relationships is either a binary edge (to_key) or a hyperedge (participant_keys):

FieldTypeDescription
relationshipstringRequired. Plain-language wording of the connection and why it holds, e.g. "search pipeline uses SurrealDB for hybrid vector+BM25 retrieval".
to_keystringKey of the node this connection points to. It must already exist.
participant_keysstring[]For a hyperedge: 2–256 node keys, including this node’s own key. Used instead of to_key.
rolesstring[]Optional role for each participant, in participant_keys order.
relationship_typestringOptional ontology relationship type. Defaults to related_to.
descriptionstringOptional longer explanation. Defaults to the relationship wording.
dataobjectOptional structured facts about the connection.

Every node a relationship points to must already exist. If one doesn’t, the whole call fails with validation_failed and nothing is written — store the target first, or store this node without that relationship and add it later with hm_add_relationships.

A node stored without relationships is an orphan until something connects it. Always include at least one relationship.

Response

FieldDescription
storedThe node key that was written.
action"created", "updated" or "kept".
existedWhether the key existed before this call.
createdWhether a new node was created.
typeThe canonical node type the node was filed under.
relationships_writtenHow many relationships were written.
effectsCounts of the entities this write changed.
memory_versionThe graph’s version after the write.
replayedtrue when the same write had already been applied (safe retry).

When the caller is an organization admin with organization write mode on, the result also carries write_target.

Example

{
  "key": "decision_jwt_auth",
  "node_type": "decision",
  "description": "Chose JWT access tokens over server sessions for the public API so it can scale statelessly across regions.",
  "data": {
    "chosen": "JWT",
    "rejected": ["server sessions"],
    "rationale": "stateless horizontal scaling",
    "date": "2026-10-01"
  },
  "relationships": [
    {
      "to_key": "project_public_api",
      "relationship": "authentication decision for the public API project"
    }
  ]
}
{
  "stored": "decision_jwt_auth",
  "action": "created",
  "existed": false,
  "created": true,
  "type": "decision",
  "relationships_written": 1
}

Writing good memories

  • One node per thing a later session would act on. A rule the user states is a preference; a choice between alternatives is a decision (with chosen, rejected, rationale in data); a finding or risk is a fact. What happened is an event that links to them.
  • Lead with the point. Descriptions are what search and people read first.
  • Link to specific siblings, not only to a project hub: the fix to the bug, the deploy to the change it shipped.

Equivalents

curl -X POST https://api.hypermemory.io/api/v1/memory/store \
  -H "Authorization: Bearer $HYPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"key":"fact_launch_date","description":"Product launches on 2026-11-02 after the security review sign-off.","node_type":"fact"}'