# Profiles

A profile is a customer identity record stored in a [memory store](/docs/conversations/orchestrator/concepts/core#memory-store). Profiles let Conversation Orchestrator recognize returning customers across conversations, link multiple addresses to one identity, and drive [profile-based grouping](/docs/conversations/orchestrator/concepts/core#conversation-grouping).

This page explains how Conversation Orchestrator identifies participant types, searches for and creates profiles, and resolves profiles across ingestion modes.

## Identifying participant types

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

### Active ingestion

When you [create conversations programmatically](/docs/conversations/orchestrator/guides/active-creation), 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.

### Passive ingestion

For [passive ingestion](/docs/conversations/orchestrator/concepts/ingestion#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. |
| WhatsApp | 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.

## Profile search

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   | `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.

## Profile creation

If no profile exists for a `CUSTOMER` whose type was set explicitly ([active ingestion](/docs/conversations/orchestrator/concepts/ingestion#active-ingestion)), Conversation Orchestrator creates a new profile populated with the participant's address as the initial trait:

```json
{
  "profileId": "mem_profile_01kpttt2f1emxtf3nqyeaksbfv",
  "traits": {
    "Phone": ["+15551234567"]
  }
}
```

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

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

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

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:

```json
{
  "memoryStoreId": "mem_store_01abc...",
  "memoryExtractionEnabled": true
}
```

| `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:

```text
POST /v1/Stores/{storeId}/Profiles/{profileId}/Observations
{
  "content": "Customer confirmed appointment for May 5 at 2pm"
}
```

## Best-effort resolution

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.

## Examples

### Passive SMS ingestion

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.

### Active creation

Your app creates a conversation with explicit types:

```json
{
  "participants": [
    { "type": "CUSTOMER", "addresses": [{ "channel": "SMS", "address": "+15559876543" }] },
    { "type": "HUMAN_AGENT", "addresses": [{ "channel": "SMS", "address": "+15551234567" }] }
  ]
}
```

Profile search for `+15559876543` finds an existing profile. The customer participant is linked to it; the `HUMAN_AGENT` participant has `profileId: null`.

### Unknown participants

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`.

## Next steps

* [Core concepts](/docs/conversations/orchestrator/concepts/core): Participants, profiles, and memory stores.
* [Ingestion modes](/docs/conversations/orchestrator/concepts/ingestion): How types are assigned in each mode.
* [Channels](/docs/conversations/orchestrator/concepts/channels): Per-channel address formats and trait mapping.
* [Conversation Memory](/docs/conversations/memory): Manage traits and profiles directly.
