Chat API
The chat endpoint is Studio's primary interface for AI-powered content management. It uses Server-Sent Events (SSE) to stream the AI response and tool execution results in real time.
Internal SPA endpoint
This documents the internal chat endpoint that powers the Studio web app. It is authenticated with a session cookie and gated by workspace + project roles (owner / admin / member, combined with the project role editor / reviewer / viewer). For programmatic, API-key access, use the Conversation API instead — it has its own 3-role model.
Chat (SSE)
Start a conversation with the AI agent.
POST /api/workspaces/:workspaceId/projects/:projectId/chatRequest Body
interface ChatRequest {
message: string
conversationId?: string // Resume existing conversation
model?: string // AI model override
context: {
activeModelId: string | null
activeLocale: string
activeEntryId: string | null
panelState: 'overview' | 'model' | 'branch' | 'vocabulary'
activeBranch: string | null
contextItems?: ContextItem[]
}
}| Field | Type | Required | Description |
|---|---|---|---|
message | string | Yes | User message text |
conversationId | string | No | Existing conversation ID for multi-turn |
model | string | No | AI model override (default: determined by plan) |
context | ChatUIContext | Yes | Current UI state for context-aware responses |
Context Items
The contextItems array allows the client to pin specific content items to the conversation:
interface ContextItem {
type: 'model' | 'entry' | 'field' | 'asset'
modelId: string
modelName?: string
entryId?: string
fieldId?: string
assetId?: string
data?: unknown
}Response (SSE Stream)
The response is a stream of Server-Sent Events with data: lines containing JSON:
data: {"type":"conversation","conversationId":"uuid"}
data: {"type":"text","content":"I'll create a new blog post"}
data: {"type":"tool_use","id":"tool_1","name":"save_content"}
data: {"type":"tool_result","id":"tool_1","name":"save_content","result":{"branch":"cr/content/blog-post/en/...","merged":true}}
data: {"type":"text","content":"Done! I've created the blog post and it has been auto-merged."}
data: {"type":"done","usage":{"inputTokens":1234,"outputTokens":567},"affected":{"models":["blog-post"],"locales":["en"],"snapshotChanged":false,"branchesChanged":true}}Event Types
| Event Type | Description | Key Fields |
|---|---|---|
conversation | Conversation created/resumed | conversationId |
text | AI text chunk (streaming) | content |
tool_use | Tool execution started | id, name |
tool_result | Tool execution completed | id, name, result |
done | Stream complete | usage, affected |
error | Error occurred | message |
Affected Resources
The done event includes an affected object that tells the client what to refresh:
interface AffectedResources {
models: string[] // Model IDs with content changes
locales: string[] // Locales that were modified
snapshotChanged: boolean // Model definitions changed (reload schema)
branchesChanged: boolean // Branch list changed (reload branches)
branch?: string // Specific branch created/merged/deleted
}Available Agent Tools
The agent can call these tools based on user permissions:
Roles here are the internal agent's effective roles (workspace admin/owner, or the member's project role viewer/reviewer/editor). "Min Role" is the lowest role that may invoke the tool.
| Tool | Description | Min Role |
|---|---|---|
list_models | List all content models | viewer |
get_content | Read content entries | viewer |
save_content | Create/update content | editor |
delete_content | Delete entries or dictionary keys | editor |
save_model | Create/update model definitions | admin |
validate | Validate content against schemas | viewer |
list_branches | List pending cr/* branches | viewer |
merge_branch | Merge a content branch | reviewer |
reject_branch | Reject/delete a branch | reviewer |
init_project | Initialize .contentrain/ structure | admin |
copy_locale | Copy content between locales | editor |
brain_query | Read from brain cache | viewer |
brain_search | Full-text search across content | viewer |
brain_analyze | Content analysis (SEO, parity, stale, quality) | viewer |
validate_schema | Comprehensive schema validation | viewer |
search_media | Search media library | viewer |
upload_media | Upload media from URL | editor |
get_media | Get media asset metadata | viewer |
update_status | Publish/unpublish/archive entries | editor |
update_media | Update asset alt text, tags, focal point | editor |
delete_media | Delete an asset and its variants | editor |
branch_health | Check pending-branch warn/block thresholds | viewer |
relation_expand | Resolve forward/reverse relations for an entry | viewer |
vocabulary | Read or update the project glossary | editor |
add_locale | Register a new supported locale | admin |
delete_model | Permanently delete a model and its content | admin |
list_submissions | List form submissions | viewer |
approve_submission | Approve form submission | reviewer |
reject_submission | Reject form submission | reviewer |
Error Codes
| Status | Condition |
|---|---|
400 | Missing message or invalid context |
401 | No valid session |
403 | No project access or AI feature not available on plan |
429 | Monthly message limit exceeded |
Example
curl -X POST http://localhost:3000/api/workspaces/{wsId}/projects/{projId}/chat \
-H "Content-Type: application/json" \
-H "Cookie: h3-session=..." \
-d '{
"message": "Create a blog post titled Hello World",
"context": {
"activeModelId": "blog-post",
"activeLocale": "en",
"activeEntryId": null,
"panelState": "model",
"activeBranch": null
}
}' \
--no-bufferConversations
List Conversations
GET /api/workspaces/:workspaceId/projects/:projectId/conversationsDelete Conversation
DELETE /api/workspaces/:workspaceId/projects/:projectId/conversations/:conversationIdGet Conversation Messages
GET /api/workspaces/:workspaceId/projects/:projectId/conversations/:conversationId/messagesRelated Pages
- Agent System -- how the agent works internally
- Content API -- direct content operations
- Conversation API -- public conversation endpoint