Overview
The Playground API provides endpoints for managing chat sessions, sending messages, and retrieving observability data. All endpoints (except health) require JWT authentication.
Base URL
# Development
http://localhost:8002
# Test Infrastructure
http://localhost:9002
# Production
https://playground.yourdomain.com
Authentication
All endpoints except /api/playground/health require JWT authentication:
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...
Health Endpoints
GET /api/playground/health
Health check endpoint (no authentication required).
Response:
{
"status": "healthy",
"version": "1.0.0",
"dependencies": {
"redis": "healthy",
"mcp_server": "healthy"
}
}
Status Codes:
One or more dependencies are unhealthy {
"status": "unhealthy",
"dependencies": {
"redis": "unhealthy",
"mcp_server": "healthy"
}
}
Session Endpoints
POST /api/playground/sessions
Create a new chat session.
Display name for the session (optional)
Custom metadata to attach to the session (optional)
Request:
curl -X POST http://localhost:9002/api/playground/sessions \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "My Chat Session",
"metadata": {
"project": "demo"
}
}'
Response:
{
"session_id": "sess_abc123def456",
"name": "My Chat Session",
"user_id": "alice",
"created_at": "2025-12-06T10:00:00Z",
"updated_at": "2025-12-06T10:00:00Z",
"metadata": {
"project": "demo"
}
}
Status Codes:
Session created successfully
Invalid or missing authentication token
GET /api/playground/sessions
List all sessions for the authenticated user.
Maximum number of sessions to return
Number of sessions to skip for pagination
Request:
curl http://localhost:9002/api/playground/sessions?limit=10 \
-H "Authorization: Bearer $TOKEN"
Response:
[
{
"session_id": "sess_abc123def456",
"name": "My Chat Session",
"user_id": "alice",
"created_at": "2025-12-06T10:00:00Z",
"updated_at": "2025-12-06T10:30:00Z",
"message_count": 5
},
{
"session_id": "sess_xyz789",
"name": "Test Session",
"user_id": "alice",
"created_at": "2025-12-05T09:00:00Z",
"updated_at": "2025-12-05T09:15:00Z",
"message_count": 3
}
]
GET /api/playground/sessions/
Get details for a specific session including message history.
Path Parameters:
Request:
curl http://localhost:9002/api/playground/sessions/sess_abc123def456 \
-H "Authorization: Bearer $TOKEN"
Response:
{
"session_id": "sess_abc123def456",
"name": "My Chat Session",
"user_id": "alice",
"created_at": "2025-12-06T10:00:00Z",
"updated_at": "2025-12-06T10:30:00Z",
"messages": [
{
"id": "msg_001",
"role": "user",
"content": "Hello, what can you help me with?",
"timestamp": "2025-12-06T10:00:05Z"
},
{
"id": "msg_002",
"role": "assistant",
"content": "I can help you with a variety of tasks...",
"timestamp": "2025-12-06T10:00:08Z",
"tool_calls": []
}
],
"metadata": {
"model": "gemini-2.5-flash",
"total_tokens": 150
}
}
Status Codes:
Session retrieved successfully
Session does not exist or user lacks access
DELETE /api/playground/sessions/
Delete a session and all its messages.
Request:
curl -X DELETE http://localhost:9002/api/playground/sessions/sess_abc123def456 \
-H "Authorization: Bearer $TOKEN"
Response:
{
"message": "Session deleted successfully"
}
Status Codes:
Session deleted successfully
Chat Endpoints
POST /api/playground/chat
Send a message and receive a response.
The session ID to send the message to
The user’s message content
Enable streaming response (SSE)
Request (Non-Streaming):
curl -X POST http://localhost:9002/api/playground/chat \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"session_id": "sess_abc123def456",
"message": "What is 2 + 2?"
}'
Response:
{
"message_id": "msg_003",
"role": "assistant",
"content": "2 + 2 equals 4.",
"timestamp": "2025-12-06T10:30:05Z",
"tool_calls": [],
"usage": {
"prompt_tokens": 25,
"completion_tokens": 10,
"total_tokens": 35
}
}
Request (Streaming):
curl -X POST http://localhost:9002/api/playground/chat \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-d '{
"session_id": "sess_abc123def456",
"message": "Explain machine learning",
"stream": true
}'
Streaming Response (SSE):
event: token
data: {"content": "Machine"}
event: token
data: {"content": " learning"}
event: token
data: {"content": " is"}
event: tool_call
data: {"name": "search", "args": {"query": "machine learning definition"}}
event: tool_result
data: {"content": "ML is a subset of AI..."}
event: end
data: {"usage": {"total_tokens": 150}}
Status Codes:
Message processed successfully
Observability Endpoints
GET /api/playground/observability/traces
Get OpenTelemetry traces for a session.
Filter traces by session ID
Maximum number of traces to return
Request:
curl "http://localhost:9002/api/playground/observability/traces?session_id=sess_abc123" \
-H "Authorization: Bearer $TOKEN"
Response:
[
{
"trace_id": "abc123def456789",
"span_id": "span_001",
"operation": "chat.process",
"duration_ms": 1250,
"status": "OK",
"attributes": {
"model": "gemini-2.5-flash",
"tokens": 150
},
"timestamp": "2025-12-06T10:30:00Z"
}
]
GET /api/playground/observability/logs
Get structured logs for a session.
Filter logs by session ID
Minimum log level (DEBUG, INFO, WARN, ERROR)
Request:
curl "http://localhost:9002/api/playground/observability/logs?session_id=sess_abc123&level=INFO" \
-H "Authorization: Bearer $TOKEN"
Response:
[
{
"timestamp": "2025-12-06T10:30:00Z",
"level": "INFO",
"message": "Processing chat message",
"attributes": {
"session_id": "sess_abc123",
"message_length": 25
}
}
]
GET /api/playground/observability/metrics
Get metrics for a session.
Request:
curl "http://localhost:9002/api/playground/observability/metrics?session_id=sess_abc123" \
-H "Authorization: Bearer $TOKEN"
Response:
{
"session_id": "sess_abc123",
"message_count": 10,
"total_tokens": 1500,
"average_latency_ms": 850,
"error_count": 0,
"tool_call_count": 3
}
WebSocket API
WS /ws/playground/
Real-time bidirectional chat using WebSocket.
Connection:
const ws = new WebSocket('ws://localhost:9002/ws/playground/sess_abc123');
ws.onopen = () => {
// Authenticate
ws.send(JSON.stringify({
type: 'auth',
token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...'
}));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Received:', data);
};
Message Types:
Client Messages
Server Messages
// Authentication
{ "type": "auth", "token": "eyJ..." }
// Send chat message
{ "type": "chat", "message": "Hello!" }
// Cancel current response
{ "type": "cancel" }
// Ping (keepalive)
{ "type": "ping" }
// Authentication result
{ "type": "auth_success", "user_id": "alice" }
{ "type": "auth_error", "message": "Invalid token" }
// Streaming tokens
{ "type": "token", "content": "Hello" }
// Tool calls
{ "type": "tool_call", "name": "search", "args": {...} }
{ "type": "tool_result", "content": "..." }
// Completion
{ "type": "end", "usage": {"total_tokens": 100} }
// Errors
{ "type": "error", "message": "Processing failed" }
// Pong (keepalive response)
{ "type": "pong" }
Example Session:
const ws = new WebSocket('ws://localhost:9002/ws/playground/sess_abc123');
ws.onopen = () => {
// Step 1: Authenticate
ws.send(JSON.stringify({ type: 'auth', token: TOKEN }));
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
switch (msg.type) {
case 'auth_success':
// Step 2: Send chat message
ws.send(JSON.stringify({ type: 'chat', message: 'Hello!' }));
break;
case 'token':
// Step 3: Handle streaming tokens
process.stdout.write(msg.content);
break;
case 'tool_call':
console.log(`\nTool: ${msg.name}(${JSON.stringify(msg.args)})`);
break;
case 'end':
console.log(`\nTotal tokens: ${msg.usage.total_tokens}`);
break;
case 'error':
console.error('Error:', msg.message);
break;
}
};
Error Responses
All endpoints may return the following errors:
401 Unauthorized
{
"error": "unauthorized",
"message": "Invalid or expired token",
"code": "AUTH_INVALID_TOKEN"
}
403 Forbidden
{
"error": "forbidden",
"message": "Access denied to this resource",
"code": "AUTH_ACCESS_DENIED"
}
404 Not Found
{
"error": "not_found",
"message": "Session not found",
"code": "SESSION_NOT_FOUND"
}
429 Too Many Requests
{
"error": "rate_limit_exceeded",
"message": "Too many requests. Please try again later.",
"retry_after": 60,
"code": "RATE_LIMIT"
}
500 Internal Server Error
{
"error": "internal_error",
"message": "An unexpected error occurred",
"trace_id": "abc123...",
"code": "INTERNAL_ERROR"
}
SDK Examples
Python
import httpx
class PlaygroundClient:
def __init__(self, base_url: str, token: str):
self.client = httpx.AsyncClient(
base_url=base_url,
headers={"Authorization": f"Bearer {token}"}
)
async def create_session(self, name: str = None) -> dict:
response = await self.client.post(
"/api/playground/sessions",
json={"name": name}
)
return response.json()
async def chat(self, session_id: str, message: str) -> dict:
response = await self.client.post(
"/api/playground/chat",
json={"session_id": session_id, "message": message}
)
return response.json()
async def delete_session(self, session_id: str) -> None:
await self.client.delete(f"/api/playground/sessions/{session_id}")
# Usage
client = PlaygroundClient("http://localhost:9002", token)
session = await client.create_session("My Session")
response = await client.chat(session["session_id"], "Hello!")
print(response["content"])
JavaScript/TypeScript
class PlaygroundClient {
private baseUrl: string;
private token: string;
constructor(baseUrl: string, token: string) {
this.baseUrl = baseUrl;
this.token = token;
}
async createSession(name?: string): Promise<Session> {
const response = await fetch(`${this.baseUrl}/api/playground/sessions`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ name })
});
return response.json();
}
async *streamChat(sessionId: string, message: string): AsyncGenerator<ChatEvent> {
const response = await fetch(`${this.baseUrl}/api/playground/chat`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.token}`,
'Content-Type': 'application/json',
'Accept': 'text/event-stream'
},
body: JSON.stringify({ session_id: sessionId, message, stream: true })
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
for (const line of chunk.split('\n')) {
if (line.startsWith('data: ')) {
yield JSON.parse(line.slice(6));
}
}
}
}
}
// Usage
const client = new PlaygroundClient('http://localhost:9002', token);
const session = await client.createSession('My Session');
for await (const event of client.streamChat(session.session_id, 'Hello!')) {
if (event.type === 'token') {
process.stdout.write(event.content);
}
}
Full API access! Use these endpoints to build custom integrations with the Interactive Playground.