Memories
Store, retrieve, update, and delete memories. Memories are the core building blocks of Hebbrix - they store knowledge that your AI can recall later.
01. Overview
A memory is a piece of information that you want your AI to remember. This could be:
- User preferences ("User prefers dark mode")
- Facts from conversations ("User's birthday is January 15th")
- Knowledge from documents ("The API rate limit is 100 requests per minute")
- Any structured or unstructured data you want to store
02. Choose an ingestion path
Choose explicit raw storage when you need to preserve source text, or fact extraction when you need atomic, updatable facts. Neither is universally better for retrieval. The smart /v1/memoriesendpoint defaults to asynchronous extraction; /v1/memories/rawexplicitly skips extraction. Storage, indexing and account quotas still apply.
| Surface | Purpose | Readiness contract |
|---|---|---|
POST /v1/memories | General smart write with infer: true; infer: false stores source text. | Queued extraction by default. Follow the returned poll_url; do not assume a 202 is searchable. |
POST /v1/memories/process | Explicit conversation extraction and revision events. | Queued by default; async_dispatch: false waits for inline processing. Inspect indexing status separately. |
POST /v1/memories/raw | Store already-authored facts without inference. | Returns an ID; indexing may still be pending. Exact duplicates reuse that ID rather than create a second row. |
POST /v1/fast/ingest | Low-overhead direct ingestion, not smart revision. | Use its indexing receipt; an accepted write is not a completed search barrier. |
POST /v1/chat/completions | Optional drop-in retrieval and post-response learning. | Learning is consented, queued work. Poll its learning job before a dependent session read. |
Carry the same collection_id and user_id through writing, search, profile and chat. Poll the returned job with the same owner credentials. Session IDs are conversation provenance, not an end-user isolation boundary. The public OpenAPI is the full customer API, including account management; this table is the focused memory integration path.
| /memories/raw | /memories/process | |
|---|---|---|
| What it does | Stores your text verbatim, then indexes it (with automatic context-window chunking) in the background. No LLM. | Runs an LLM to extract clean atomic facts, resolve references, and supersede outdated info. |
| Retrieval recall | Preserves original wording, including irrelevant or outdated statements. | Normalizes facts and resolves corrections, but may omit or paraphrase source text. Evaluate recall on your workload. |
| Write latency | No extraction call; indexing readiness is separate. | Queued acknowledgement by default; completion depends on models, indexing and worker load. No three-second guarantee. |
| LLM cost | None | Per-memory LLM call |
| Supersession / dedup | Exact content deduplication, not semantic revision. Different old and corrected facts remain separate. | Yes. Corrections replace old facts; redundant facts are merged |
Use explicit /raw storage for
- Chat history & agent memory
- Documents, notes, transcripts
- Fact stores & knowledge bases
- Bulk import & high-throughput writes
- Anywhere recall and speed matter most
Use /process when you need
- "Latest value wins" (users correct/update facts over time)
- Conflict resolution between contradictory facts
- Deduplicated, normalized atomic facts
- A curated knowledge base with explicit revision history
Revision review compares the original message with current facts in the same owner scope. A replacement must identify the same subject and an assertion that has changed; adding another job, car or relative does not by itself retire an existing one. Reviewed edits bind the exact predecessor and replacement and preserve unaffected clauses. Semantic review is fallible: inspect UPDATE events and their predecessor IDs rather than assuming every submitted correction was applied. Session and run IDs record provenance, not new owners.
03. Endpoints
04. Response shape (POST /v1/memories)
By default, POST /v1/memories returns HTTP 202 withjob_id, status and poll_url. This accepts an extraction job; it does not return an inlineresults array. Poll the returned URL with the same credentials until the job completes or fails.
POST /v1/memories → HTTP 202
{
"job_id": "job_example",
"status": "queued",
"poll_url": "/v1/memory-jobs/job_example"
}For direct storage, send infer: false andwait_for_index: true. This skips fact extraction and returns an inline HTTP 200 result. Inspect searchable andindex_wait_status; do not assume a bounded indexing wait succeeded. For inline extraction instead, use /v1/memories/process withasync_dispatch: false.
collection_id: the resolved collection the memories were stored in.processing_status"async": the memory row is persisted and indexed in the background. PollGET /v1/memories/{id}(or use Background Jobs) until it is searchable.created_count,updated_count,skipped_count: a summary derived from the per-row events (ADD / UPDATE / NOOP).
results[].id is the PARENT Memory.id, retrievable via GET /v1/memories/{id}. The proposition-style ids you may see in search results (like uuid::subj=...) are a search-layer artifact and are NOT returned by POST /v1/memories.POST /v1/memories (infer: false, wait_for_index: true) → HTTP 200
{
"results": [
{ "id": "mem_abc123", "event": "ADD", "memory": "User prefers dark mode" }
],
"collection_id": "col_default",
"processing_status": "async",
"created_count": 1,
"updated_count": 0,
"skipped_count": 0
}05. Destructive operations: dry-run & confirmation
Bulk deletes that wipe everything support two opt-in safety controls so you can preview the blast radius before committing. Both are backward compatible.
DELETE /v1/memories/all?dry_run=truereturns{"deleted_count":0,"dry_run":true,"would_delete":N}and deletes NOTHING. Use it to preview how many memories would be removed.- An optional header
X-Hebbrix-Confirm-Destructive: all-memoriesguards the real delete. If the header is PRESENT it must equalall-memoriesor the request is rejected with HTTP 400. If OMITTED, behavior is unchanged. - The same pattern applies to
DELETE /v1/collections/all, whose confirm value isall-collections.
import requests
BASE = "https://api.hebbrix.com/v1"
H = {"Authorization": "Bearer <your-api-key>"}
# 1) Dry-run first: preview the blast radius, deletes NOTHING
preview = requests.delete(
f"{BASE}/memories/all",
headers=H,
params={"dry_run": "true"},
).json()
# {"deleted_count": 0, "dry_run": True, "would_delete": 1287}
print(f"Would delete {preview['would_delete']} memories")
# 2) Confirmed delete: header must equal "all-memories" if present
result = requests.delete(
f"{BASE}/memories/all",
headers={**H, "X-Hebbrix-Confirm-Destructive": "all-memories"},
).json()
print(f"Deleted {result['deleted_count']} memories")
# Same safety pattern for collections (confirm value "all-collections")
requests.delete(
f"{BASE}/collections/all",
headers={**H, "X-Hebbrix-Confirm-Destructive": "all-collections"},
)06. Code Examples
Create a Memory
import asyncio
from hebbrix import MemoryClient
async def main():
async with MemoryClient(api_key="mem_sk_...") as client:
collection = await client.collections.create(name="my-agent")
# Simple memory (SDK calls the smart-extract POST /v1/memories endpoint)
memory = await client.memories.create(
collection_id=collection["id"],
content="User's favorite color is blue",
)
# Memory with importance + metadata (tags live inside metadata)
await client.memories.create(
collection_id=collection["id"],
content="User completed Python certification on 2024-01-15",
importance=0.8,
metadata={
"tags": ["education", "python"],
"certification_provider": "Coursera",
"completion_date": "2024-01-15",
},
)
asyncio.run(main())Process Conversations (fact extraction)
import os
import requests
BASE = "https://api.hebbrix.com/v1"
H = {"Authorization": f"Bearer {os.environ['HEBBRIX_API_KEY']}"}
# POST /v1/memories/process: hand it a chat transcript; the backend
# extracts atomic facts, detects conflicts, and emits ADD/UPDATE/NOOP
# events for each resulting memory. Explicit false requests inline results;
# omit it for the default 202 job receipt and poll the returned poll_url.
r = requests.post(
f"{BASE}/memories/process",
headers=H,
json={
"async_dispatch": False,
"messages": [
{"role": "user", "content": "Hi! My name is Alex and I work at Acme Corp."},
{"role": "assistant", "content": "Nice to meet you, Alex!"},
]
},
)
result = r.json()
print(f"Facts extracted: {result['facts_extracted']}")
print(f"Created: {result['memories_created']}")
print(f"Updated: {result['memories_updated']}")
# Later, user corrects their name: the old memory is updated
r = requests.post(
f"{BASE}/memories/process",
headers=H,
json={
"async_dispatch": False,
"messages": [
{"role": "user", "content": "Actually, my name is Jordan, not Alex."},
{"role": "assistant", "content": "No problem, Jordan!"},
]
},
)
result = r.json()
# result["events"][0]["event"] == "UPDATE"
# result["events"][0]["previous_content"] == "Alex is the user's name"Batch Create
import os
import requests
BASE = "https://api.hebbrix.com/v1"
H = {"Authorization": f"Bearer {os.environ['HEBBRIX_API_KEY']}"}
# POST /v1/memories/batch: create multiple memories in one request (max 100).
# Note: collection_id is top-level, NOT per-memory.
memories = [
{"content": "User prefers email over phone calls"},
{"content": "User works in software development"},
{"content": "User is based in EST timezone"},
]
r = requests.post(
f"{BASE}/memories/batch",
headers=H,
json={
"memories": memories,
"collection_id": "col_default",
},
)
result = r.json()
print(f"Created {result['created']} memories")List and Filter (SDK)
import asyncio
import os
from hebbrix import MemoryClient
async def main():
async with MemoryClient(api_key=os.environ["HEBBRIX_API_KEY"]) as client:
# --- Explicit page-at-a-time access ---
page = await client.memories.list_page(limit=50)
# page = {"items": [...], "next_cursor": str | None,
# "has_more": bool, "total_count": int}
print(f"Showing {len(page['items'])} of {page['total_count']} memories")
# Fetch the next page if there is one
if page["has_more"]:
next_page = await client.memories.list_page(
cursor=page["next_cursor"], limit=50
)
print(f"Next page has {len(next_page['items'])} memories")
# Filter by collection
page = await client.memories.list_page(
collection_id="col_customer_support", limit=20
)
# --- Simple "give me everything" iteration ---
# iter_all follows next_cursor until has_more == False
async for memory in client.memories.iter_all(collection_id="col_customer_support"):
print(memory["id"], memory["content"][:80])
asyncio.run(main())List and Filter (raw HTTP)
import os
import requests
BASE = "https://api.hebbrix.com/v1"
H = {"Authorization": f"Bearer {os.environ['HEBBRIX_API_KEY']}"}
# List all memories (cursor-paginated response)
r = requests.get(f"{BASE}/memories", headers=H, params={"limit": 50})
page = r.json() # {"items": [...], "next_cursor": ..., "has_more": ..., "total_count": ...}
# Paginate through all
cursor = None
while True:
params = {"limit": 100}
if cursor:
params["cursor"] = cursor
page = requests.get(f"{BASE}/memories", headers=H, params=params).json()
for memory in page["items"]:
process(memory)
if not page.get("has_more") or not page.get("next_cursor"):
break
cursor = page["next_cursor"]07. cURL Examples
/v1/memoriescurl -X POST "https://api.hebbrix.com/v1/memories" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "User prefers dark mode",
"tags": [
"preferences"
]
}'/v1/memories?limit=10curl -X GET "https://api.hebbrix.com/v1/memories?limit=10" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json"/v1/memories/mem_abc123curl -X DELETE "https://api.hebbrix.com/v1/memories/mem_abc123" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json"/v1/memories/processcurl -X POST "https://api.hebbrix.com/v1/memories/process" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"async_dispatch": false,
"messages": [
{
"role": "user",
"content": "My name is Jordan and I work at Acme Corp."
},
{
"role": "assistant",
"content": "Nice to meet you, Jordan!"
}
]
}'/v1/memories/bulk-deletecurl -X POST "https://api.hebbrix.com/v1/memories/bulk-delete" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"ids": [
"mem_abc123",
"mem_def456"
]
}'/v1/memories/allcurl -X DELETE "https://api.hebbrix.com/v1/memories/all" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json"08. Memory Lifecycle
When you create a memory, Hebbrix automatically:
- Embeds the Content. Converts text to vector embeddings for semantic search.
- Calculates Importance. Analyzes content to determine relevance and priority.
- Extracts Entities. Identifies people, places, concepts and adds to knowledge graph.
- Indexes for Search. Adds to BM25 index for keyword matching alongside vectors.
- Detects Conflicts. When using /process, checks for conflicting memories and automatically updates or removes outdated information.