Skip to Content
APIOrg Chat

Org Chat

The main agent-interaction endpoint. Every path that results in an agent doing work — web UI chat, MCP do() tool, inbound WhatsApp/Slack/email messages — ultimately calls this.

POST /api/org/chat/message

Sends a message to a specific node and streams the agent’s response.

Request

POST /api/org/chat/message Content-Type: application/json Authorization: Bearer <token> { "structure_id": "uuid-of-the-workspace", "role_id": "executive-office-cmo", "message": "Launch this week's paid campaigns.", "attachments": [ { "name": "marketing-plan.pdf", "content_base64": "...", "content_type": "application/pdf" } ] }
FieldTypeRequiredNotes
structure_idstring (UUID)The workspace holding the target node.
role_idstringThe target node’s role (e.g. executive-office-cmo).
messagestringNatural-language request for the agent.
attachmentsarrayOptional files staged to the agent’s artifact shelf.

Response

200 OK — a text/event-stream of newline-delimited JSON events:

data: {"type":"thought","content":"Looking up skills for 'launch campaigns'...","timestamp":"2026-04-20T14:30:00Z"} data: {"type":"tool_call","content":{"tool":"list_skills"},"timestamp":"..."} data: {"type":"tool_result","content":{"matched":"ad-campaigns"},"timestamp":"..."} data: {"type":"text","content":"I'll run the ad-campaigns skill.","timestamp":"..."} data: {"type":"tool_call","content":{"tool":"validate_prereqs"},"timestamp":"..."} ... data: {"type":"complete","content":{"summary":"Launched 3 campaigns","artifacts":["campaign-config.json"]},"timestamp":"..."}

Event types:

TypePayloadMeaning
thought{content: string}Agent’s internal reasoning step.
tool_call{tool, args}Agent invoked a tool inside its container.
tool_result{tool, result}Tool returned.
text{content}Streamed text the agent produced (partial reply).
decision{title, body}Agent committed a decision to the workspace log.
milestone{title}Agent flagged a milestone.
skill_learned{slug, name}Agent learned a new skill — persisted as markdown.
complete{summary, artifacts[]}Terminal event.
error{message}Terminal event; something went wrong.

How the stream terminates

Either complete or error is always the last event. Long-running skills can run for minutes — the stream stays open and emits events as the agent progresses.

If the HTTP connection drops mid-stream, the agent keeps running. Re-read with GET /api/org/chat/history (below) to pick up where you left off.

POST /api/org/chat/message/sync

Same as above but non-streaming. Returns once the agent’s turn completes. Use for short tasks (under 30s) or when your HTTP client can’t handle streaming.

Response

200 OK:

{ "events": [ ... full event list ... ], "summary": "string", "artifacts": [ ... ] }

GET /api/org/chat/history

Returns recent chat history for a node.

Request

GET /api/org/chat/history?structure_id=<uuid>&role_id=<role>&limit=50

Response

200 OK — array of historical messages (both user-side and agent-side), newest first.

Upstream detail

org_chat_service.stream_message() (cto-gui-libvirt-backend/app/services/org_chat_service.py:1986) is the dispatcher. It:

  1. Enriches the request with workspace context + agent starting context.
  2. Routes to either the agent’s container (if deployed) or the local Claude SDK fallback.
  3. Streams events back as they’re produced.
  4. Persists the final state + any learned skills + artifacts to disk.

This is the endpoint the direktor-mcp do() tool forwards to. Every surface into the agent flows through this single funnel.

Last updated on