Skip to main content

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:

  1. Customer A sends message today
  2. AI processes, loads memory, generates reply
  3. Episode extracted, memory updated
  4. Customer A sends message 1 week later
  5. 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:

  1. Parse conversation for new customer info
  2. Update L1 — language, timezone, channel hints
  3. Update L2 — increment conversation count, recalculate trust score
  4. Extract episode — summarize conversation, embed, insert into pgvector
  5. 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

ErrorHTTPCause
NOT_FOUND404Customer does not exist
INTERNAL_ERROR500Server error