Profiles
A profile is a customer identity record stored in a memory store. Profiles let Conversation Orchestrator recognize returning customers across conversations, link multiple addresses to one identity, and drive profile-based grouping.
This page explains how Conversation Orchestrator identifies participant types, searches for and creates profiles, and resolves profiles across ingestion modes.
Only participants with type CUSTOMER are eligible for profile resolution. How the type is determined depends on the ingestion mode.
When you create conversations programmatically, you set type directly on each participant. This is the most reliable way to drive profile resolution. Use this approach when roles are known upfront.
For passive ingestion, Conversation Orchestrator infers types by comparing addresses against what your account owns.
| Channel | Heuristic |
|---|---|
| SMS | Twilio-owned phone number is the agent; the other is the customer. |
| Voice | Twilio-owned phone number is the agent; the other is the customer. |
| Registered WhatsApp Business Account is the agent. |
If neither address can be identified (for example, neither is Twilio-owned), Conversation Orchestrator searches the memory store for both. If exactly one address returns a profile, that participant becomes CUSTOMER. If neither returns a profile, both stay UNKNOWN and no profile is created.
For each CUSTOMER participant, Conversation Orchestrator looks up the memory store by address. Each channel maps to a specific trait.
| Channel | Trait searched |
|---|---|
| SMS, Voice | Phone |
WhatsApp | |
| Chat | ChatID |
Voice CLIENT calls use client identity strings (for example, agent-1) rather than phone numbers, so they don't resolve to Phone trait profiles.
If a profile exists with that trait, Conversation Orchestrator sets the participant's profileId to the existing profile.
If no profile exists for a CUSTOMER whose type was set explicitly (active ingestion), Conversation Orchestrator creates a new profile populated with the participant's address as the initial trait:
1{2"profileId": "mem_profile_01kpttt2f1emxtf3nqyeaksbfv",3"traits": {4"Phone": ["+15551234567"]5}6}
Profiles aren't created for:
- Agent-typed participants (
HUMAN_AGENT,AI_AGENT,AGENT) orUNKNOWNparticipants. CUSTOMERparticipants whose type was inferred by heuristic in passive ingestion. Resolution still looks up existing profiles; it doesn't create new ones.- Configurations without a
memoryStoreId.
Once a profile exists, the same address in a later conversation resolves to the same profileId:
- Day 1, SMS from
+15551234567: new conversation, new profilemem_profile_01abc.... - Day 2, voice call from
+15551234567: new conversation, samemem_profile_01abc...linked to the customer participant.
With GROUP_BY_PROFILE, step 2 lands in the same conversation as step 1.
A customer who calls from +15551234567 and texts from the same number resolves to the same profile (same phone trait). A customer who calls from one number and texts from a different number only unifies if both numbers are traits on the same profile.
For GROUP_BY_PROFILE grouping, resolution must succeed for grouping to work. If a customer's address isn't in the memory store as a trait, the system falls back to address-based routing (behaves like GROUP_BY_PARTICIPANT_ADDRESSES for that interaction).
Memory extraction automatically writes observations and summaries to a customer's profile based on the content of their conversations. Recall returns the extracted observations in subsequent interactions.
Extraction is opt-in. Set memoryExtractionEnabled: true on the configuration:
1{2"memoryStoreId": "mem_store_01abc...",3"memoryExtractionEnabled": true4}
memoryExtractionEnabled | memoryStoreId set | Behavior |
|---|---|---|
true | Yes | Extraction fires on configured lifecycle transitions. Observations and summaries are written. |
false (default) | Yes | Profile resolution works. Recall works. But no automatic observations are extracted. |
| Any | No | Profile resolution and extraction are disabled. |
Extraction fires on INACTIVE and/or CLOSED lifecycle transitions:
| Trigger | Tradeoff |
|---|---|
INACTIVE | Faster context availability, but the transcript might be incomplete if the customer resumes. |
CLOSED | Complete transcript, higher-quality observations, but longer delay before context is available. |
For most implementations, enable extraction on CLOSED. Only add INACTIVE extraction if your use case requires sub-minute availability of context between interactions.
There is no mid-conversation extraction. If you need to save facts during an active conversation (for example, a confirmed booking), send observations directly to the Memory API:
1POST /v1/Stores/{storeId}/Profiles/{profileId}/Observations2{3"content": "Customer confirmed appointment for May 5 at 2pm"4}
Profile resolution runs asynchronously in the background and is best-effort:
- Participants are created successfully even if the memory store is unavailable.
- If resolution fails, the participant's
profileIdisnulland the conversation continues to work.
This keeps conversations from being blocked by memory store outages. The tradeoff is that profileId may appear shortly after the participant is first returned from the API.
A customer sends an SMS from +15559876543 to your Twilio number +15551234567:
- A capture rule matches and creates a conversation.
+15551234567(Twilio-owned) becomes the agent;+15559876543becomes the customer.- Profile search for
Phone: +15559876543finds nothing. Because the type was inferred (not explicit), no profile is created. - The customer participant has
profileId: nulluntil you set an explicit type with active ingestion or an existing profile exists.
Your app creates a conversation with explicit types:
1{2"participants": [3{ "type": "CUSTOMER", "addresses": [{ "channel": "SMS", "address": "+15559876543" }] },4{ "type": "HUMAN_AGENT", "addresses": [{ "channel": "SMS", "address": "+15551234567" }] }5]6}
Profile search for +15559876543 finds an existing profile. The customer participant is linked to it; the HUMAN_AGENT participant has profileId: null.
A message arrives from +15551111111 to +15552222222, and neither address is Twilio-owned or has an existing profile. Both participants end up as UNKNOWN with profileId: null.
- Core concepts: Participants, profiles, and memory stores.
- Ingestion modes: How types are assigned in each mode.
- Channels: Per-channel address formats and trait mapping.
- Conversation Memory: Manage traits and profiles directly.