Co-authored-by: Jeffrey Smith <jasafpro@gmail.com> Co-committed-by: Jeffrey Smith <jasafpro@gmail.com>
644 lines
17 KiB
YAML
644 lines
17 KiB
YAML
openapi: 3.0.3
|
|
info:
|
|
title: Chat Switchboard API
|
|
description: |
|
|
Multi-provider AI chat platform with team workspaces, workflow automation,
|
|
and extensibility via Starlark packages.
|
|
|
|
**Authentication:** Most endpoints require a Bearer JWT token obtained via
|
|
`POST /api/v1/auth/login`. Include it as `Authorization: Bearer <token>`.
|
|
|
|
**WebSocket:** Real-time events are delivered over WebSocket at `/ws`.
|
|
Obtain a ticket via `POST /api/v1/ws/ticket` and connect with `?ticket=<id>`.
|
|
version: "${VERSION}"
|
|
contact:
|
|
name: Chat Switchboard
|
|
|
|
servers:
|
|
- url: /
|
|
description: Current instance
|
|
|
|
tags:
|
|
- name: Auth
|
|
description: Authentication and session management
|
|
- name: Channels
|
|
description: Chat channel CRUD and messaging
|
|
- name: Completions
|
|
description: AI completion requests (streaming and non-streaming)
|
|
- name: Health
|
|
description: System health and readiness probes
|
|
- name: Admin
|
|
description: Platform administration (requires admin role)
|
|
- name: Usage
|
|
description: Token usage and cost tracking
|
|
- name: WebSocket
|
|
description: Real-time event delivery
|
|
|
|
paths:
|
|
# ── Auth ───────────────────────────────────
|
|
/api/v1/auth/register:
|
|
post:
|
|
tags: [Auth]
|
|
summary: Register a new user
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [username, password]
|
|
properties:
|
|
username:
|
|
type: string
|
|
minLength: 3
|
|
password:
|
|
type: string
|
|
minLength: 8
|
|
email:
|
|
type: string
|
|
format: email
|
|
display_name:
|
|
type: string
|
|
responses:
|
|
"201":
|
|
description: User created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/AuthResponse"
|
|
"400":
|
|
$ref: "#/components/responses/BadRequest"
|
|
"409":
|
|
description: Username already taken
|
|
|
|
/api/v1/auth/login:
|
|
post:
|
|
tags: [Auth]
|
|
summary: Authenticate and obtain JWT tokens
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [username, password]
|
|
properties:
|
|
username:
|
|
type: string
|
|
password:
|
|
type: string
|
|
responses:
|
|
"200":
|
|
description: Login successful
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/AuthResponse"
|
|
"401":
|
|
description: Invalid credentials
|
|
"429":
|
|
description: Rate limited
|
|
|
|
/api/v1/auth/refresh:
|
|
post:
|
|
tags: [Auth]
|
|
summary: Refresh an expired access token
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [refresh_token]
|
|
properties:
|
|
refresh_token:
|
|
type: string
|
|
responses:
|
|
"200":
|
|
description: New token pair
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/AuthResponse"
|
|
"401":
|
|
description: Invalid or expired refresh token
|
|
|
|
/api/v1/auth/logout:
|
|
post:
|
|
tags: [Auth]
|
|
summary: Invalidate the current session
|
|
security:
|
|
- bearerAuth: []
|
|
responses:
|
|
"200":
|
|
description: Logged out
|
|
|
|
# ── Channels ───────────────────────────────
|
|
/api/v1/channels:
|
|
get:
|
|
tags: [Channels]
|
|
summary: List channels accessible to the current user
|
|
security:
|
|
- bearerAuth: []
|
|
parameters:
|
|
- name: type
|
|
in: query
|
|
schema:
|
|
type: string
|
|
enum: [direct, group, workflow]
|
|
- name: page
|
|
in: query
|
|
schema:
|
|
type: integer
|
|
default: 1
|
|
- name: per_page
|
|
in: query
|
|
schema:
|
|
type: integer
|
|
default: 50
|
|
responses:
|
|
"200":
|
|
description: Channel list
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
channels:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/Channel"
|
|
total:
|
|
type: integer
|
|
post:
|
|
tags: [Channels]
|
|
summary: Create a new channel
|
|
security:
|
|
- bearerAuth: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
title:
|
|
type: string
|
|
type:
|
|
type: string
|
|
enum: [direct, group]
|
|
default: direct
|
|
system_prompt:
|
|
type: string
|
|
responses:
|
|
"201":
|
|
description: Channel created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Channel"
|
|
|
|
/api/v1/channels/{id}:
|
|
get:
|
|
tags: [Channels]
|
|
summary: Get channel details
|
|
security:
|
|
- bearerAuth: []
|
|
parameters:
|
|
- $ref: "#/components/parameters/ChannelID"
|
|
responses:
|
|
"200":
|
|
description: Channel details
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Channel"
|
|
"404":
|
|
$ref: "#/components/responses/NotFound"
|
|
put:
|
|
tags: [Channels]
|
|
summary: Update channel settings
|
|
security:
|
|
- bearerAuth: []
|
|
parameters:
|
|
- $ref: "#/components/parameters/ChannelID"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
title:
|
|
type: string
|
|
system_prompt:
|
|
type: string
|
|
ai_mode:
|
|
type: string
|
|
enum: [auto, off, mention_only]
|
|
responses:
|
|
"200":
|
|
description: Updated
|
|
delete:
|
|
tags: [Channels]
|
|
summary: Archive a channel
|
|
security:
|
|
- bearerAuth: []
|
|
parameters:
|
|
- $ref: "#/components/parameters/ChannelID"
|
|
responses:
|
|
"200":
|
|
description: Archived
|
|
|
|
# ── Completions ────────────────────────────
|
|
/api/v1/chat/completions:
|
|
post:
|
|
tags: [Completions]
|
|
summary: Send a message and get an AI completion
|
|
description: |
|
|
Streams SSE events by default. Set `stream: false` for a single JSON response.
|
|
Supports tool calling, @mentions, multi-model routing, and workflow integration.
|
|
security:
|
|
- bearerAuth: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [content, channel_id]
|
|
properties:
|
|
channel_id:
|
|
type: string
|
|
format: uuid
|
|
content:
|
|
type: string
|
|
model:
|
|
type: string
|
|
description: Override the default model
|
|
persona_id:
|
|
type: string
|
|
format: uuid
|
|
provider_config_id:
|
|
type: string
|
|
format: uuid
|
|
max_tokens:
|
|
type: integer
|
|
temperature:
|
|
type: number
|
|
minimum: 0
|
|
maximum: 2
|
|
top_p:
|
|
type: number
|
|
minimum: 0
|
|
maximum: 1
|
|
stream:
|
|
type: boolean
|
|
default: true
|
|
file_ids:
|
|
type: array
|
|
items:
|
|
type: string
|
|
format: uuid
|
|
disabled_tools:
|
|
type: array
|
|
items:
|
|
type: string
|
|
responses:
|
|
"200":
|
|
description: |
|
|
**Streaming:** SSE events (`data: {"content":"..."}`, `data: [DONE]`).
|
|
**Non-streaming:** OpenAI-compatible JSON response.
|
|
content:
|
|
text/event-stream:
|
|
schema:
|
|
type: string
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
model:
|
|
type: string
|
|
choices:
|
|
type: array
|
|
items:
|
|
type: object
|
|
properties:
|
|
message:
|
|
type: object
|
|
properties:
|
|
role:
|
|
type: string
|
|
content:
|
|
type: string
|
|
finish_reason:
|
|
type: string
|
|
usage:
|
|
type: object
|
|
properties:
|
|
prompt_tokens:
|
|
type: integer
|
|
completion_tokens:
|
|
type: integer
|
|
total_tokens:
|
|
type: integer
|
|
"400":
|
|
$ref: "#/components/responses/BadRequest"
|
|
"429":
|
|
description: Token budget exceeded
|
|
|
|
# ── Health ─────────────────────────────────
|
|
/health:
|
|
get:
|
|
tags: [Health]
|
|
summary: Basic health check
|
|
responses:
|
|
"200":
|
|
description: System status
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
status:
|
|
type: string
|
|
example: ok
|
|
version:
|
|
type: string
|
|
database:
|
|
type: boolean
|
|
schema_version:
|
|
type: integer
|
|
|
|
/healthz/live:
|
|
get:
|
|
tags: [Health]
|
|
summary: Kubernetes liveness probe
|
|
responses:
|
|
"200":
|
|
description: Process alive
|
|
|
|
/healthz/ready:
|
|
get:
|
|
tags: [Health]
|
|
summary: Kubernetes readiness probe
|
|
description: Returns 503 if the database is unreachable (2s timeout).
|
|
responses:
|
|
"200":
|
|
description: Ready to serve traffic
|
|
"503":
|
|
description: Database unavailable
|
|
|
|
/metrics:
|
|
get:
|
|
tags: [Health]
|
|
summary: Prometheus metrics endpoint
|
|
description: |
|
|
Returns all `switchboard_*` metrics in Prometheus text exposition format.
|
|
Includes HTTP request latency, WebSocket connections, completion tokens,
|
|
DB pool stats, and provider health gauges.
|
|
responses:
|
|
"200":
|
|
description: Prometheus text format
|
|
content:
|
|
text/plain:
|
|
schema:
|
|
type: string
|
|
|
|
# ── WebSocket ──────────────────────────────
|
|
/api/v1/ws/ticket:
|
|
post:
|
|
tags: [WebSocket]
|
|
summary: Obtain a WebSocket connection ticket
|
|
description: |
|
|
Returns a single-use ticket (30s TTL) for authenticating the WebSocket
|
|
upgrade at `/ws?ticket=<id>`. Cross-pod safe (v0.32.0).
|
|
security:
|
|
- bearerAuth: []
|
|
responses:
|
|
"200":
|
|
description: Ticket issued
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
ticket:
|
|
type: string
|
|
|
|
# ── Admin: Stats ───────────────────────────
|
|
/api/v1/admin/stats:
|
|
get:
|
|
tags: [Admin]
|
|
summary: Platform statistics
|
|
security:
|
|
- bearerAuth: []
|
|
responses:
|
|
"200":
|
|
description: Aggregate counts
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
users:
|
|
type: integer
|
|
channels:
|
|
type: integer
|
|
messages:
|
|
type: integer
|
|
|
|
# ── Admin: Provider Health ─────────────────
|
|
/api/v1/admin/providers/health:
|
|
get:
|
|
tags: [Admin]
|
|
summary: Current health status for all providers
|
|
security:
|
|
- bearerAuth: []
|
|
responses:
|
|
"200":
|
|
description: Provider health summaries
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
providers:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/ProviderHealth"
|
|
|
|
# ── Admin: Usage ───────────────────────────
|
|
/api/v1/admin/usage:
|
|
get:
|
|
tags: [Admin, Usage]
|
|
summary: Aggregated token usage
|
|
security:
|
|
- bearerAuth: []
|
|
parameters:
|
|
- name: since
|
|
in: query
|
|
schema:
|
|
type: string
|
|
format: date-time
|
|
- name: until
|
|
in: query
|
|
schema:
|
|
type: string
|
|
format: date-time
|
|
- name: period
|
|
in: query
|
|
schema:
|
|
type: string
|
|
enum: [7d, 30d, 90d]
|
|
- name: group_by
|
|
in: query
|
|
schema:
|
|
type: string
|
|
enum: [model, user, provider, day]
|
|
responses:
|
|
"200":
|
|
description: Usage data
|
|
|
|
# ── Admin: Dashboard ───────────────────────
|
|
/api/v1/admin/dashboard:
|
|
get:
|
|
tags: [Admin]
|
|
summary: Real-time observability dashboard data
|
|
description: |
|
|
Aggregates provider health, 24h usage, DB pool stats, WebSocket count,
|
|
recent errors, and uptime into a single response. Designed for the
|
|
built-in admin dashboard (v0.33.0).
|
|
security:
|
|
- bearerAuth: []
|
|
responses:
|
|
"200":
|
|
description: Dashboard snapshot
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
providers:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/ProviderHealth"
|
|
usage_24h:
|
|
type: object
|
|
db_pool:
|
|
type: object
|
|
properties:
|
|
open_connections:
|
|
type: integer
|
|
in_use:
|
|
type: integer
|
|
idle:
|
|
type: integer
|
|
ws_connections:
|
|
type: integer
|
|
uptime_seconds:
|
|
type: number
|
|
|
|
components:
|
|
securitySchemes:
|
|
bearerAuth:
|
|
type: http
|
|
scheme: bearer
|
|
bearerFormat: JWT
|
|
|
|
parameters:
|
|
ChannelID:
|
|
name: id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
|
|
responses:
|
|
BadRequest:
|
|
description: Invalid request
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
error:
|
|
type: string
|
|
NotFound:
|
|
description: Resource not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
error:
|
|
type: string
|
|
|
|
schemas:
|
|
AuthResponse:
|
|
type: object
|
|
properties:
|
|
token:
|
|
type: string
|
|
refresh_token:
|
|
type: string
|
|
user:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
username:
|
|
type: string
|
|
role:
|
|
type: string
|
|
enum: [admin, user]
|
|
|
|
Channel:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
title:
|
|
type: string
|
|
type:
|
|
type: string
|
|
enum: [direct, group, workflow]
|
|
user_id:
|
|
type: string
|
|
format: uuid
|
|
team_id:
|
|
type: string
|
|
format: uuid
|
|
ai_mode:
|
|
type: string
|
|
enum: [auto, off, mention_only]
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
|
|
ProviderHealth:
|
|
type: object
|
|
properties:
|
|
provider_config_id:
|
|
type: string
|
|
format: uuid
|
|
status:
|
|
type: string
|
|
enum: [healthy, degraded, down, unknown]
|
|
request_count:
|
|
type: integer
|
|
error_count:
|
|
type: integer
|
|
error_rate:
|
|
type: number
|
|
avg_latency_ms:
|
|
type: number
|
|
max_latency_ms:
|
|
type: integer
|