Skip to content

WebSocket API Reference ​

The WebSocket endpoint enables real-time, bidirectional communication for streaming text, tool execution, session management, and provider switching -- all over a single persistent connection.

Connection ​

Endpoint ​

ws://localhost:7337/ws?token=sk_seepient_...

Authentication is performed via the token query parameter. The server validates the API key during the HTTP upgrade. If the key is invalid, the upgrade is rejected with 401 Unauthorized.

Upgrade lifecycle ​

  1. Client initiates a WebSocket upgrade to /ws?token=sk_seepient_...
  2. Server validates the API key
  3. On success, the connection is established and ready for messages
  4. On failure, the socket is destroyed with a 401 response

Connection close on auth failure

If authentication fails after upgrade (should not happen in normal flow), the server sends an error message with code UNAUTHORIZED and closes the connection with code 4001.

The server enforces a maximum frame payload of 1048576 bytes (1 MiB) on all incoming WebSocket messages. Messages exceeding this limit will result in immediate connection closure with status 1009.


Client-to-Server messages ​

All client messages are JSON. Every message must include a type field.

chat ​

Send a user message and receive a streaming response.

json
{
  "type": "chat",
  "id": "client-msg-001",
  "message": "Explain closures in JavaScript",
  "options": {
    "model": "claude-sonnet-4-6-20260320",
    "provider": "anthropic",
    "tools": ["execute_shell_command"],
    "maxSteps": 10,
    "skills": ["code-review"]
  },
  "sessionId": "550e8400-e29b-41d4-a716-446655440000"
}
FieldTypeRequiredDescription
idstringYesClient-generated message ID for correlation
messagestringYesThe user prompt
options.modelstringNoModel ID (overrides connection default)
options.providerstringNoProvider name (overrides connection default)
options.toolsstring[]NoTool names or group names to enable
options.maxStepsnumberNoMax agent loop iterations (default: 10)
options.skillsstring[]NoSkill names to activate
sessionIdstringNoResume an existing session

abort ​

Cancel the current in-flight chat request. The AbortSignal is passed through to the provider SDK, so the underlying HTTP request to the LLM is cancelled at the network level.

json
{
  "type": "abort",
  "reason": "User cancelled"
}
FieldTypeRequiredDescription
reasonstringNoOptional reason for abort

resume ​

Resume an existing session by ID. The server replies with session_resumed containing the message history.

json
{
  "type": "resume",
  "sessionId": "550e8400-e29b-41d4-a716-446655440000",
  "lastMessageId": "msg_005"
}
FieldTypeRequiredDescription
sessionIdstringYesSession to resume
lastMessageIdstringNoLast message the client has seen

reconnect ​

Reconnect after a dropped connection. The server replies with replay containing messages after lastSeenId.

json
{
  "type": "reconnect",
  "sessionId": "550e8400-e29b-41d4-a716-446655440000",
  "lastSeenId": "msg_005"
}
FieldTypeRequiredDescription
sessionIdstringYesSession to reconnect to
lastSeenIdstringNoLast message ID the client processed

switch_provider ​

Change the active provider and/or model for subsequent messages on this connection.

json
{
  "type": "switch_provider",
  "provider": "openai",
  "model": "gpt-5.4"
}
FieldTypeRequiredDescription
providerstringYesNew provider name
modelstringNoNew default model

list_models ​

Request the list of models available from your configured inference providers. Seepient Agent does not host models — it forwards requests to your provider APIs. The server replies with models_list.

json
{
  "type": "list_models"
}

list_skills ​

Request the list of available skills. The server replies with skills_list.

json
{
  "type": "list_skills"
}

ping ​

Heartbeat / latency measurement. The server replies with pong.

json
{
  "type": "ping",
  "clientTime": "2026-04-08T12:00:00.000Z"
}
FieldTypeRequiredDescription
clientTimestringYesISO 8601 client timestamp

Server-to-Client messages ​

ack ​

Confirms receipt of a client message. Sent immediately after a chat message is received.

json
{
  "type": "ack",
  "clientMsgId": "client-msg-001",
  "serverMsgId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "timestamp": "2026-04-08T12:00:01.000Z"
}
FieldTypeDescription
clientMsgIdstringMatches the id from the client's chat message
serverMsgIdstringServer-generated ID for this generation
timestampstringISO 8601 timestamp

text ​

Streaming text delta. Multiple text messages are sent as the model generates tokens.

