---
title: "Graph Namespacing"
---

> Documentation Index
> Fetch the complete documentation index at: https://truememory-docs.mvstg.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Graph Namespacing

Truememory supports graph namespacing through `graph_id`s — named, isolated knowledge graphs that live alongside user memory in the same Truememory instance. Namespaces hold knowledge that belongs to no user — a product catalog, company policies, a coaching methodology, a support playbook — and can be blended into any user's memory retrieval.

Graph namespacing is particularly useful for:

- **Multi-tenant applications**: Isolate curated knowledge between different customers or organizations
- **Domain-specific knowledge**: Create specialized graphs for different domains or use cases
- **Team collaboration**: Allow different teams or apps to maintain their own knowledge spaces
- **Testing environments**: Keep experimental knowledge separate from what production agents see

## How Namespacing Works

In Truememory, every node and edge in the knowledge graph is associated with a group id. Each user's memory already lives in its own automatically managed namespace, keyed by the user's identity. A `graph_id` names an additional namespace: nodes and edges written under the same `graph_id` form a cohesive, isolated graph that can be queried, inspected, and curated independently of every user's memory and of other namespaces.

Namespaces meet user memory at retrieval time. When a request includes `graph_ids`, Truememory searches the user's own graph and each named namespace **concurrently**, then fuses the rankings into a single result list:

1. Each graph is searched independently with the same query
2. Rankings are merged — when a user fact and a namespace fact are equally relevant, the user fact wins
3. Every result carries its origin: `scope: "user"`, or `scope: "graph"` with the `graph_id` it came from

A namespace that is empty, nonexistent, or temporarily unreachable contributes no results — the user's own results always come back. A missing namespace is not an error.

## Key Benefits

- **Data isolation**: A request only touches the namespaces it names — no leakage between namespaces or into user memory
- **Simplified management**: Related knowledge lives together, inspectable and curatable as one unit
- **Curated by design**: Namespaces are never written automatically — chat auto-ingest and the agent's `memory_write` tool only touch the user's own graph
- **Flexible architecture**: Per-tenant graphs, per-domain graphs, and user memory coexist in one Truememory instance

Namespaces store facts and entities in a knowledge graph, retrieved and ranked like memory. To give an agent passages quoted from an uploaded document instead, use [Brains (knowledge packs)](/ai-gateway/knowledge-packs/) — the two work well together.

## Naming Namespaces

A `graph_id` is a freeform slug. No registration step is required — a namespace exists once data lands in it. Ids must match:

- Lowercase letters, digits, underscores, or hyphens
- Start with a letter, 3–64 characters long
- Must **not** begin with a bare UUID — that shape is reserved for user memory namespaces. Ids that need UUID-based uniqueness (an org id, a tenant id) are fully supported — put a short prefix in front: `org_9f8e4b2a-…`

Invalid ids are rejected with `400 VALIDATION_ERROR` before any operation runs.

| graph_id | Verdict |
|---|---|
| `acme__product_kb` | ✅ Recommended shape |
| `kb__sales_playbook` | ✅ Recommended shape |
| `org_9f8e4b2a-e89b-12d3-a456-426614174000` | ✅ UUID-based uniqueness, prefixed |
| `Sales-KB` | ❌ Uppercase not allowed |
| `kb` | ❌ Too short (min 3 characters) |
| `123_team_docs` | ❌ Must start with a letter |
| `9f8e4b2a-e89b-12d3-a456-426614174000` | ❌ Bare UUID — reserved for user memory |

## Adding Data to a Namespace

Populate a namespace through [Add Data to Memory](/memory/add-data/) with a `graph_id` target instead of `agent_id`. Writing to a namespace requires a **service token** — end-user tokens receive `403 AUTH_SERVICE_REQUIRED`:

```bash
POST https://your-tie-host/memories/graph
Authorization: Bearer $SERVICE_TOKEN
Content-Type: application/json
```

```json
{
  "graph_id": "acme__coaching_kb",
  "data": "The beginner running program has three phases: base, build, peak.",
  "type": "text",
  "source_description": "Coaching methodology import"
}
```

