Skip to content

Latest commit

 

History

History
1561 lines (1211 loc) · 21.5 KB

File metadata and controls

1561 lines (1211 loc) · 21.5 KB

API Reference

This document describes the WebSocket and REST APIs for interacting with Botical.


Connection

WebSocket Endpoint

ws://HOST/ws?projectId={projectId}&token={authToken}

Query Parameters:

  • projectId (required): Project to connect to
  • token (required): JWT authentication token

On Connect: Server sends:

{
  "type": "connected",
  "payload": {
    "connectionId": "conn_abc123",
    "projectId": "project_xyz",
    "userId": "user_456"
  }
}

Message Protocol

Request (Client → Server)

{
  id: string;       // Unique request ID for response matching
  type: string;     // Request type (see Operations below)
  payload: unknown; // Type-specific data
}

Response (Server → Client)

{
  id: string;        // Matches request ID
  type: "response";
  success: boolean;
  payload?: unknown; // Result data (if success)
  error?: {          // Error info (if !success)
    code: string;
    message: string;
    details?: unknown;
  }
}

Event (Server → Client)

{
  type: string;      // Event type
  payload: unknown;  // Event data
}

Operations

Session Operations

session.create

Create a new conversation session.

Request Payload:

{
  title?: string;          // Optional title (auto-generated if omitted)
  agent?: string;          // Agent ID to use (default: "default")
}

Response Payload:

{
  session: {
    id: string;
    slug: string;
    title: string;
    agent: string;
    status: "active";
    createdAt: number;
    updatedAt: number;
  }
}

session.list

List sessions in the project.

Request Payload:

{
  status?: "active" | "archived" | "deleted";
  limit?: number;          // Default: 50
  cursor?: string;         // For pagination
}

Response Payload:

{
  sessions: Session[];
  nextCursor?: string;
}

session.get

Get session details with recent messages.

Request Payload:

{
  sessionId: string;
  includeMessages?: boolean;  // Default: true
  messageLimit?: number;      // Default: 50
}

Response Payload:

{
  session: Session;
  messages?: Message[];
}

session.delete

Delete (archive) a session.

Request Payload:

{
  sessionId: string;
  permanent?: boolean;  // Hard delete, default: false
}

Response Payload:

{
  deleted: true
}

Message Operations

message.send

Send a message to the agent.

Request Payload:

{
  sessionId: string;
  content: string;        // User message text
  attachments?: {         // Optional file attachments
    path: string;
    inline?: boolean;
  }[];
}

Response Payload:

{
  messageId: string;      // The assistant's response message ID
  status: "completed" | "error";
}

Streaming Events: While processing, server sends events (see Events section).


message.cancel

Cancel an in-progress message generation.

Request Payload:

{
  sessionId: string;
}

Response Payload:

{
  cancelled: true
}

message.retry

Retry from a specific message.

Request Payload:

{
  sessionId: string;
  messageId: string;  // User message to retry from
}

Response Payload: Same as message.send.


Agent Operations

agent.list

List available agents.

Request Payload:

{
  mode?: "primary" | "subagent" | "all";
  includeHidden?: boolean;  // Default: false
}

Response Payload:

{
  agents: Agent[]
}

agent.create

Create a custom agent.

Request Payload:

{
  name: string;
  description?: string;
  mode: "primary" | "subagent" | "all";
  prompt?: string;         // System prompt
  providerId?: string;     // LLM provider
  modelId?: string;        // Model ID
  temperature?: number;
  maxSteps?: number;
  permissions?: Permission[];
}

Response Payload:

{
  agent: Agent
}

agent.update

Update an agent configuration.

Request Payload:

{
  agentId: string;
  updates: Partial<AgentConfig>;
}

Response Payload:

{
  agent: Agent
}

Tool Operations

tool.list

List available tools.

Request Payload:

{
  includeDisabled?: boolean;  // Default: false
}

Response Payload:

{
  tools: Tool[]
}

tool.create

Create a custom tool.

Request Payload:

{
  name: string;
  description: string;
  type: "code" | "mcp" | "http";
  parametersSchema: JSONSchema;
  // For type: "code"
  code?: string;
  // For type: "mcp"
  mcpServer?: string;
  mcpTool?: string;
  // For type: "http"
  httpUrl?: string;
  httpMethod?: "GET" | "POST" | "PUT" | "DELETE";
}

Response Payload:

{
  tool: Tool
}

tool.approve

Approve a pending tool execution.

Request Payload:

{
  toolCallId: string;
  sessionId: string;
  remember?: boolean;  // Save permission for future
  scope?: "session" | "project";
}

Response Payload:

{
  approved: true
}

tool.reject

Reject a pending tool execution.

Request Payload:

{
  toolCallId: string;
  sessionId: string;
  reason?: string;
}

