Skip to main content
This guide describes two common strategies for using personas when you run many conversations. The right choice depends on a single question: Does each conversation need different behavior, or only different data?
  • Different data only (e.g. user name, profile, session goal) → Approach A: Reuse personas and pass per-conversation data via conversational_context and related options.
  • Different behavior (e.g. different voice, objectives, guardrails, or tools per session) → Approach B: Create a persona per conversation — define the persona config in your code, create it via the API at session start, then delete it when the session ends.
Both patterns are valid and used in production. Below we lay out how each works, what you can and can’t customize, tradeoffs, and when to use which.

Approach A: Reuse Personas + conversational_context

How it works

You keep persistent personas in Tavus — whether one or many — and reuse them. For each new conversation you call POST /v2/conversations and pass per-user or per-session data via request body fields. The persona itself is unchanged; only the conversation-level context changes.

What you can customize per conversation

  • Any text/data in the LLM context — e.g. user name, profile, history, prior session summary — via conversational_context when creating the conversation.
  • Custom greeting — via custom_greeting so each participant gets a personalized opening.
Mid-session context — You can inject or replace context during a call via WebSocket events: append_llm_context to add context without replacing what’s there, or overwrite_llm_context to replace the current conversational_context. Useful for injecting tool results or refreshed instructions without ending the call.

What you cannot customize per conversation

Everything that lives on the persona is shared by all conversations using that persona:
  • Objectives, guardrails, TTS voice, LLM model, tools — all are persona-level. A PATCH to the persona affects every current and future conversation that uses it.

Advantages

  • Low API overhead — one call to create a conversation (POST /v2/conversations) with persona_id and optional conversational_context, custom_greeting, etc.
  • Centralized updates — change the persona once (e.g. system prompt, guardrails, voice) and all new conversations immediately use the new config.
  • Simple at scale — fewer personas to manage; no create/delete lifecycle per session.

Disadvantages

  • No per-session behavioral isolation — a mistaken or premature PATCH to the persona affects every conversation using it.
  • No per-session variation of voice, objectives, guardrails, or tools — those stay fixed at the persona level.

Example use cases

  • Single shared persona, rich context per call — A team uses one persona and builds a detailed conversational_context server-side before each call (e.g. user lifecycle stage, profile, engagement history, prior session summary). During the call they can use append_llm_context (and optionally respond events) to inject tool outputs or updated guidance without interrupting the avatar.
  • Centralized prompt, per-user personalization — One persona holds the core system prompt and behavior. Each conversation gets a different conversational_context (e.g. participant name, background, preferences). A single update to the persona rolls out to all users at once while still allowing personalized greetings and questions per session.
  • Static knowledge, dynamic audience — A single persona is configured with fixed reference content (e.g. product FAQs, academic programs). Institution or user context is passed in via conversational_context per conversation.
  • Storyteller — A persona has a static system_prompt and base context; each conversation passes in the participant’s name, age, and genre preferences via conversational_context and optionally a custom_greeting.

Approach B: New Persona Per Conversation (Create & Delete)

How it works

You define the persona config in your own code (or in a config file, database, etc. — however you want to store it). That “template” is not stored in Tavus. At session start you:
  1. POST /v2/personas — create a new persona with the config from your code, including any session-specific overrides (e.g. voice, objectives, guardrails, system prompt).
  2. POST /v2/conversations — create the conversation using the new persona’s ID.
  3. When the session ends — DELETE /v2/personas/ to remove the ephemeral persona.
Any changes you make to the template are in your code; you deploy or update that code when you want new sessions to pick up new behavior. In-flight sessions keep the config they were created with until they end.

What you can customize per conversation

Everything that lives on a persona can differ per session:
  • Objectives, guardrails, TTS voice, LLM model, tools, system_prompt, and any other persona-level fields.
You can still use conversational_context and custom_greeting on the conversation for additional per-session data.

Advantages

  • Full isolation — no cross-session contamination. A change or mistake in one session’s persona does not affect others.
  • Maximum flexibility — every persona-level setting can vary per session (e.g. different voice per conversation, different objectives per role or demo).
  • Safe for multi-tenant or demo flows — each tenant or demo can have its own persona instance with custom guardrails, objectives, and context.
  • No risk of a PATCH affecting live sessions — you only patch or delete the ephemeral persona for that session.

Disadvantages

  • Three API calls per session — create persona → create conversation → delete persona (after session end). You need reliable cleanup logic (e.g. on session-end webhook or timeout) so ephemeral personas don’t accumulate.
  • Template updates don’t affect in-flight sessions — only new sessions pick up changes when you deploy updated code; existing conversations keep the config they were created with.

Example use cases

  • Different TTS voice per conversation — Because voice is configured at the persona level and can’t be overridden at conversation creation, one practical approach is to create a new persona per session (from your code template) with the desired voice. This pattern is often adopted as the standard production approach when voice-per-session is a requirement.
  • Demos with custom guardrails and objectives — Demos need custom guardrails, objectives, and persona context per demo — all persona-level. Define your base config in code and create a new persona per demo with demo-specific overrides. That way you avoid maintaining a large library of static personas in Tavus or running batch update jobs when behavior changes; the template in your code is the single source of truth.
  • Structured flows where persona-level changes broke global state — Teams running structured conversations (e.g. role-specific recruiting or role-play) found that changing a shared persona’s voice or objectives affected all active conversations. Creating a persona per session from their code template gave per-conversation isolation and avoided those side effects.
  • Event- or deployment-specific config — For kiosks or event-specific advisors, the persona config can vary per event or deployment (system prompt, LLM backend, TTS provider). Store the config in your code or deployment pipeline and create a fresh persona per context when the session starts.

Choosing an approach

If you need…Prefer
Different data per conversation (user name, profile, history, session goal)Approach A — reuse personas and use conversational_context, custom_greeting, and (optionally) mid-session context events.
Different behavior per conversation (voice, objectives, guardrails, tools, or system prompt)Approach B — define the persona config in your code and create a new persona per session via the API, then delete it when the session ends.
You can combine both: for example, create a persona per session (Approach B) and still pass conversational_context and custom_greeting when creating the conversation for extra per-session data.