Skip to contentSkip to navigationSkip to topbar
Page toolsOn this page
Looking for more inspiration?Visit the

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.


Identifying participant types

identifying-participant-types page anchor

Only participants with type CUSTOMER are eligible for profile resolution. How the type is determined depends on the ingestion mode.

Active ingestion

active-ingestion page anchor

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.

ChannelHeuristic
SMSTwilio-owned phone number is the agent; the other is the customer.
VoiceTwilio-owned phone number is the agent; the other is the customer.
WhatsAppRegistered 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.

ChannelTrait searched
SMS, VoicePhone
WhatsAppWhatsApp
ChatChatID

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) or UNKNOWN participants.
  • CUSTOMER participants 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.

Profile linking across conversations

profile-linking-across-conversations page anchor

Once a profile exists, the same address in a later conversation resolves to the same profileId:

  1. Day 1, SMS from +15551234567: new conversation, new profile mem_profile_01abc....
  2. Day 2, voice call from +15551234567: new conversation, same mem_profile_01abc... linked to the customer participant.

With GROUP_BY_PROFILE, step 2 lands in the same conversation as step 1.

Cross-channel unification

cross-channel-unification page anchor

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": true
4
}
memoryExtractionEnabledmemoryStoreId setBehavior
trueYesExtraction fires on configured lifecycle transitions. Observations and summaries are written.
false (default)YesProfile resolution works. Recall works. But no automatic observations are extracted.
AnyNoProfile resolution and extraction are disabled.

Extraction fires on INACTIVE and/or CLOSED lifecycle transitions:

TriggerTradeoff
INACTIVEFaster context availability, but the transcript might be incomplete if the customer resumes.
CLOSEDComplete 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:

1
POST /v1/Stores/{storeId}/Profiles/{profileId}/Observations
2
{
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 profileId is null and 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:

  1. A capture rule matches and creates a conversation.
  2. +15551234567 (Twilio-owned) becomes the agent; +15559876543 becomes the customer.
  3. Profile search for Phone: +15559876543 finds nothing. Because the type was inferred (not explicit), no profile is created.
  4. The customer participant has profileId: null until 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.