A brain is a named collection of knowledge packs. Instead of listing pack IDs on every chat request, you group the packs once under a brain and attach the whole set with a single brain_id. A user might have one brain for their company handbook, another for a product catalog — each a stable bundle they can point a conversation at.
A brain owns nothing on its own. It is just a name plus a list of which packs are in it. Packs go in and out, and the brain uses whatever packs are in it at the moment of the request. The documents, their ingestion, and their access rules all still live on the packs themselves (see Knowledge Packs).
Think of it like a playlist. The songs (packs) exist independently; a playlist (brain) just references them in one place. Deleting a playlist never deletes the songs, and the same song can sit on many playlists.
Where that metaphor breaks down: a brain only points at its packs — it never copies them. If a pack is deleted after you add it, the brain simply stops returning it, and there is nothing left behind to clean up.
flowchart LR
subgraph Build["Assemble once"]
direction TB
B["Create a brain<br/>name · description"] --> M["Add knowledge packs<br/>POST /brains/{id}/knowledge-packs"]
end
subgraph Use["Every chat request"]
direction TB
R["POST /v1/chat/completions<br/>brain_id"] --> D["Resolve brain → its pack IDs"]
D --> C["Hybrid search + inject as<br/>Relevant Document Context"]
end
M -.->|"brain now holds N packs"| R
style B fill:#f0f9ff,stroke:#3b82f6,color:#181818
style M fill:#ecfdf5,stroke:#10b981,color:#181818
style R fill:#f0f9ff,stroke:#3b82f6,color:#181818
style D fill:#e6f4ff,stroke:#0a99ff,color:#181818
style C fill:#ecfdf5,stroke:#10b981,color:#181818
All brain endpoints require authentication. User-scoped endpoints (/brains) accept either a user Bearer token from Truememory Auth, or the calling app’s service token plus X-On-Behalf-Of: <user_id> when your backend proxies for a user. Admin endpoints (/admin/brains/*) require the service token or an admin user token — see Admin endpoints.
Using a brain in a chat request
This is the whole point of a brain. On a /v1/chat/completions request, pass brain_id (or brain_ids for several at once):
POST https://your-tie-host/v1/chat/completions
Authorization: Bearer $TOKEN
Content-Type: application/json
{
"model": "anthropic/claude-sonnet-4-5",
"messages": [
{ "role": "user", "content": "What's our refund window for annual plans?" }
],
"brain_id": "b1a2c3..."
}Truememory resolves the brain to the pack IDs it currently holds, then runs the same retrieval as knowledge_pack_ids — hybrid search over the packs, top chunks injected under Relevant Document Context. Everything from Knowledge Packs → Retrieval applies: only ready packs are searched, and pack visibility is enforced first.
Grounding a request in several brains
Pass brain_ids to attach more than one brain to the same request — the handbook plus the product catalog, say:
{
"model": "anthropic/claude-sonnet-4-5",
"messages": [{ "role": "user", "content": "Does the Pro plan follow the standard refund window?" }],
"brain_ids": ["b1a2c3...", "d4e5f6..."]
}Every named brain resolves in one lookup, and the packs are merged into a single deduplicated set before retrieval — a pack that sits in two of the brains is searched once. brain_id is the singular alias for the same field: send both and Truememory resolves their union.
A few behaviors worth knowing:
knowledge_pack_idswins. If a request sends a non-emptyknowledge_pack_ids, the brains are ignored and the explicit IDs are used.- An empty brain injects nothing. If the brains hold no packs (or their packs were deleted), no document context is added — the request proceeds normally.
- Only memory-enabled agents resolve them. Brains are read on the same path as memory context. Agents that do not use memory, or requests with memory reads disabled, skip resolution.
- An unreachable brain is a 404. Resolution is scoped to what the caller can read — brains they own, hold an entitlement for, or that are public. An ID that fails that check returns
BRAIN_NOT_FOUNDwith the offending ID indetails, rather than answering with less grounding than you asked for. In a multi-brain request, one bad ID fails the whole request.
Creating and managing a brain
# Create
POST https://your-tie-host/brains
Authorization: Bearer $TOKEN
Content-Type: application/json
{ "name": "Company Handbook", "description": "HR policies and SOPs", "app_id": "vibrantly" }name is required; description and app_id are optional. The response returns the brain with its id, user_id, app_id, and timestamps.
| Endpoint | Behavior |
|---|---|
POST /brains |
Create a brain owned by the caller |
GET /brains |
List the caller’s brains. Filters: app_id; paginate with limit (default 50, max 200) and offset |
GET /brains/{id} |
Fetch one brain |
PATCH /brains/{id} |
Update name and/or description. name cannot be set to null |
DELETE /brains/{id} |
Delete the brain. Its packs are untouched — only the collection is removed |
Adding and removing packs
Add and remove packs through these endpoints. Adding a pack that is already in the brain is safe — it is not added twice.
# Add a pack to a brain
POST https://your-tie-host/brains/{brain_id}/knowledge-packs
Authorization: Bearer $TOKEN
Content-Type: application/json
{ "pack_id": "7f3c..." }| Endpoint | Behavior |
|---|---|
POST /brains/{id}/knowledge-packs |
Add a pack (body { "pack_id": "..." }). The pack must be one the caller owns or a public pack |
GET /brains/{id}/knowledge-packs |
List the packs currently in the brain |
DELETE /brains/{id}/knowledge-packs/{pack_id} |
Remove a pack from the brain (the pack itself is not deleted) |
A brain points at its packs rather than holding copies of them. So a pack that is deleted after you add it simply disappears from the brain’s list and from retrieval — no leftover entry to remove, and no error.
Admin endpoints
The /admin/brains/* block manages brains across all users without the per-user ownership check. It is gated by the Truememory service token (AUTH_SECRET) or an admin user token; end-user tokens cannot reach it.
The admin surface mirrors the user one — create, list, fetch, update, delete, and add or remove packs — with two differences:
- Ownerless (app-owned or global) brains.
POST /admin/brainsaccepts an optionaluser_id. Set it to assign the brain to a user; leave itnullto create a brain with no owner — an app-scoped brain (withapp_id) or a fully global one (bothnull). - No ownership filter.
GET /admin/brainslists across everyone, with optionaluser_idandapp_idfilters pluslimit/offset. Admin pack-add does not require the pack to be owned by any particular user.
# Create a global brain (no owner) scoped to one app
POST https://your-tie-host/admin/brains
Authorization: Bearer $AUTH_SECRET
Content-Type: application/json
{ "name": "Shared Playbook", "app_id": "vibrantly", "user_id": null }Common pitfalls
- Expecting a brain to add itself. Like packs, brains are opt-in per request. A brain injects context only when a request passes its
brain_id(or lists it inbrain_ids). - Sending
brain_idandknowledge_pack_idstogether. The explicitknowledge_pack_idswins and the brain is ignored — not merged. Pass one or the other. (brain_idandbrain_ids, by contrast, are merged.) - Resolving a brain you cannot read. Resolution is scoped to the caller. A brain you neither own, hold an entitlement for, nor can see as public returns
404 BRAIN_NOT_FOUND— including when it is one ID among several. - Deleting a brain to remove documents. Deleting a brain removes only the collection. The packs — and their entitlements and skill tags — remain. Delete the packs themselves if that is the intent.
- Adding a pack you cannot see. A user add requires a pack you own or a public pack; anything else is rejected.