Skip to content

State API

Manage temporary, dialogue-specific state data.

Overview

The State API provides a key-value store for dialogue-specific context and session data. Unlike memories (which persist across conversations and are searchable), state is designed for temporary, conversation-scoped data that changes frequently.

Key characteristics:

  • Scoped to individual dialogues
  • Merged with existing state on each update
  • Not searchable
  • Maximum 1MB uncompressed size
  • Can be updated even after dialogue is ended
  • Included automatically in dialogue GET responses

Common use cases:

  • Multi-step workflow progress tracking
  • Temporary session variables
  • Conversation context for your application logic
  • Shopping cart or form data during a conversation

Update Dialogue State

Create or update the state for a dialogue.

Endpoint

http
PUT /dialogue/{id}/state

Authentication

Bearer token (via Authorization header)

Path Parameters

ParameterTypeRequiredDescription
idstringYesDialogue identifier

Request Body

Any JSON object representing your state data. The entire body becomes the state.

typescript
{
  [key: string]: any;  // Your custom key-value pairs
}

Response

The response body is the state

typescript
{
  [key: string]: any;
}

For example, if you set { "currentStep": "payment", "cartTotal": 299.99 }, the response is:

json
{
  "currentStep": "payment",
  "cartTotal": 299.99
}

Behavior

  • Creates state if it doesn't exist for the dialogue
  • Merges with existing state if it already exists — new keys are added, existing keys are updated, keys you don't mention are kept as-is
  • State data is compressed for efficient storage
  • Maximum 1MB uncompressed size
  • Can be updated even after dialogue status is "ended"
  • State is automatically included in GET /dialogue/{id} responses

Data Format

State accepts any valid JSON structure:

typescript
// Simple key-value
{
  "currentStep": "payment",
  "itemsInCart": 3
}

// Nested objects
{
  "user": {
    "name": "Jane",
    "preferences": {
      "theme": "dark"
    }
  },
  "session": {
    "started": "2025-01-15T10:00:00Z",
    "lastActive": "2025-01-15T10:30:00Z"
  }
}

// Arrays and mixed types
{
  "completedSteps": ["intro", "details", "review"],
  "score": 87,
  "isVerified": true
}

Use Cases

  • Conversation context: Track current topic, intent, or conversation stage
  • Multi-step flows: Store progress through wizards or forms
  • Session data: Temporary variables for the duration of the conversation
  • Application logic: Custom flags and state your app needs
  • Shopping carts: Items and totals during a purchase flow
  • Form data: Partially completed forms across multiple turns

Errors

StatusError CodeDescription
400MISSING_PARAMETERMissing required dialogue ID
400INVALID_INPUTInvalid JSON or exceeds 1MB size limit
401N/AUnauthorized - invalid or missing API key
404DIALOGUE_NOT_FOUNDDialogue does not exist
429RATE_LIMIT_EXCEEDEDToo many requests - retry with backoff
500INTERNAL_ERRORServer error - contact support with requestId

See Error Handling for complete error reference.

Examples

bash
curl -X PUT https://api.dialoguedb.com/dialogue/dlg_abc123/state \
  -H "Authorization: Bearer DIALOGUE_DB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "currentStep": "payment",
    "cartTotal": 299.99
  }'
typescript
const response = await fetch(
  `https://api.dialoguedb.com/dialogue/${dialogueId}/state`,
  {
    method: 'PUT',
    headers: {
      'Authorization': 'Bearer DIALOGUE_DB_API_KEY',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      currentTopic: 'billing',
      userIntent: 'cancel_subscription',
      requiresReview: true,
      supportTicketId: 'TKT-12345'
    })
  }
);

const state = await response.json();
console.log('State updated:', state);  // { currentTopic: 'billing', ... }
python
import requests

response = requests.put(
    f"https://api.dialoguedb.com/dialogue/{dialogue_id}/state",
    headers={"Authorization": "Bearer DIALOGUE_DB_API_KEY"},
    json={
        "currentStep": "payment",
        "cartTotal": 299.99
    }
)

state = response.json()
print("State updated:", state)  # { "currentStep": "payment", ... }
typescript
import { DialogueDB } from 'dialogue-db';

const db = new DialogueDB({ apiKey: 'DIALOGUE_DB_API_KEY' });
const dialogue = await db.getDialogue(dialogueId);

// Option 1: Direct saveState call
await dialogue.saveState({

  shoppingCart: {
    items: [
      { id: 'prod_123', quantity: 2, price: 29.99 },
      { id: 'prod_456', quantity: 1, price: 49.99 }
    ],
    subtotal: 109.97,
    tax: 9.90,
    total: 119.87
  },
  checkoutStep: 'shipping'
});

// Option 2: Set state property and save
dialogue.state = { currentStep: 'payment', cartTotal: 299.99 };

await dialogue.save();

Retrieve Dialogue State

State is automatically included when retrieving a dialogue.

Endpoint

http
GET /dialogue/{id}

The response includes the state field containing your state data.

Response

typescript
{
  id: string;
  projectId: string;
  // ... other dialogue fields
  state: Record<string, any>;  // Your state
}

Example

typescript
const response = await fetch(
  `https://api.dialoguedb.com/dialogue/${dialogueId}`,
  {
    headers: {
      'Authorization': 'Bearer DIALOGUE_DB_API_KEY'
    }
  }
);

const dialogue = await response.json();

// Access state directly
console.log(dialogue.state.currentStep);  // "payment"
console.log(dialogue.state.cartTotal);    // 299.99

State Patterns

Multi-Step Workflow

Track progress through a multi-step process:

typescript
// Step 1: Initialize workflow
await updateDialogueState(dialogueId, {
  workflow: 'account_setup',
  currentStep: 1,
  totalSteps: 4,
  completed: [],
  data: {}
});

