Appearance
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}/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
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
| 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 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.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
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:
| Feature | State | Memory | Messages |
|---|---|---|---|
| Lifespan | Per-dialogue | Indefinite | Per-dialogue |
| Search | Not searchable | Semantic search | Semantic search |
| Updates | Full replace | 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 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
});Related Endpoints
- Dialogues API - Create and manage dialogues
- Memory API - Long-term knowledge storage
- Messages API - Conversation message management