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 (user-provided or auto-generated)
  threadOf?: string;       // Parent dialogue if this is a thread
  requestId: string;       // Request tracking ID
  status: "active" | "ended" | "archived";
  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 db.createDialogue({
  messages: [{
    role: 'user',
    content: 'Hello!'
  }]
});

// Dialogue with metadata
const dialogue = await db.createDialogue({
  metadata: {
    userId: 'user_123',
    channel: 'web',
    sessionId: 'session_abc'
  },
  tags: ['customer-support', 'billing-question'],
  messages: [{
    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 (user-provided or auto-generated)
  dialogueId: string;      // Parent dialogue ID
  role: "user" | "assistant" | "system";  // Message role
  content: string | object | object[];    // Message content (flexible shape)
  name?: string;           // Optional name for the speaker
  metadata: object;        // Custom metadata
  tags: string[];          // Custom tags
  created: string;         // Creation timestamp (ISO 8601)
}

Content accepts any shape: plain strings, structured objects, or arrays of content blocks. This lets you store tool calls, multi-part messages, and provider-specific formats directly without serializing to a string. See the Messages API for detailed examples.

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 dialogue.saveMessage({
  role: 'user',
  content: 'Can you explain quantum computing?'
});

// Message with metadata
const message = await dialogue.saveMessage({
  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 = await dialogue.loadMessages({
  limit: 50,
  next: paginationToken // From previous request
});

// Get specific message
const message = await dialogue.getMessage(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 dialogue.createThread({
  metadata: { purpose: 'tool-call' },
  tags: ['fetch_weather_data']
});

// Add messages after creation — createThread does not accept messages
await thread.saveMessage({
  role: 'system',
  content: 'Executing tool call: fetch_weather_data'
});

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

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

Listing Threads

typescript
// Get all threads of a dialogue
const threads = await dialogue.getThreads();

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

// Each thread is a full dialogue
threads.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 db.createDialogue({
  state: {
    currentTopic: 'weather',
    location: 'San Francisco',
    userPreference: 'celsius'
  },
  messages: [{
    role: 'user',
    content: 'What\'s the temperature?'
  }]
});

// Update state
await dialogue.saveState({
  currentTopic: 'weather',
  location: 'New York', // Updated
  userPreference: 'fahrenheit' // Updated
});

// Or set state property and save
dialogue.state = { currentTopic: 'weather', location: 'New York' };
await dialogue.save();

// Retrieve dialogue with state
const d = await db.getDialogue(dialogueId);
console.log(d.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 dialogue.end();
bash
curl -X POST https://api.dialoguedb.com/dialogue/DIALOGUE_ID/end \
  -H "Authorization: Bearer DIALOGUE_DB_API_KEY"

Use when:

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

Compact Conversation

Summarize a long conversation to reduce token usage:

bash
# Compact a dialogue (REST API only — not yet available in SDK)
curl -X POST https://api.dialoguedb.com/dialogue/DIALOGUE_ID/compact \
  -H "Authorization: Bearer DIALOGUE_DB_API_KEY"

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

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

Pagination

When retrieving large datasets, use pagination:

typescript
// Initial request
const { items, next } = await db.listDialogues({
  limit: 20
});

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

// Same pattern for messages
const messages = await dialogue.loadMessages({
  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 db.createDialogue({
  namespace: 'user_789',
  messages: [{
    role: 'user',
    content: 'Hello'
  }]
});

// List dialogues by namespace
const { items } = await db.listDialogues({
  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 db.createDialogue({
  namespace: `org_${organizationId}`,
  message: { role: 'user', content: 'Help request' }
});

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

User-specific conversations:

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

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

Memory isolation by user:

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

// Search only this user's memories
const memories = await db.searchMemories('preferences', {
  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: User-provided or auto-generated (e.g., 01KACE6RAZKQ93354SF18CJHHC or my-custom-id)
  • Messages: User-provided or auto-generated (e.g., 01KAD7FYPBVZBF5J1KVVR9NR8S)
  • Memories: Custom keys or auto-generated
  • Projects: e.g., 01KAD6R34620P1C5J0M2KRMGRG

Auto-generated 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

Custom IDs must be unique within a project and can be 1-64 characters.

Next Steps

Built with DialogueDB