Appearance
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}/stateAuthentication
Bearer token (via Authorization header)
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Dialogue 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
| Status | Error Code | Description |
|---|---|---|
| 400 | MISSING_PARAMETER | Missing required dialogue ID |
| 400 | INVALID_INPUT | Invalid JSON or exceeds 1MB size limit |
| 401 | N/A | Unauthorized - invalid or missing API key |
| 404 | DIALOGUE_NOT_FOUND | Dialogue does not exist |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests - retry with backoff |
| 500 | INTERNAL_ERROR | Server 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.99State 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:
| Feature | State | Memory | Messages |
|---|---|---|---|
| Lifespan | Per-dialogue | Indefinite | Per-dialogue |
| Search | Not searchable | Searchable | Searchable |
| Updates | Merged on update | Delete/recreate | Immutable |
| Size Limit | 1MB | No explicit limit | 1MB per message |
| Use Case | Session context | Long-term facts | Conversation turns |
| Scope | Dialogue-specific | Global/namespace | Dialogue-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
});Related Endpoints
- Dialogues API - Create and manage dialogues
- Memory API - Long-term knowledge storage
- Messages API - Conversation message management

