Chat sessions give you an interactive, multi-turn agent that stays alive between messages. Unlike runs (fire-and-forget batch jobs), a chat session keeps its sandbox running so the agent retains full context — files, environment, and conversation history — across every message you send.
Chat session lifecycle
create ──→ active ──→ idle (snapshot) ──→ resumed ──→ active
│ │
└──→ ended (delete) ←─────────────────┘
| Status | Description |
|---|
active | Sandbox is running, ready for messages |
idle | Sandbox snapshotted after idle timeout, restorable on next message |
ended | Session terminated, sandbox destroyed |
Step 1: Create a session
const chat = await client.agents.chat.create({
title: 'Contract Review Session',
model: 'anthropic/claude-sonnet-4.6',
idleTimeoutMs: 300000, // 5 minutes
})
console.log(chat.id) // chat_xxx
console.log(chat.status) // "active"
{
"id": "chat_abc123",
"status": "active",
"idleTimeoutMs": 300000,
"createdAt": "2026-03-03T21:23:18.434Z"
}
Create parameters
| Parameter | Type | Required | Description |
|---|
title | string | no | Human-readable session name |
model | string | no | LLM model (default: anthropic/claude-sonnet-4.6) |
idleTimeoutMs | integer | no | Idle time before snapshot eligibility (default: 15 min, min: 1 min, max: 24 hr) |
Step 2: Send messages
POST /agent/v1/chat/:id/message
Messages are proxied to the agent running in the sandbox. The agent has the same full tool access as batch runs — vaults, legal research, OCR, web search, and the casedev CLI.
const response = await client.agents.chat.sendMessage(chat.id, {
parts: [{ type: 'text', text: 'Search vault vault_abc for indemnification clauses.' }],
})
The response contains the agent’s output. Send follow-up messages to the same session — the agent retains full context from prior turns.
If the sandbox was snapshotted due to idle timeout, sending a message automatically restores it.
There is a brief resume delay (~5-10s) but no context is lost.
Step 3: Stream events (optional)
GET /agent/v1/chat/:id/stream
Open an SSE connection to receive real-time events as the agent works. Events are buffered server-side, so you can reconnect without missing anything.
const stream = client.agents.chat.stream(chat.id)
for await (const event of stream) {
console.log(event.type, event.data)
}
Replay from a sequence number
Each SSE event has a numeric id. Pass lastEventId to replay events after a given sequence — useful for reconnecting after a network drop:
# Replay events after sequence 42
curl -N "https://api.case.dev/agent/v1/chat/$CHAT_ID/stream?lastEventId=42" \
-H "Authorization: Bearer $CASEDEV_API_KEY"
The Last-Event-ID HTTP header is also supported, following the SSE spec.
Events are buffered up to 500 per session. For long-running sessions with high event volume,
connect the stream early to avoid gaps.
Cancel generation
POST /agent/v1/chat/:id/cancel
Abort the agent’s current generation without ending the session. The sandbox stays alive and you can send another message immediately.
const result = await client.agents.chat.cancel(chat.id)
console.log(result.ok) // true
End the session
DELETE /agent/v1/chat/:id
Snapshots the sandbox, terminates it, and marks the session as ended. The response includes runtime billing data.
const result = await client.agents.chat.delete(chat.id)
console.log(result.status) // "ended"
console.log(result.runtimeMs) // 48230
console.log(result.cost) // 0.00268
{
"id": "chat_abc123",
"status": "ended",
"snapshotImageId": "im-abc123",
"runtimeMs": 48230,
"cost": 0.00268
}
| Field | Type | Description |
|---|
status | string | Always "ended" |
snapshotImageId | string | Final sandbox snapshot (nullable) |
runtimeMs | integer | Total sandbox uptime in milliseconds |
cost | number | Runtime cost in USD ($0.20/hr) |
Sending a message to an ended session returns 409 Conflict. Create a new session to continue.
Idle timeout and snapshots
Chat sessions have a configurable idle timeout (default: 15 minutes). When no messages are sent within the timeout window:
- The sandbox is snapshotted (memory + filesystem persisted)
- The sandbox is terminated to stop billing
- The next message automatically restores the sandbox from the snapshot
This means you only pay for active compute time, not idle wait. A background reaper runs every 5 minutes to clean up idle sessions.
Runs vs. chat
| Runs | Chat |
|---|
| Pattern | Single prompt in, result out | Multi-turn conversation |
| Sandbox lifetime | One execution | Persists across messages |
| Streaming | Poll or webhook | Real-time SSE |
| Context | Fresh each run | Retained across turns |
| Billing | Per-execution | Per-second of sandbox uptime |
| Best for | Batch processing, scheduled tasks | Interactive workflows, iterative analysis |
Use runs for fire-and-forget batch tasks. Use chat when you need back-and-forth
interaction with the agent or when the task requires iterative refinement.
Authentication
Chat endpoints require an API key with agent:read (for streaming) and agent:write (for create, message, cancel, delete) permissions. Session-based or OAuth authentication is not supported — all downstream token usage and billing is attributed to the API key’s organization.
Complete example
import Casedev from '@case.dev/sdk'
const client = new Casedev({ apiKey: process.env.CASEDEV_API_KEY })
// 1. Create a session
const chat = await client.agents.chat.create({
title: 'Deposition Analysis',
model: 'anthropic/claude-sonnet-4.6',
})
// 2. First message
await client.agents.chat.sendMessage(chat.id, {
parts: [{ type: 'text', text: 'Search vault vault_depo for all witness testimony about the accident timeline.' }],
})
// 3. Follow-up based on results
await client.agents.chat.sendMessage(chat.id, {
parts: [{ type: 'text', text: 'Now cross-reference that with the police report in vault vault_evidence.' }],
})
// 4. End session when done
const result = await client.agents.chat.delete(chat.id)
console.log(`Session cost: $${result.cost.toFixed(4)}`)