Response Payload:

{
  rejected: true
}

File Operations

file.list

List files in a directory.

Request Payload:

{
  path?: string;        // Default: "/" (project root)
  recursive?: boolean;  // Default: false
  pattern?: string;     // Glob pattern filter
}

Response Payload:

{
  files: {
    path: string;
    type: "file" | "directory";
    size?: number;
    mimeType?: string;
    updatedAt: number;
  }[]
}

file.read

Read file contents.

Request Payload:

{
  path: string;
  encoding?: "utf-8" | "base64";  // Default: "utf-8"
}

Response Payload:

{
  path: string;
  content: string;
  mimeType?: string;
  size: number;
}

file.write

Write file contents.

Request Payload:

{
  path: string;
  content: string;
  encoding?: "utf-8" | "base64";
  createDirectories?: boolean;  // Default: true
}

Response Payload:

{
  path: string;
  size: number;
  version: number;
}

file.delete

Delete a file or directory.

Request Payload:

{
  path: string;
  recursive?: boolean;  // For directories, default: false
}

Response Payload:

{
  deleted: true
}

Subscription Operations

subscribe

Subscribe to a channel for events.

Request Payload:

{
  channel: string;  // "session:{id}" or "project:{id}"
}

Response Payload:

{
  subscribed: true;
  channel: string;
}

unsubscribe

Unsubscribe from a channel.

Request Payload:

{
  channel: string;
}

Response Payload:

{
  unsubscribed: true;
}

ping

Health check / keep-alive.

Request Payload: (none)

Response Payload:

{
  pong: number;  // Server timestamp
}

Events

Events are pushed from server to client without a request.

Session Events

session.created

{
  session: Session
}

session.updated

{
  session: Session
}

session.deleted

{
  sessionId: string
}

Message Streaming Events

These events are sent during message generation:

message.created

New message started.

{
  sessionId: string;
  message: Message;
}

message.text.delta

Incremental text output.

{
  sessionId: string;
  messageId: string;
  partId: string;
  delta: string;
}

message.text.complete

Text part finished.

{
  sessionId: string;
  messageId: string;
  partId: string;
  text: string;
}

message.reasoning.delta

Chain-of-thought text (if enabled).

{
  sessionId: string;
  messageId: string;
  partId: string;
  delta: string;
}

message.tool.call

Agent is calling a tool.

{
  sessionId: string;
  messageId: string;
  partId: string;
  toolName: string;
  toolCallId: string;
  input: unknown;
}

message.tool.result

Tool execution completed.

{
  sessionId: string;
  messageId: string;
  partId: string;
  toolCallId: string;
  output: string;
  metadata?: unknown;
}

message.tool.error

Tool execution failed.

{
  sessionId: string;
  messageId: string;
  partId: string;
  toolCallId: string;
  error: string;
}

message.complete

Message generation finished.

{
  sessionId: string;
  messageId: string;
  finishReason: "stop" | "tool-calls" | "length" | "error";
  usage: {
    inputTokens: number;
    outputTokens: number;
    cost: number;
  }
}

message.error

Message generation failed.

{
  sessionId: string;
  messageId: string;
  error: {
    code: string;
    message: string;
  }
}

Tool Approval Events

tool.approval.required

Agent needs permission to run a tool.

{
  sessionId: string;
  messageId: string;
  toolCallId: string;
  toolName: string;
  input: unknown;
  message: string;  // Human-readable description
}

tool.approval.resolved

Tool approval was handled.

{
  sessionId: string;
  toolCallId: string;
  approved: boolean;
}

File Events

file.created

{
  path: string;
  type: "file" | "directory";
}

file.updated

{
  path: string;
  sessionId?: string;  // If changed by agent
}

file.deleted

{
  path: string;
}

Presence Events

presence.joined

User connected to project.

{
  userId: string;
  username: string;
  avatar?: string;
}

presence.left

User disconnected.

{
  userId: string;
}

presence.cursor

User cursor position (for collaborative features).

{
  userId: string;
  sessionId: string;
  position?: number;
}

REST Endpoints

REST API provides CRUD operations for sessions, messages, and agents.

Response Format

All REST endpoints return consistent JSON responses:

// Success response
{
  data: T;              // The requested resource(s)
  meta?: {              // Pagination/metadata (for lists)
    total: number;
    limit: number;
    offset: number;
    hasMore: boolean;
  }
}

// Error response
{
  error: {
    code: string;       // Machine-readable error code
    message: string;    // Human-readable message
    details?: unknown;  // Additional context
  }
}

Sessions API

Base path: /api/sessions

GET /api/sessions

List sessions with pagination and filters.