Provide exactly one of `agent_id` (user memory) or `graph_id` (namespace). Ingestion is asynchronous — the `202` response includes a `job_id` to poll via [`GET /memories/graph/status/{job_id}`](/memory/add-data/), or pass `wait: true` for synchronous writes. Writers to one namespace serialize cluster-wide, so concurrent imports never race entity extraction.

## Records About People

A namespace often holds one profile record per member of a community. Follow the [record-ingestion conventions](/memory/add-data/#ingesting-records): stamp each record with the member's id, put the id in the document, and frame the document as third-party. Each member then becomes one `Person` node carrying the member's id as `external_id`:

```cypher
MATCH (p:Person {external_id: $memberId}) RETURN p
```

Identity comes from the id, not the name — two members who share a name keep two separate nodes, and a re-sent record finds its member even after a name change. Records that reference members by id (an event's host, a group's owner) get real edges to those members automatically.

## Using a Namespace in Chat

For agents running through Truememory's chat, pass `graph_ids` directly on the [chat completions](/ai-gateway/chat-completions/) request — Truememory blends namespace facts into the memory context it injects, with no prompt composition on your side:

```json
POST /v1/chat/completions
{
  "model": "chatbot",
  "messages": [{"role": "user", "content": "what running program fits a beginner"}],
  "graph_ids": ["acme__coaching_kb"]
}
```

The conversation itself still only ingests into the user's graph — `graph_ids` affects what the agent *reads*, never what gets written.

If you run your **own** LLM, specify `graph_ids` on [Retrieve Memory Context](/memory/retrieval/#retrieve-memory-context) instead — the endpoint that returns exactly what Truememory injects during a chat turn. Namespace facts blend into the per-message retrieval; the cached session context block remains user-only. Everything else about the endpoint is unchanged, and omitting the field gives the existing user-only behavior.

```bash
POST https://your-tie-host/memories/retrieve
Authorization: Bearer $TOKEN
Content-Type: application/json
```

**Request body:**

```json
{
  "query": "what running program fits a beginner",
  "agent_id": "chatbot",
  "graph_ids": ["acme__coaching_kb"]
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `graph_ids` | string[] | No | Up to 4 namespaces to blend into the retrieval. Duplicates are deduped. |

**Response** (trimmed to the retrieval half) — one ranked list; `scope` and `graph_id` identify each fact's origin:

```json
{
  "retrieval": {
    "results": [
      {
        "id": "edge-uuid-1",
        "name": "TRAINS_FOR",
        "content": "User is training for a marathon in October",
        "source_name": "User",
        "target_name": "Marathon",
        "scope": "user",
        "graph_id": null
      },
      {
        "id": "edge-uuid-2",
        "name": "HAS_PHASE",
        "content": "The beginner running program has three phases: base, build, peak",
        "source_name": "Beginner running program",
        "target_name": "Training phases",
        "scope": "graph",
        "graph_id": "acme__coaching_kb"
      }
    ],
    "prompt": "## Relevant memories for this message\n..."
  }
}
```

The rest of the payload — the context block, timings, cache diagnostics — is unchanged; see [Retrieve Memory Context](/memory/retrieval/#retrieve-memory-context).

The same field works on [Search Memory](/memory/retrieval/#search-memory) when you want raw ranked fact edges without the context block or rendered prompts — results carry the same `scope` and `graph_id` tags.

:::note
`graph_ids` on chat, retrieve, and search always **blends** — the user's own graph is searched too, and these endpoints require a subject user. There is no namespace-only mode on them; to view a namespace by itself, use the inspection endpoints below.
:::

## Inspecting a Namespace

Inspection endpoints target exactly one graph per call — the user's own, or a namespace via `graph_id`. No blending: pass `graph_id` to see the namespace, omit it for the user's graph. `graph_id` is mutually exclusive with `agent_id`.

```bash
GET https://your-tie-host/memories/graph?graph_id=acme__product_kb
GET https://your-tie-host/memories/timeline?graph_id=acme__product_kb
Authorization: Bearer $TOKEN
```

- [Memory Graph](/memory/building-uis/#memory-graph-visualization) renders the namespace's entities and facts for a graph UI.
- [Memory Timeline](/memory/building-uis/#memory-timeline) returns an entity feed, recently active first — useful for reviewing what a namespace contains.

Inspecting a namespace requires a **service token** — end-user tokens receive `403 AUTH_SERVICE_REQUIRED` from both endpoints. A namespace holds cross-user data that is scoped to no caller in particular, so its inspection views are gated exactly like namespace writes. Service tokens **with no user attached** work fine: curation tooling and org-level dashboards do not need to impersonate a user.

Reading the user's own graph or timeline (omitting `graph_id`) is unaffected and still works with an end-user token.

:::caution[Changed behaviour]
Namespace inspection was previously open to any authenticated caller. **Both `GET /memories/graph?graph_id=…` and `GET /memories/timeline?graph_id=…` now return `403 AUTH_SERVICE_REQUIRED` for end-user tokens.** If a browser client calls either one directly with an end-user token, proxy the call through your backend with a service token instead.
:::

These views are single-scope — one call shows one graph, never a mix. A memories dashboard maps this onto a filter: a "my memories" tab calls the endpoint without `graph_id`, an "organization" tab calls it with one (through your backend). Same response shape, same cursor pagination, same rendering components. There is no server-side blended feed (the two graphs have independent cursors); if you need an interleaved "all memories" scroll, merge the two cursor streams client-side by timestamp and badge each item by origin.

## Managing Namespace Memories

Deleting or merging namespace facts affects every reader, so both operations require a **service token**. End-user tokens receive `403 AUTH_SERVICE_REQUIRED`.

Delete (invalidate) one fact edge — the `agent_id` path segment is ignored when `graph_id` is present:

```bash
DELETE https://your-tie-host/memories/{agent_id}/{memory_id}?graph_id=acme__product_kb
Authorization: Bearer $SERVICE_TOKEN
```

Merge duplicate entities within a namespace — same semantics as a [user-scoped merge](/memory/building-uis/#merge-two-memories):

```json
POST /memories/{agent_id}/merge
{
  "primary_id": "node-uuid-keep",
  "duplicate_id": "node-uuid-fold-in",
  "graph_id": "acme__product_kb"
}
```

Empty an entire namespace — a hard delete of every node, edge, and episode in it, from both stores:

```bash
POST https://your-tie-host/memories/{agent_id}/clear?graph_id=acme__product_kb
Authorization: Bearer $SERVICE_TOKEN
```

The response's `scope` field names what was cleared (`"graph"` plus the `graph_id`, vs `"user"` for a pair clear), so a caller can verify the blast radius. See [Clear all memories](/memory/deleting-data/#clear-all-memories) for details.

Export and import remain user-scoped — the rest of the namespace lifecycle (list, export) ships separately.

## Best Practices for Graph Namespacing

- **Consistent naming**: Use a consistent naming convention for your `graph_id` values — we recommend `{org_or_app}__{topic}`, with underscores over hyphens. A namespace *is* its id (there is no rename), so choose descriptive, permanent names: `acme__support_kb` ages better than `kb2`. When ids must be globally unique rather than readable, derive them from your own identifiers: `org_{org_uuid}`.
- **Documentation**: Maintain documentation of your namespace structure and purpose — which namespaces exist, what each contains, and who writes to it. Truememory does not maintain a registry.
- **Granularity**: Choose an appropriate level of granularity for your namespaces
  - Too many namespaces can lead to fragmented data and extra search fan-out per request
  - Too few namespaces may not provide sufficient isolation, mixing unrelated domains into every result list
  - One namespace per coherent knowledge domain is a good default
- **Cross-namespace queries**: Retrieval blends the user's graph with up to 4 namespaces in one call. When you need more than that — or a combined view of inspection feeds — perform multiple queries across namespaces and combine the results in your application logic.

## A Multi-Tenant Example

Map each tenant to its own namespace prefix, and pass the tenant's namespaces on every retrieval for that tenant's users:

```json
{
  "query": "what is the refund policy",
  "agent_id": "support-agent",
  "graph_ids": ["acme__support_kb", "acme__product_kb"]
}
```

Users of tenant `acme` get Acme's policies blended with their personal memory; tenant `globex` passes `globex__support_kb` and never sees Acme's data. Isolation is structural — a request only touches the namespaces it names.

Source: https://truememory-docs.mvstg.com/memory/graph-namespacing/index.mdx
