Conversations (threads)
Use conversations to manage parallel conversation threads with a single agent in the Letta API
A conversation is a message thread within an agent. A single agent can have multiple conversations running in parallel, each with its own context window, but all sharing the same memory blocks and searchable message history.
This is useful when you want to run several sessions with the same agent simultaneously without them interfering with each other. For example, you might have one conversation where you’re refactoring an API while another is writing tests—both sessions share the agent’s learned context about your codebase.
Creating a conversation
Section titled “Creating a conversation”Create a conversation by specifying the agent ID:
import Letta from "@letta-ai/letta-client";
const client = new Letta({ apiKey: process.env.LETTA_API_KEY });
const conversation = await client.conversations.create({ agent_id: "agent-xxx",});
console.log(`Created conversation: ${conversation.id}`);from letta_client import Lettaimport os
client = Letta(api_key=os.getenv("LETTA_API_KEY"))
conversation = client.conversations.create(agent_id="agent-xxx")
print(f"Created conversation: {conversation.id}")Sending messages
Section titled “Sending messages”Send messages to a conversation. The response is always a stream:
const stream = await client.conversations.messages.create(conversation.id, { messages: [{ role: "user", content: "Explain this codebase" }], stream_tokens: true,});
for await (const chunk of stream) { if (chunk.message_type === "assistant_message") { process.stdout.write(chunk.content); }}stream = client.conversations.messages.create( conversation.id, messages=[{"role": "user", "content": "Explain this codebase"}], stream_tokens=True,)
for chunk in stream: if chunk.message_type == "assistant_message": print(chunk.content, end="")Model overrides
Section titled “Model overrides”You can set a different model for a conversation, or override the model on a single request. This is useful for routing cheaper conversations to a smaller model without creating a separate agent.
Model selection priority:
- Per-request
override_modelparameter (highest) - Conversation-level
modelsetting - Agent’s default model
// Create a conversation with a specific modelconst conversation = await client.conversations.create({ agent_id: "agent-xxx", model: "openai/gpt-5-mini",});
// Or override the model for a single requestconst stream = await client.conversations.messages.create(conversation.id, { messages: [{ role: "user", content: "Hello" }], override_model: "anthropic/claude-haiku-4-5",});# Create a conversation with a specific modelconversation = client.conversations.create( agent_id="agent-xxx", model="openai/gpt-5-mini",)
# Or override the model for a single requeststream = client.conversations.messages.create( conversation.id, messages=[{"role": "user", "content": "Hello"}], override_model="anthropic/claude-haiku-4-5",)Listing conversations
Section titled “Listing conversations”List all conversations for an agent. Use order_by and order to control sort order:
// Most recently active conversationsconst conversations = await client.conversations.list({ agent_id: "agent-xxx", order_by: "last_run_completion", order: "desc",});
for (const conv of conversations) { console.log(conv.id);}# Most recently active conversationsconversations = client.conversations.list( agent_id="agent-xxx", order_by="last_run_completion", order="desc",)
for conv in conversations: print(conv.id)order_by accepts "created_at" or "last_run_completion". order accepts "asc" or "desc".
Listing messages in a conversation
Section titled “Listing messages in a conversation”Retrieve the message history for a specific conversation:
const messages = await client.conversations.messages.list(conversation.id);
for (const msg of messages) { console.log(`[${msg.message_type}] ${msg.content || msg.reasoning || ""}`);}messages = client.conversations.messages.list(conversation.id)
for msg in messages: print(f"[{msg.message_type}] {msg.content or msg.reasoning or ''}")Deleting a conversation
Section titled “Deleting a conversation”Delete a conversation when it’s no longer needed:
client.conversations.delete(conversation.id)// Not yet available in the TypeScript SDK.// Use the REST API directly:await fetch(`${baseUrl}/v1/conversations/${conversation.id}`, { method: "DELETE", headers: { Authorization: `Bearer ${apiKey}` },});Deleted conversations are removed from list results and can no longer be retrieved.
How conversations share state
Section titled “How conversations share state”All conversations within an agent share:
-
Memory blocks: The agent’s core memory (persona, human, project blocks, etc.) is shared across all conversations. When the agent updates a memory block in one conversation, that change is visible in all other conversations.
-
Searchable message history: Messages from all conversations are pooled together in a searchable database. The agent can use
conversation_searchto recall context from any past conversation, not just the current one.
Each conversation has its own:
-
Context window: The active messages being processed. Long conversations get compacted independently.
-
Message history: The sequence of messages in that specific thread.
This design lets you run parallel sessions that build on shared knowledge while keeping their immediate context separate.
When to use conversations
Section titled “When to use conversations”Concurrency: The agents.messages.create endpoint is not thread-safe—concurrent requests to the same agent can cause race conditions. If you need to send messages to an agent from multiple threads or processes simultaneously, use separate conversations. Each conversation has its own message stream that can be written to independently.
Separating context: When your application has clearly distinct interaction sessions (e.g., different user sessions, different tasks), conversations let you keep their context windows separate while still sharing the agent’s learned memory. This prevents unrelated messages from one session polluting another’s context.