Query Parameters:

  • projectId (required): Project ID
  • status: Filter by status (active, archived, deleted)
  • agent: Filter by agent name
  • parentId: Filter by parent session (for sub-agents)
  • limit: Max results (default: 50, max: 100)
  • offset: Skip results (default: 0)

Response:

{
  data: Session[];
  meta: {
    total: number;
    limit: number;
    offset: number;
    hasMore: boolean;
  }
}

POST /api/sessions

Create a new session.

Request Body:

{
  projectId: string;
  title?: string;          // Auto-generated if omitted
  agent?: string;          // Default: "default"
  parentId?: string;       // For sub-agent sessions
  providerId?: string;     // LLM provider
  modelId?: string;        // Model ID
}

Response: 201 Created

{
  data: Session
}

GET /api/sessions/:id

Get session by ID.

Query Parameters:

  • projectId (required): Project ID

Response:

{
  data: Session
}

PUT /api/sessions/:id

Update session.

Request Body:

{
  projectId: string;
  title?: string;
  status?: "active" | "archived" | "deleted";
  agent?: string;
  providerId?: string;
  modelId?: string;
}

Response:

{
  data: Session
}

DELETE /api/sessions/:id

Soft delete a session (sets status to "deleted").

Query Parameters:

  • projectId (required): Project ID

Response:

{
  data: { deleted: true }
}

GET /api/sessions/:id/messages

List messages in a session.

Query Parameters:

  • projectId (required): Project ID
  • role: Filter by role (user, assistant, system)
  • limit: Max results (default: 50)
  • offset: Skip results (default: 0)

Response:

{
  data: Message[];
  meta: {
    total: number;
    limit: number;
    offset: number;
    hasMore: boolean;
  }
}

Messages API

Base path: /api/messages

POST /api/messages

Send a message and trigger agent orchestration.

Request Body:

{
  projectId: string;
  sessionId: string;
  content: string;           // User message text
  userId: string;            // User ID for permission context
  providerId?: string;       // Default: "anthropic"
  modelId?: string;          // Uses provider default
  agentName?: string;        // Override session's agent
  canExecuteCode?: boolean;  // Default: false
}

Response: 201 Created

{
  data: {
    message: Message;          // Assistant message
    parts: MessagePart[];      // All message parts
    usage: {
      inputTokens: number;
      outputTokens: number;
    };
    cost: number;              // Estimated cost in USD
    finishReason: "stop" | "tool-calls" | "length" | "error";
  }
}

GET /api/messages/:id

Get message with all parts.

Query Parameters:

  • projectId (required): Project ID

Response:

{
  data: Message & {
    parts: MessagePart[];
  }
}

GET /api/messages/:id/parts

List message parts.

Query Parameters:

  • projectId (required): Project ID

Response:

{
  data: MessagePart[];
  meta: {
    total: number;
  }
}

Agents API

Base path: /api/agents

GET /api/agents

List available agents (built-in + custom).

Query Parameters:

  • projectId: Project ID (required for custom agents)
  • mode: Filter by mode (primary, subagent)
  • includeHidden: Include hidden agents (default: false)
  • builtinOnly: Only built-in agents (default: false)
  • customOnly: Only custom agents (default: false)

Response:

{
  data: AgentConfig[];
  meta: {
    total: number;
    builtinCount: number;
    customCount: number;
  }
}

POST /api/agents

Create a custom agent.

Request Body:

{
  projectId: string;
  name: string;              // Lowercase, hyphens, starts with letter
  description?: string;
  mode?: "primary" | "subagent" | "all";  // Default: "subagent"
  hidden?: boolean;          // Default: false
  providerId?: string;
  modelId?: string;
  temperature?: number;      // 0-2
  topP?: number;             // 0-1
  maxSteps?: number;
  prompt?: string;           // System prompt
  tools?: string[];          // Tool names
  color?: string;
}

Response: 201 Created

{
  data: AgentConfig
}

Errors:

  • 400: Name is reserved or already exists
  • 400: Invalid name format

GET /api/agents/:name

Get agent config by name.

Query Parameters:

  • projectId: Project ID (for custom agents)

Response:

{
  data: AgentConfig
}

PUT /api/agents/:name

Update custom agent.

Request Body:

{
  projectId: string;
  name?: string;             // Rename agent
  description?: string;
  mode?: "primary" | "subagent" | "all";
  hidden?: boolean;
  providerId?: string;
  modelId?: string;
  temperature?: number;
  topP?: number;
  maxSteps?: number;
  prompt?: string;
  tools?: string[];
  color?: string;
}

Response:

{
  data: AgentConfig
}

Errors:

  • 403: Cannot update built-in agents
  • 400: New name is reserved

DELETE /api/agents/:name

Delete custom agent.

Query Parameters:

  • projectId (required): Project ID

Response:

{
  data: { deleted: true }
}