// Step 2: Update as user progresses
await updateDialogueState(dialogueId, {
  workflow: 'account_setup',
  currentStep: 2,
  totalSteps: 4,
  completed: ['personal_info'],
  data: {
    email: 'user@example.com',
    name: 'Jane Doe'
  }
});

// Step 3: Complete workflow
await updateDialogueState(dialogueId, {
  workflow: 'account_setup',
  currentStep: 4,
  totalSteps: 4,
  completed: ['personal_info', 'preferences', 'verification', 'complete'],
  data: {
    email: 'user@example.com',
    name: 'Jane Doe',
    accountId: 'acc_789'
  }
});

Conversation Context

Maintain conversational context:

typescript
await updateDialogueState(dialogueId, {
  currentTopic: 'product_inquiry',
  productId: 'prod_123',
  userSentiment: 'interested',
  questionsAsked: [
    'What are the specs?',
    'Is it in stock?'
  ],
  lastIntent: 'pricing_question'
});

Session Management

Track session-specific information:

typescript
await updateDialogueState(dialogueId, {
  session: {
    id: 'sess_xyz789',
    startedAt: '2025-01-15T10:00:00Z',
    lastActivity: '2025-01-15T10:30:00Z',
    authenticated: true,
    userId: 'user_456'
  },
  permissions: ['read', 'write'],
  features: ['premium_support', 'priority_queue']
});

Form Data Collection

Store partially completed forms:

typescript
// Initial form state
await updateDialogueState(dialogueId, {
  formType: 'contact_request',
  fields: {
    name: null,
    email: null,
    company: null,
    message: null
  },
  validation: {},
  complete: false
});

// Update as fields are filled
await updateDialogueState(dialogueId, {
  formType: 'contact_request',
  fields: {
    name: 'Jane Doe',
    email: 'jane@example.com',
    company: 'Acme Corp',
    message: null  // Still collecting
  },
  validation: {
    name: true,
    email: true,
    company: true
  },
  complete: false
});

Best Practices

Merge Behavior

State updates are merged — new keys are added, existing keys are updated, and keys you don't include are kept as-is:

typescript
// Existing state: { step: 1, total: 100 }

await updateDialogueState(dialogueId, {
  step: 2  // Only updates 'step' — 'total' is preserved
});

// Resulting state: { step: 2, total: 100 }

This applies to nested objects as well:

typescript
// Existing state: { user: { name: "Jane", role: "admin" } }

await updateDialogueState(dialogueId, {
  user: { role: "viewer" }
});

// Resulting state: { user: { name: "Jane", role: "viewer" } }

Keep State Focused

State should be temporary and conversation-specific:

typescript
// Good: Conversation-specific context
{
  currentTopic: 'billing',
  pendingAction: 'cancel_subscription',
  requiresConfirmation: true
}

// Bad: Long-term user data (use Memory instead)
{
  userPreferences: { theme: 'dark' },  // Use Memory API
  accountHistory: [...],                // Use your own database
  pastPurchases: [...]                  // Use your own database
}

Size Management

Stay well under the 1MB limit:

typescript
// Monitor state size
const stateData = {
  // ... your state
};

const sizeInBytes = new Blob([JSON.stringify(stateData)]).size;
const sizeInKB = (sizeInBytes / 1024).toFixed(2);

console.log(`State size: ${sizeInKB} KB`);

if (sizeInBytes > 500000) { // 500KB warning threshold
  console.warn('State is getting large, consider optimization');
}

Clear State When Done

Clean up state when conversations end:

typescript
// Clear state after workflow completion
await updateDialogueState(dialogueId, {
  workflowComplete: true,
  clearedAt: new Date().toISOString()
});

// Or set to minimal state
await updateDialogueState(dialogueId, {});

State vs. Memory vs. Messages

Understanding when to use each storage type:

FeatureStateMemoryMessages
LifespanPer-dialogueIndefinitePer-dialogue
SearchNot searchableSearchableSearchable
UpdatesMerged on updateDelete/recreateImmutable
Size Limit1MBNo explicit limit1MB per message
Use CaseSession contextLong-term factsConversation turns
ScopeDialogue-specificGlobal/namespaceDialogue-specific

Use State when:

  • Tracking temporary conversation context
  • Multi-step workflow progress
  • Session-specific variables
  • Data that changes frequently during a conversation

Use Memory when:

  • Information should persist across dialogues
  • You need semantic search
  • Storing facts, preferences, or long-term knowledge

Use Messages when:

  • Recording actual conversation exchanges
  • Building context for LLM prompts
  • Need chronological dialogue history

Important Notes

State After Dialogue Ends

Unlike messages, state can be updated even after a dialogue is ended:

typescript
// End the dialogue
await dialogueAction(dialogueId, 'end');

// State can still be updated
await updateDialogueState(dialogueId, {
  finalStatus: 'completed',
  closedAt: new Date().toISOString()
});

State is Not Searchable

State is not searchable. If you need to find data by content, use Memory or Messages:

typescript
// Cannot search state

// Use Memory for searchable data
await db.createMemory({
  value: 'User prefers phone support',
  tags: ['preference', 'support']
});

// Search memories
const results = await db.searchMemories('support preferences');

State is Per-Dialogue

Each dialogue has its own independent state. Use Memory with namespace for shared data:

typescript
// State is dialogue-specific
await updateDialogueState(dialogueId1, { theme: 'dark' });
// dialogueId2 doesn't have this state

// Use Memory with namespace for user-wide data
await db.createMemory({
  id: `user_${userId}_theme`,
  value: { theme: 'dark' },
  namespace: userId
});

Built with DialogueDB