Skip to content

State API

Manage ephemeral, dialogue-specific state data.

Node.js SDK Support

State management is currently available via REST API. Node.js SDK support coming soon.

Overview

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

Key characteristics:

  • Scoped to individual dialogues
  • Replaced entirely on each update (not merged)
  • Not vectorized or 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 replace 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

typescript
{
  data: Record<string, any>;  // Your state data
  created: string;            // When state was first created
  modified: string;           // When state was last updated
}

Behavior

  • Creates state if it doesn't exist for the dialogue
  • Replaces entire state if it already exists (not merged)
  • 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 YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "currentStep": "payment",
    "cartTotal": 299.99
  }'
bash
curl -X PUT https://api.dialoguedb.com/dialogue/dlg_abc123/state \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "workflow": {
      "currentStep": 3,
      "totalSteps": 5,
      "completed": ["intro", "details", "review"]
    },
    "userData": {
      "email": "user@example.com",
      "verified": true
    },
    "sessionInfo": {
      "startedAt": "2025-01-15T10:00:00Z",
      "expiresAt": "2025-01-15T12:00:00Z"
    }
  }'
typescript
const response = await fetch(
  `https://api.dialoguedb.com/dialogue/${dialogueId}/state`,
  {
    method: 'PUT',
    headers: {
      'Authorization': 'Bearer YOUR_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.data);
typescript
// SDK state support coming soon - use REST API
const response = await fetch(
  `https://api.dialoguedb.com/dialogue/${dialogueId}/state`,
  {
    method: 'PUT',
    headers: {
      'Authorization': `Bearer ${process.env.DIALOGUE_DB_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      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'
    })
  }
);

const state = await response.json();

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: {
    data: Record<string, any>;  // Your state data
    created: string;
    modified: string;
  }
}

Example

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

const dialogue = await response.json();

// Access state directly
console.log(dialogue.state.data.currentStep);  // "payment"
console.log(dialogue.state.data.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

Full Replacement Pattern

Remember that state is replaced entirely, not merged:

typescript
// ❌ This will lose existing state
const dialogue = await getDialogue(dialogueId);
// dialogue.state.data = { step: 1, total: 100 }

await updateDialogueState(dialogueId, {
  step: 2  // This replaces entire state - total is now lost!
});

// ✅ Correct: Read first, then replace
const dialogue = await getDialogue(dialogueId);
const currentState = dialogue.state.data;

await updateDialogueState(dialogueId, {
  ...currentState,  // Preserve existing
  step: 2           // Update specific field
});

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 searchableSemantic searchSemantic search
UpdatesFull replaceDelete/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 indexed for vector search. If you need to search by content, use Memory or Messages:

typescript
// ❌ Cannot search state
// State is not vectorized

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

// Search memories
const results = await search({
  query: 'support preferences',
  object: 'memory'
});

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 createMemory({
  key: `user_${userId}_theme`,
  value: { theme: 'dark' },
  namespace: userId
});

Built with DialogueDB