Skip to content

Core Concepts

Understanding DialogueDB's core concepts will help you build better conversational applications. This guide covers the fundamental building blocks: dialogues, messages, threads, and state.

Dialogues

A dialogue is a conversation session containing a series of messages. Think of it as a container for a complete conversation between users and AI assistants.

Dialogue Properties

typescript
{
  id: string;              // Unique identifier (ULID format)
  threadOf?: string;       // Parent dialogue if this is a thread
  requestId: string;       // Request tracking ID
  status: "active" | "canceled" | "expired" | "deleted";
  tags: string[];          // Custom tags for organization
  totalMessages: number;   // Count of messages in this dialogue
  threadCount: number;     // Number of child threads
  lastMessageCreated: string;  // Timestamp of most recent message
  metadata: object;        // Custom metadata
  metadataLength?: number; // Size of metadata
  metadataSHA256?: string; // Metadata hash
  created: string;         // Creation timestamp (ISO 8601)
  modified: string;        // Last modification timestamp (ISO 8601)
  state: object;           // Custom state data
  messages?: Message[];    // Array of message objects (when included)
}

Dialogue Lifecycle

  1. Created: New dialogue initialized
  2. Active: Accepting new messages
  3. Ended: Conversation marked as complete
  4. Deleted: Permanently removed (messages remain)

Creating Dialogues

typescript
// Simple dialogue
const dialogue = await client.dialogues.create({
  message: {
    role: 'user',
    content: 'Hello!'
  }
});

// Dialogue with metadata
const dialogue = await client.dialogues.create({
  metadata: {
    userId: 'user_123',
    channel: 'web',
    sessionId: 'session_abc'
  },
  tags: ['customer-support', 'billing-question'],
  message: {
    role: 'user',
    content: 'I have a billing question'
  }
});

Messages

Messages are the individual exchanges within a dialogue. Each message represents a single turn in the conversation.

Message Properties

typescript
{
  id: string;              // Unique identifier (ULID format)
  dialogueId: string;      // Parent dialogue ID
  role: "user" | "assistant" | "system";  // Message role
  content: string;         // Message content
  preview: string;         // Content preview (first 100 chars)
  name?: string;           // Optional name for the speaker
  metadata: object;        // Custom metadata
  tags: string[];          // Custom tags
  created: string;         // Creation timestamp (ISO 8601)
}

Message Roles

DialogueDB supports three message roles:

User - Messages from end users

typescript
{
  role: 'user',
  content: 'What is the weather today?'
}

Assistant - Responses from AI assistants

typescript
{
  role: 'assistant',
  content: 'The current weather is sunny with a high of 75°F.'
}

System - System prompts and instructions

typescript
{
  role: 'system',
  content: 'You are a helpful weather assistant. Provide accurate and friendly weather information.'
}

Message Storage

Messages are automatically stored and optimized for fast retrieval. The system handles storage transparently - you don't need to do anything special.

Creating Messages

typescript
// Add a message to an existing dialogue
const message = await client.messages.create(dialogueId, {
  role: 'user',
  content: 'Can you explain quantum computing?'
});

// Message with metadata
const message = await client.messages.create(dialogueId, {
  role: 'assistant',
  content: 'Quantum computing uses quantum mechanics...',
  metadata: {
    model: 'gpt-4',
    tokensUsed: 250,
    temperature: 0.7
  }
});

Retrieving Messages

typescript
// Get messages with pagination
const { messages, next } = await client.messages.list(dialogueId, {
  limit: 50,
  next: paginationToken // From previous request
});

// Get specific message
const message = await client.messages.get(dialogueId, messageId);

Threads

Threads are dialogues that are organizationally linked to a parent dialogue. Think of them as sub-conversations or related processes grouped under a main dialogue, rather than traditional conversational branches.

Understanding Threads

A thread is a full dialogue with its own messages, state, and lifecycle - but it references a parent dialogue via the threadOf field. This creates an organizational hierarchy without affecting the content or independence of either dialogue.

Key characteristics:

  • Threads are complete, independent dialogues
  • Used primarily for grouping and organization
  • Each thread has its own message history
  • Parent dialogue tracks threadCount automatically
  • Threads can be listed by their parent

