Skip to content

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/chat

Request Body

typescript
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[]
  }
}
FieldTypeRequiredDescription
messagestringYesUser message text
conversationIdstringNoExisting conversation ID for multi-turn
modelstringNoAI model override (default: determined by plan)
contextChatUIContextYesCurrent UI state for context-aware responses

Context Items

The contextItems array allows the client to pin specific content items to the conversation:

typescript
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 TypeDescriptionKey Fields
conversationConversation created/resumedconversationId
textAI text chunk (streaming)content
tool_useTool execution startedid, name
tool_resultTool execution completedid, name, result
doneStream completeusage, affected
errorError occurredmessage

Affected Resources

The done event includes an affected object that tells the client what to refresh:

typescript
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.

ToolDescriptionMin Role
list_modelsList all content modelsviewer
get_contentRead content entriesviewer
save_contentCreate/update contenteditor
delete_contentDelete entries or dictionary keyseditor
save_modelCreate/update model definitionsadmin
validateValidate content against schemasviewer
list_branchesList pending cr/* branchesviewer
merge_branchMerge a content branchreviewer
reject_branchReject/delete a branchreviewer
init_projectInitialize .contentrain/ structureadmin
copy_localeCopy content between localeseditor
brain_queryRead from brain cacheviewer
brain_searchFull-text search across contentviewer
brain_analyzeContent analysis (SEO, parity, stale, quality)viewer
validate_schemaComprehensive schema validationviewer
search_mediaSearch media libraryviewer
upload_mediaUpload media from URLeditor
get_mediaGet media asset metadataviewer
update_statusPublish/unpublish/archive entrieseditor
update_mediaUpdate asset alt text, tags, focal pointeditor
delete_mediaDelete an asset and its variantseditor
branch_healthCheck pending-branch warn/block thresholdsviewer
relation_expandResolve forward/reverse relations for an entryviewer
vocabularyRead or update the project glossaryeditor
add_localeRegister a new supported localeadmin
delete_modelPermanently delete a model and its contentadmin
list_submissionsList form submissionsviewer
approve_submissionApprove form submissionreviewer
reject_submissionReject form submissionreviewer

Error Codes

StatusCondition
400Missing message or invalid context
401No valid session
403No project access or AI feature not available on plan
429Monthly message limit exceeded

Example

bash
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-buffer

Conversations

List Conversations

GET /api/workspaces/:workspaceId/projects/:projectId/conversations

Delete Conversation

DELETE /api/workspaces/:workspaceId/projects/:projectId/conversations/:conversationId

Get Conversation Messages

GET /api/workspaces/:workspaceId/projects/:projectId/conversations/:conversationId/messages

Released under the AGPL-3.0 License.