Customer Profiles & Memory
Each customer has a 4-layer memory that persists across conversations. This section covers retrieving and managing customer profiles.
List Customers
Retrieve all customers for your tenant.
GET /api/tenants/me/customers?limit=20
Response
{
"data": [
{
"id": "cust_ulid",
"name": "Nguyen Van A",
"phone": "+84912345678",
"email": "customer@example.com",
"channels": ["pancake", "facebook"],
"trustScore": 0.8,
"churnRisk": 0.2,
"totalConversations": 5,
"firstContactAt": "2026-02-01T00:00:00Z",
"lastMessageAt": "2026-03-12T10:05:00Z"
}
],
"pagination": {
"limit": 20,
"hasMore": true
}
}
Get Customer Profile
Retrieve a single customer's full profile including all 4 memory layers.
GET /api/tenants/me/customers/:customerId
Response
{
"id": "cust_ulid",
"name": "Nguyen Van A",
"phone": "+84912345678",
"email": "customer@example.com",
"channels": ["pancake", "facebook"],
"language": "vi",
"timezone": "Asia/Ho_Chi_Minh",
"memory": {
"l1_identity": {
"name": "Nguyen Van A",
"channels": ["pancake", "facebook"],
"language": "vi",
"timezone": "Asia/Ho_Chi_Minh"
},
"l2_history": {
"firstContact": "2026-02-01T00:00:00Z",
"totalConversations": 5,
"trustScore": 0.8,
"churnRisk": 0.2
},
"l3_episodes": [
{
"id": "ep_ulid",
"summary": "Customer purchased polo shirt, found defect in seam, requested exchange",
"conversationId": "conv_ulid",
"createdAt": "2026-03-01T00:00:00Z"
},
{
"id": "ep_ulid",
"summary": "Checked order status for YD-20260312-001, ETA 2 days",
"conversationId": "conv_ulid",
"createdAt": "2026-03-12T00:00:00Z"
}
],
"l4_prefs_mood": {
"tonePref": "friendly",
"openIssues": ["defective polo shirt"],
"personalDetails": "Prefers quick responses, impatient",
"lastMood": "frustrated",
"lastMoodAt": "2026-03-12T10:00:00Z"
}
},
"createdAt": "2026-02-01T00:00:00Z",
"updatedAt": "2026-03-12T10:05:00Z"
}
4-Layer Memory Explained
L1: Identity (Hot KeyDB Cache)
Basic customer info:
{
"name": "Nguyen Van A",
"phone": "+84912345678",
"email": "customer@example.com",
"channels": ["pancake", "facebook"],
"language": "vi",
"timezone": "Asia/Ho_Chi_Minh"
}
Loaded instantly on every message. TTL: 1 hour after last update.
L2: History (Hot KeyDB Cache)
Engagement metrics:
{
"firstContact": "2026-02-01T00:00:00Z",
"totalConversations": 5,
"trustScore": 0.8,
"churnRisk": 0.2
}
- trustScore — 0-1, higher = more trusted customer
- churnRisk — 0-1, higher = likely to stop buying
Updated after each conversation. TTL: 1 hour.
L3: Episodes (pgvector Storage)
Vector-indexed conversation summaries:
[
{
"summary": "Purchased polo shirt, found defect, requested exchange",
"conversationId": "conv_ulid",
"embedding": [0.2, -0.1, 0.5, ...],
"createdAt": "2026-03-01T00:00:00Z"
}
]
Used for semantic search during message processing. Extracted asynchronously after each conversation.
L4: Preferences & Mood (Hot KeyDB Cache)
Current state and preferences:
{
"tonePref": "friendly",
"openIssues": ["defective polo shirt", "refund pending"],
"personalDetails": "VIP customer, prefers expedited shipping",
"lastMood": "frustrated",
"lastMoodAt": "2026-03-12T10:00:00Z"
}
Updated after each AI response. Used to personalize future replies.
Search Customers
Search for customers by name or phone.
GET /api/tenants/me/customers/search?q=Nguyen&limit=10
Response
{
"results": [
{
"id": "cust_ulid",
"name": "Nguyen Van A",
"phone": "+84912345678",
"lastMessageAt": "2026-03-12T10:05:00Z"
}
]
}
Memory Persistence
Memory persists across conversations:
- Customer A sends message today
- AI processes, loads memory, generates reply
- Episode extracted, memory updated
- Customer A sends message 1 week later
- AI loads same memory → context intact → personalized reply
Example:
Day 1: Customer says "My shirt has a tear"
AI response: "I understand. Let me help with an exchange..."
Memory updated: openIssues = ["defective polo shirt"]
Day 8: Customer says "Any update on my exchange?"
AI response: "Yes! Your new polo shirt is on the way..."
(AI remembers the original issue)
Memory Update Flow
After each conversation, async jobs:
- Parse conversation for new customer info
- Update L1 — language, timezone, channel hints
- Update L2 — increment conversation count, recalculate trust score
- Extract episode — summarize conversation, embed, insert into pgvector
- Update L4 — mood, open issues, personal details
No locking or blocking — updates are eventual consistent.
Trust Score Calculation
trust_score = min(1.0,
base_score
+ (# positive interactions * 0.1)
- (# escalations * 0.15)
- (# complaints * 0.1)
)
Higher trust score → AI is more permissive (trusts customer claims more).
Churn Risk Calculation
churn_risk = min(1.0,
base_risk
+ (days_since_last_interaction / 90 * 0.2)
+ (# unresolved_issues * 0.25)
- (# positive_resolutions * 0.1)
)
Higher churn risk → AI prioritizes engagement and resolution.
Export Customer Data
Export customer profile and memory.
GET /api/tenants/me/customers/:customerId/export?format=json
Error Responses
| Error | HTTP | Cause |
|---|---|---|
NOT_FOUND | 404 | Customer does not exist |
INTERNAL_ERROR | 500 | Server error |