Thread Use Cases

  • Agent activities: Sub-tasks or tool executions related to a main conversation
  • Parallel reasoning: Multiple AI thought processes for a single query
  • Tool call logs: Detailed execution traces grouped under the main dialogue
  • Internal processes: System operations associated with a user conversation
  • Work breakdown: Decomposing a complex request into sub-dialogues
  • Related conversations: Grouping follow-up discussions under an original inquiry

Creating Threads

typescript
// Create a thread linked to a parent dialogue
const thread = await client.dialogues.create({
  threadOf: 'dlg_abc123',  // ID of parent dialogue
  message: {
    role: 'system',
    content: 'Executing tool call: fetch_weather_data'
  }
});

// The thread is a full dialogue with its own messages
await client.messages.create(thread.id, {
  role: 'assistant',
  content: 'Weather data retrieved successfully'
});

// Parent dialogue automatically tracks threadCount
const parent = await client.dialogues.get('dlg_abc123');
console.log(`Parent has ${parent.threadCount} threads`);

Listing Threads

typescript
// Get all threads of a dialogue
const { items } = await client.dialogues.list({
  query: 'threads',
  threadOf: 'dlg_abc123'  // ID of parent dialogue
});

console.log(`Found ${items.length} threads`);

// Each thread is a full dialogue
items.forEach(thread => {
  console.log(`Thread ${thread.id}: ${thread.totalMessages} messages`);
});

Thread Hierarchy Example

Main Dialogue (ID: dlg_abc123)
├── User: "Analyze this code and suggest improvements"
├── Assistant: "I'll analyze the code using multiple approaches..."

├── Thread: Code Quality Analysis (threadOf: dlg_abc123)
│   ├── System: "Running static analysis..."
│   └── Assistant: "Found 5 issues..."

├── Thread: Performance Analysis (threadOf: dlg_abc123)
│   ├── System: "Profiling execution..."
│   └── Assistant: "Identified 3 bottlenecks..."

└── Thread: Security Scan (threadOf: dlg_abc123)
    ├── System: "Scanning for vulnerabilities..."
    └── Assistant: "No critical issues found..."

In this example, the main dialogue contains the user conversation, while threads capture parallel analysis processes.

Threads vs. Namespace

Don't confuse threads with namespace:

FeatureThreadsNamespace
PurposeOrganizational hierarchyUser/tenant isolation
RelationshipParent-child linkFlat grouping
Use CaseSub-processes of a dialogueMulti-tenant data separation
VisibilityListed via parent IDQueried by namespace value

Use threads when: You need to group related dialogues under a parent conversation

Use namespace when: You need to isolate dialogues by user, tenant, or organization

State

State allows you to attach custom data to dialogues. Use state to persist conversation context, user preferences, or application-specific information.

State Use Cases

  • Conversation context: Store current topic, intent, or stage
  • User preferences: Remember user settings across messages
  • Application data: Store form data, shopping cart, etc.
  • Session information: Track authentication, permissions

Managing State

typescript
// Create dialogue with initial state
const dialogue = await client.dialogues.create({
  state: {
    currentTopic: 'weather',
    location: 'San Francisco',
    userPreference: 'celsius'
  },
  message: {
    role: 'user',
    content: 'What\'s the temperature?'
  }
});

// Update state
await client.dialogues.update(dialogueId, {
  state: {
    currentTopic: 'weather',
    location: 'New York', // Updated
    userPreference: 'fahrenheit' // Updated
  }
});

// Retrieve dialogue with state
const dialogue = await client.dialogues.get(dialogueId);
console.log(dialogue.state); // Access state

State Best Practices

Do:

  • Keep state small and focused
  • Use state for session-specific data
  • Update state as conversation progresses

Don't:

  • Store large objects in state
  • Use state for message history (use messages instead)
  • Store sensitive data without encryption

Dialogue Actions

DialogueDB provides actions to manage dialogue lifecycle:

End Conversation

Mark a dialogue as complete:

typescript
await client.dialogues.action(dialogueId, 'end');

