User Profiles
Build rich user profiles through dialectic conversations. Hebbrix learns user preferences, interests, and personality traits to provide personalized AI experiences.
How Profiles Work
Automatic Learning
Profiles are built automatically from conversations. Every interaction adds to the user's profile.
Dialectic Chat
Use the dialectic endpoint to have focused conversations that extract specific profile information.
Profile Fields
Profiles can store any structured data. Common fields include:
namepreferencesinterestsexpertisecommunication_stylegoalstimezonelanguageindustryrolepersonality_traitscustom_fieldsEndpoints
Per-Fact CRUD
Beyond the bulk PATCH, every profile fact can be read, created, edited, or removed individually. Each of these also honours the multi-tenant collection_id scoping described below.
Code Examples
Get User Profile
import requests
# GET /v1/profile
# Flat convenience fields (name/timezone/language) live at the top
# level; all other facts sit under the "profile.static" and
# "profile.dynamic" buckets.
r = requests.get(
"https://api.hebbrix.com/v1/profile",
headers={"Authorization": "Bearer <your-api-key>"},
)
data = r.json()
print(f"Name: {data.get('name')}")
print(f"Timezone: {data.get('timezone')}")
print(f"Language: {data.get('language')}")
# Every other fact lives under profile.static (long-lived) or
# profile.dynamic (short-lived). Each entry is {key, value, confidence, ...}.
for fact in data["profile"]["static"]:
print(f" static · {fact['key']} = {fact['value']}")
for fact in data["profile"]["dynamic"]:
print(f" dynamic · {fact['key']} = {fact['value']}")Dialectic Conversation
import requests
# POST /v1/profile/dialectic — ask natural-language questions about the user.
# Send the prompt as `user_message` (preferred); legacy `message` / `question`
# are still accepted.
r = requests.post(
"https://api.hebbrix.com/v1/profile/dialectic",
headers={"Authorization": "Bearer <your-api-key>"},
json={"user_message": "What are this user's main professional interests?"},
)
body = r.json()
# Preferred response fields
print(body["assistant_message"]) # the LLM answer
print(body["session_id"]) # echo back in the next turn for continuity
# Legacy alias (same value as assistant_message)
# body["response"] is also available for older integrations.
# Continue the conversation — reuse session_id to link turns
r2 = requests.post(
"https://api.hebbrix.com/v1/profile/dialectic",
headers={"Authorization": "Bearer <your-api-key>"},
json={
"user_message": "Tell me more about how they like to communicate.",
"session_id": body["session_id"],
},
)Update Profile Manually
import requests
# PATCH /v1/profile — bulk-update profile facts. Body is a flat
# {fact_key: fact_value} dict. Existing facts are updated; new ones
# are created as "static" / "other" automatically.
r = requests.patch(
"https://api.hebbrix.com/v1/profile",
headers={"Authorization": "Bearer <your-api-key>"},
json={
"name": "John Doe",
"timezone": "UTC",
"language": "en",
"role": "Senior Engineer",
"company": "Acme Inc",
},
)
body = r.json()
print(body["status"]) # "success"
print(body["updated"]) # count of facts updated in place
print(body["created"]) # count of new facts created
print(body["message"]) # e.g. "Updated 3, created 2 profile facts"cURL Example
/v1/profilecurl -X GET "https://api.hebbrix.com/v1/profile" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json"Profile Injection in Chat
When using the chat completions endpoint, profile data is automatically injected:
import os
import requests
BASE = "https://api.hebbrix.com/v1"
H = {"Authorization": f"Bearer {os.environ['HEBBRIX_API_KEY']}"}
# POST /v1/chat/completions — set features.user_profile=True to have
# the backend inject profile facts into the context automatically.
r = requests.post(
f"{BASE}/chat/completions",
headers=H,
json={
"model": "gpt-5-nano",
"messages": [
{"role": "user", "content": "Recommend a book for me"},
],
"features": {
"memory": True,
"user_profile": True, # inject profile context
},
},
)
# The assistant sees the caller's profile and gives a personalized answer
print(r.json()["choices"][0]["message"]["content"])Multi-Tenant Scoping (collection_id)
If you run a multi-tenant SaaS where many end-users share one Hebbrix account, you can keep each end-user's profile isolated by tagging their facts to a per-end-user collection_id. A scoped read returns only the facts belonging to that collection, so one end-user's profile never leaks into another's.
Isolation model
- A fact written without a
collection_idis account-wide (legacy / global). - A scoped read (with
collection_id) returns only facts tagged to that collection or derived from its memories. - Omitting
collection_ideverywhere keeps the legacy account-wide behavior — fully backward compatible.
Where it applies
GET /v1/profile, GET /v1/profile/facts (query param), PATCH /v1/profile and POST /v1/profile/facts (body key).
Preview before you write
Send dry_run: true on PATCH /v1/profile to get a preview of the planned changes (would_update / would_create) without persisting anything.
Scoped Write with Dry-Run Preview
import requests
BASE = "https://api.hebbrix.com/v1"
H = {"Authorization": "Bearer <your-api-key>"}
# Each end-user in your SaaS gets their own collection id.
COLLECTION = "coll_123"
# 1) PREVIEW — dry_run returns the planned changes WITHOUT persisting.
preview = requests.patch(
f"{BASE}/profile",
headers=H,
json={
"role": "Product Manager",
"timezone": "America/New_York",
"collection_id": COLLECTION, # reserved key — scopes the facts
"dry_run": True, # reserved key — preview only
},
).json()
print(preview["status"]) # "preview"
print(preview["would_update"]) # N facts that would be updated in place
print(preview["would_create"]) # N facts that would be created
print(preview["collection_id"]) # "coll_123"
print(preview["changes"]) # [...] per-fact diff, nothing written yet
# 2) REAL WRITE — drop dry_run (or set it False) to persist the facts,
# tagged to this end-user's collection so they stay isolated.
written = requests.patch(
f"{BASE}/profile",
headers=H,
json={
"role": "Product Manager",
"timezone": "America/New_York",
"collection_id": COLLECTION,
},
).json()
print(written["status"]) # "success"
print(written["updated"], written["created"])
# 3) SCOPED READ — only facts for this collection come back; another
# end-user's profile never leaks in.
profile = requests.get(
f"{BASE}/profile",
headers=H,
params={"collection_id": COLLECTION},
).json()
for fact in profile["profile"]["static"]:
print(f" {fact['key']} = {fact['value']}")