json
{
  "type": "text",
  "delta": "A closure is ",
  "serverMsgId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
FieldTypeDescription
deltastringIncremental text chunk
serverMsgIdstringCorrelates with the ack

tool_call ​

Indicates the agent is invoking a tool.

json
{
  "type": "tool_call",
  "callId": "call_abc123",
  "name": "execute_shell_command",
  "args": { "command": "ls -la" }
}
FieldTypeDescription
callIdstringUnique tool call identifier
namestringTool name
argsobjectArguments passed to the tool

tool_progress ​

Progress update during a long-running tool execution.

json
{
  "type": "tool_progress",
  "callId": "call_abc123",
  "percentage": 50,
  "output": "Processing file 5 of 10..."
}
FieldTypeDescription
callIdstringMatches the tool_call
percentagenumberProgress percentage (0-100)
outputstringOptional partial output

tool_result ​

Final result of a tool invocation.

json
{
  "type": "tool_result",
  "callId": "call_abc123",
  "output": "README.md\npackage.json\nsrc/",
  "success": true
}
FieldTypeDescription
callIdstringMatches the tool_call
outputstringTool execution result
successbooleanWhether the tool succeeded

progress ​

Agent loop progress update.

json
{
  "type": "progress",
  "step": 2,
  "totalSteps": 5,
  "percentage": 40,
  "activity": "Executing tool: execute_shell_command"
}
FieldTypeDescription
stepnumberCurrent step number
totalStepsnumberEstimated total steps (may be 0 if unknown)
percentagenumberProgress percentage
activitystringDescription of current activity

done ​

Signals the end of a generation. Always sent as the final message in a chat flow.

json
{
  "type": "done",
  "serverMsgId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "usage": {
    "promptTokens": 245,
    "completionTokens": 187,
    "totalTokens": 432,
    "cost": 0.0041
  },
  "finishReason": "stop"
}
FieldTypeDescription
serverMsgIdstringCorrelates with the ack
usageobjectToken usage and cost
usage.promptTokensnumberTokens in the prompt
usage.completionTokensnumberTokens in the completion
usage.totalTokensnumberTotal tokens consumed
usage.costnumberEstimated cost in USD
finishReasonstring"stop", "max_steps", "error", or "aborted"

error ​

An error occurred. May be sent at any time during a chat flow or connection lifecycle. Error frames carry generic wire text; operators diagnose details from the server's JSON-line log. Codes a client can receive:

CodeRetryableWhen
FORBIDDENNoThe key lacks the scope the message requires (agent:run for chat, agent:read for resume/reconnect)
RATE_LIMITEDYesThe key exceeded the shared per-key rate limit (WS messages consume the same budget as REST)
REQUEST_IN_FLIGHTYesAnother chat turn is running on this connection or session
SESSION_ERRORNoSession lookup/persistence failed mid-turn
SESSION_NOT_FOUNDNoSession expired, foreign-owned, or missing
SESSION_LIMITNoPer-key concurrent-session cap reached
STREAM_ERRORNoThe model stream failed
PROVIDER_ERRORYesLLM provider error (rate limit, auth, outage)
INTERNAL_ERRORNoUnexpected server error

error ​

An error occurred. May be sent at any time during a chat flow or connection lifecycle.

json
{
  "type": "error",
  "code": "PROVIDER_ERROR",
  "retryable": true,
  "message": "Stream failed"
}
FieldTypeDescription
codestringError code (see table below)
retryablebooleanWhether the client should retry
messagestringHuman-readable error description
providerstringProvider that caused the error (optional)
toolstringTool that caused the error (optional)

pong ​

Response to a client ping.

json
{
  "type": "pong",
  "serverTime": "2026-04-08T12:00:01.500Z"
}
FieldTypeDescription
serverTimestringISO 8601 server timestamp

models_list ​

Response to list_models.

json
{
  "type": "models_list",
  "models": {
    "openai": ["gpt-5.4", "gpt-5.4-pro", "gpt-5.4-mini", "gpt-5.4-nano", "gpt-5.3-instant", "gpt-5.3-codex", "o3", "o3-mini"],
    "anthropic": ["claude-sonnet-4-6-20260320", "claude-opus-4-6-20260320", "claude-haiku-4-5-20251001"],
    "glm": ["opus", "sonnet", "haiku"],
    "openai-compatible": ["(user-configured)"]
  }
}

skills_list ​

Response to list_skills.