Use when:

  • User closes the conversation
  • AI completes a task
  • Session expires

Compact Conversation

Summarize a long conversation to reduce token usage:

typescript
const summary = await client.dialogues.action(dialogueId, 'compact');

This generates a summary while preserving the full message history. Useful for:

  • Long-running conversations
  • Reducing LLM context size
  • Maintaining conversation continuity

Continue Conversation

Create a new dialogue continuing from an ended one:

typescript
// Note: Currently returns empty object, future implementation
await client.dialogues.action(dialogueId, 'continue');

Pagination

When retrieving large datasets, use pagination:

typescript
// Initial request
const { items, next } = await client.dialogues.list({
  limit: 20
});

// Next page
if (next) {
  const { items: moreItems, next: nextToken } = await client.dialogues.list({
    limit: 20,
    next: next
  });
}

// Same pattern for messages
const { messages, next: msgNext } = await client.messages.list(dialogueId, {
  limit: 50,
  next: paginationToken
});

Namespace (Advanced)

Namespace is an optional property for custom grouping and isolation of dialogues, messages, and memories. It provides a flexible way to organize your data beyond the built-in project structure.

Understanding Namespace

Namespace is a user-defined string that you can use to group and filter resources:

typescript
// Create dialogue with namespace
const dialogue = await client.dialogues.create({
  namespace: 'user_789',
  message: {
    role: 'user',
    content: 'Hello'
  }
});

// List dialogues by namespace
const { items } = await client.dialogues.list({
  namespace: 'user_789'
});

Namespace Use Cases

  • User isolation: Scope conversations by user ID for multi-user apps
  • Tenant separation: Isolate data by organization in B2B applications
  • Environment segmentation: Separate dev/staging/prod data
  • Feature flagging: Group dialogues by feature or experiment
  • Custom categorization: Any domain-specific grouping needs

Namespace Examples

Multi-tenant SaaS:

typescript
// Each organization has isolated data
await createDialogue({
  namespace: `org_${organizationId}`,
  message: { role: 'user', content: 'Help request' }
});

// Query only this organization's dialogues
const orgDialogues = await listDialogues({
  namespace: `org_${organizationId}`
});

User-specific conversations:

typescript
// Store user ID in namespace
await createDialogue({
  namespace: `user_${userId}`,
  message: { role: 'user', content: 'Question' }
});

// List all conversations for this user
const userDialogues = await listDialogues({
  namespace: `user_${userId}`
});

Memory isolation by user:

typescript
// Store user-specific memory
await createMemory({
  namespace: `user_${userId}`,
  value: 'Prefers dark mode',
  label: 'UI Preference'
});

// Search only this user's memories
const memories = await search({
  query: 'preferences',
  object: 'memory',
  namespace: `user_${userId}`
});

Namespace Best Practices

Do:

  • Use consistent naming conventions (e.g., user_${id}, org_${id})
  • Keep namespace values meaningful and predictable
  • Use namespace for cross-dialogue grouping
  • Combine with metadata for additional context

Don't:

  • Put sensitive data in namespace (it's not encrypted)
  • Use extremely long namespace strings (keep under 128 characters)
  • Treat namespace as authentication (use proper API key scoping)

Namespace vs. Other Features

FeaturePurposeScope
NamespaceCustom groupingUser-defined, filterable
ProjectTop-level isolationAPI key scoped
ThreadParent-child linkingHierarchical relationship
MetadataArbitrary key-valuesResource-specific
TagsCategorizationSimple labels

Identifiers

All resources in DialogueDB have unique identifiers:

  • Dialogues: e.g., 01KACE6RAZKQ93354SF18CJHHC
  • Messages: e.g., 01KAD7FYPBVZBF5J1KVVR9NR8S
  • Memories: Use custom keys or auto-generated ULIDs
  • Projects: e.g., 01KAD6R34620P1C5J0M2KRMGRG

These IDs are:

  • Globally unique: No collisions across your account
  • Time-sortable: IDs contain timestamp information for natural ordering
  • URL-safe: Can be used directly in API paths
  • Automatically generated: No need to create them manually

Next Steps

Built with DialogueDB