Errors:

  • 403: Cannot delete built-in agents

Projects API

Base path: /api/projects

GET /api/projects

List projects with pagination and filters.

Query Parameters:

  • ownerId: Filter by owner user ID
  • memberId: Filter by member user ID (includes projects where user is owner or member)
  • type: Filter by type (local, remote)
  • includeArchived: Include archived projects (default: false)
  • limit: Max results (default: 50, max: 100)
  • offset: Skip results (default: 0)

Response:

{
  data: Project[];
  meta: {
    total: number;
    limit: number;
    offset: number;
    hasMore: boolean;
  }
}

POST /api/projects

Create a new project.

Request Body:

{
  name: string;              // Required, 1-200 chars
  ownerId: string;           // Required, user ID
  description?: string;      // Max 2000 chars
  type?: "local" | "remote"; // Default: "local"
  path?: string;             // Local filesystem path
  gitRemote?: string;        // Git repository URL
  iconUrl?: string;          // Project icon URL
  color?: string;            // Hex color (#RRGGBB)
  settings?: Record<string, unknown>;
}

Response: 201 Created

{
  data: Project
}

GET /api/projects/:id

Get project by ID.

Response:

{
  data: Project
}

PUT /api/projects/:id

Update project.

Request Body:

{
  name?: string;
  description?: string | null;
  path?: string | null;
  gitRemote?: string | null;
  iconUrl?: string | null;
  color?: string | null;
  settings?: Record<string, unknown>;
}

Response:

{
  data: Project
}

DELETE /api/projects/:id

Archive a project (soft delete).

Response:

{
  data: { archived: true }
}

Project Members API

Base path: /api/projects/:id/members

GET /api/projects/:id/members

List project members.

Response:

{
  data: ProjectMember[];
  meta: {
    total: number;
  }
}

POST /api/projects/:id/members

Add a member to the project.

Request Body:

{
  userId: string;
  role: "admin" | "member" | "viewer";
  invitedBy?: string;
}

Response: 201 Created

{
  data: ProjectMember
}

Errors:

  • 409 CONFLICT: User is already a member

PUT /api/projects/:id/members/:userId

Update member role.

Request Body:

{
  role: "admin" | "member" | "viewer";
}

Response:

{
  data: ProjectMember
}

Errors:

  • 403 FORBIDDEN: Cannot change owner's role

DELETE /api/projects/:id/members/:userId

Remove a member from the project.

Response:

{
  data: { removed: true }
}

Errors:

  • 403 FORBIDDEN: Cannot remove project owner

Authentication API

Base path: /auth

POST /auth/magic-link

Request a magic link for passwordless login.

Request Body:

{
  email: string;
}

Response:

{
  success: true;
  message: string;
}

GET /auth/verify

Verify magic link token and create session.

Query Parameters:

  • token: Magic link token

Response: Redirects or returns session.


POST /auth/logout

Logout current session.

Response:

{
  success: true
}

GET /auth/me

Get current user info.

Response:

{
  user: User;
  session: AuthSession;
}

Provider Credentials API

Base path: /credentials

GET /credentials

List user's provider credentials.

Response:

{
  credentials: ProviderCredential[];
}

POST /credentials

Store a new provider credential.

Request Body:

{
  provider: "anthropic" | "openai" | "google";
  apiKey: string;
  name?: string;
  isDefault?: boolean;
}

Response:

{
  credential: ProviderCredential;
}

DELETE /credentials/:id

Delete a provider credential.

Response:

{
  deleted: true
}

Health API

Base path: /health

GET /health

Basic health check.

Response:

{
  status: "ok";
  timestamp: number;
}

GET /health/ready

Readiness check (includes database).

Response:

{
  status: "ok" | "error";
  timestamp: number;
  checks: {
    database: "ok" | "error";
  }
}

GET /health/live

Liveness check with uptime.

Response:

{
  status: "ok";
  uptime: number;
}

---

## Error Codes

| Code | Description |
|------|-------------|
| `AUTH_REQUIRED` | No authentication token provided |
| `AUTH_INVALID` | Token is invalid or expired |
| `FORBIDDEN` | User lacks permission for operation |
| `NOT_FOUND` | Requested resource doesn't exist |
| `VALIDATION_ERROR` | Invalid request parameters |
| `RATE_LIMITED` | Too many requests |
| `INTERNAL_ERROR` | Server error |
| `LLM_ERROR` | LLM provider error |
| `TOOL_ERROR` | Tool execution failed |
| `CANCELLED` | Operation was cancelled |

---

## Related Documents

- [Architecture](./01-architecture.md) - System design
- [Data Model](./02-data-model.md) - Entity definitions
- [Realtime Communication](../implementation-plan/05-realtime-communication.md) - Detailed protocol