json
{
  "type": "skills_list",
  "skills": [
    { "name": "code-review", "description": "...", "tags": ["code"] }
  ]
}

session_resumed ​

Response to resume. Contains the session's message history.

json
{
  "type": "session_resumed",
  "sessionId": "550e8400-e29b-41d4-a716-446655440000",
  "messages": [
    { "id": "msg_001", "role": "user", "content": "Hello", "timestamp": 1712505600000 },
    { "id": "msg_002", "role": "assistant", "content": "Hi there!", "timestamp": 1712505601000 }
  ]
}

replay ​

Response to reconnect. Contains messages the client may have missed.

json
{
  "type": "replay",
  "messages": [
    { "id": "msg_006", "role": "assistant", "content": "...", "timestamp": 1712505610000 }
  ],
  "currentStatus": "ready"
}

Error codes ​

CodeRetryableDescription
UNAUTHORIZEDNoInvalid or missing API key
INVALID_MESSAGENoMalformed JSON in client message
UNKNOWN_MESSAGE_TYPENoUnrecognized type field
PROVIDER_ERRORYesLLM provider returned an error
STREAM_ERRORNoInternal streaming failure
SESSION_NOT_FOUNDNoSession expired or does not exist
ABORTEDNoRequest aborted by client

Client examples ​

JavaScript / TypeScript (browser) ​

typescript
const ws = new WebSocket("ws://localhost:7337/ws?token=sk_seepient_...");

ws.onopen = () => {
  // Start a chat
  ws.send(JSON.stringify({
    type: "chat",
    id: crypto.randomUUID(),
    message: "Explain closures in JavaScript",
    options: {
      provider: "anthropic",
      tools: [],
      maxSteps: 5
    }
  }));
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);

  switch (msg.type) {
    case "ack":
      console.log("Server acknowledged:", msg.serverMsgId);
      break;
    case "text":
      process.stdout.write(msg.delta);
      break;
    case "tool_call":
      console.log(`Tool call: ${msg.name}`, msg.args);
      break;
    case "tool_result":
      console.log(`Tool result (${msg.success}):`, msg.output);
      break;
    case "done":
      console.log("\nDone. Tokens:", msg.usage.totalTokens);
      break;
    case "error":
      console.error("Error:", msg.code, msg.message);
      break;
  }
};

// Heartbeat every 30 seconds
setInterval(() => {
  if (ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify({ type: "ping", clientTime: new Date().toISOString() }));
  }
}, 30000);

Python (websockets) ​

python
import asyncio
import json
import uuid
from websockets import connect

async def chat():
    uri = "ws://localhost:7337/ws?token=sk_seepient_..."

    async with connect(uri) as ws:
        # Send a chat message
        await ws.send(json.dumps({
            "type": "chat",
            "id": str(uuid.uuid4()),
            "message": "Write a Python hello world",
            "options": {
                "provider": "openai",
                "model": "gpt-5.4",
                "maxSteps": 5
            }
        }))

        # Receive streaming response
        async for raw in ws:
            msg = json.loads(raw)

            if msg["type"] == "text":
                print(msg["delta"], end="", flush=True)
            elif msg["type"] == "tool_call":
                print(f"\n[Tool] {msg['name']}({msg['args']})")
            elif msg["type"] == "tool_result":
                print(f"[Result] {msg['output'][:100]}")
            elif msg["type"] == "done":
                print(f"\n--- Done. Cost: ${msg['usage']['cost']:.4f}")
                break
            elif msg["type"] == "error":
                print(f"Error: {msg['code']} - {msg['message']}")
                break

asyncio.run(chat())

Message flow diagram ​

A typical chat exchange follows this sequence:

Client                              Server
  │                                    │
  │──── chat { id, message } ─────────►│
  │◄─── ack { clientMsgId } ──────────│
  │                                    │
  │◄─── text { delta: "A " } ─────────│
  │◄─── text { delta: "closure " } ───│
  │◄─── text { delta: "is..." } ──────│
  │                                    │
  │◄─── tool_call { name, args } ─────│
  │◄─── tool_result { output } ───────│
  │                                    │
  │◄─── text { delta: "Based on" } ───│
  │◄─── text { delta: "the files" } ──│
  │                                    │
  │◄─── done { usage, finishReason } ─│
  │                                    │

Streaming is real-time

Text deltas arrive as soon as the LLM generates tokens. Tool calls and results are sent inline. The done message is always the last message in a generation cycle.

Released under the Business Source License 1.1.