| # Stack 2.9 API Documentation |
|
|
| Complete API reference for integrating Stack 2.9 into your applications. |
|
|
| ## Table of Contents |
|
|
| - [Overview](#overview) |
| - [Authentication](#authentication) |
| - [Rate Limits](#rate-limits) |
| - [REST Endpoints](#rest-endpoints) |
| - [WebSocket Streaming](#websocket-streaming) |
| - [Request/Response Formats](#requestresponse-formats) |
| - [Error Handling](#error-handling) |
| - [SDKs and Examples](#sdks-and-examples) |
|
|
| --- |
|
|
| ## Overview |
|
|
| Stack 2.9 provides an OpenAI-compatible API for seamless integration with existing tools and workflows. |
|
|
| ### Base URL |
|
|
| ``` |
| Production: https://api.stack2.9.openclaw.org/v1 |
| Local: http://localhost:3000/v1 |
| ``` |
|
|
| ### API Versioning |
|
|
| The current API version is `v1`. Version information is included in all responses. |
|
|
| ```json |
| { |
| "api_version": "1.0", |
| "deprecation_date": null |
| } |
| ``` |
|
|
| --- |
|
|
| ## Authentication |
|
|
| ### API Key Authentication |
|
|
| Include your API key in the `Authorization` header: |
|
|
| ```bash |
| curl -H "Authorization: Bearer YOUR_API_KEY" \ |
| -H "Content-Type: application/json" \ |
| https://api.stack2.9.openclaw.org/v1/chat/completions |
| ``` |
|
|
| ### Obtaining an API Key |
|
|
| 1. **Self-hosted:** Set `API_KEY` environment variable |
| 2. **Cloud:** Sign up at [stack2.9.openclaw.org](https://stack2.9.openclaw.org) |
|
|
| ### Authentication Errors |
|
|
| | Status | Error Type | Description | |
| |--------|------------|-------------| |
| | 401 | `invalid_api_key` | API key is missing or invalid | |
| | 403 | `account_disabled` | Account has been disabled | |
| | 429 | `rate_limit_exceeded` | Too many requests | |
|
|
| --- |
|
|
| ## Rate Limits |
|
|
| ### Tier Limits |
|
|
| | Tier | Requests/min | Tokens/day | Concurrent | WebSocket | |
| |------|-------------|------------|------------|-----------| |
| | **Free** | 100 | 100,000 | 5 | ✅ | |
| | **Pro** | 1,000 | 10,000,000 | 20 | ✅ | |
| | **Enterprise** | Custom | Custom | Custom | ✅ | |
|
|
| ### Rate Limit Headers |
|
|
| Every response includes rate limit information: |
|
|
| ``` |
| X-RateLimit-Limit: 100 |
| X-RateLimit-Remaining: 95 |
| X-RateLimit-Reset: 1640995200 |
| X-RateLimit-Used: 5 |
| ``` |
|
|
| ### Handling Rate Limits |
|
|
| ```python |
| import time |
| import openai |
| |
| client = openai.OpenAI(api_key="your-api-key") |
| |
| for i in range(100): |
| try: |
| response = client.chat.completions.create( |
| model="qwen/qwen2.5-coder-32b", |
| messages=[{"role": "user", "content": "Hello"}] |
| ) |
| except openai.RateLimitError: |
| time.sleep(60) # Wait 1 minute |
| continue |
| ``` |
|
|
| --- |
|
|
| ## REST Endpoints |
|
|
| ### Chat Completions |
|
|
| **Endpoint:** `POST /chat/completions` |
|
|
| Generate chat completions with optional streaming. |
|
|
| #### Request Body |
|
|
| ```json |
| { |
| "model": "qwen/qwen2.5-coder-32b", |
| "messages": [ |
| { |
| "role": "system", |
| "content": "You are a helpful coding assistant." |
| }, |
| { |
| "role": "user", |
| "content": "Write a Python function to calculate Fibonacci numbers." |
| } |
| ], |
| "temperature": 0.7, |
| "max_tokens": 1000, |
| "top_p": 1.0, |
| "frequency_penalty": 0.0, |
| "presence_penalty": 0.0, |
| "stream": false, |
| "stop": null, |
| "tools": [], |
| "tool_choice": "auto", |
| "user": "user-identifier" |
| } |
| ``` |
|
|
| #### Parameters |
|
|
| | Parameter | Type | Required | Default | Description | |
| |-----------|------|----------|---------|-------------| |
| | `model` | string | ✅ | - | Model identifier | |
| | `messages` | array | ✅ | - | Conversation messages | |
| | `temperature` | number | ❌ | 0.7 | Sampling temperature (0-2) | |
| | `max_tokens` | integer | ❌ | 1000 | Maximum tokens to generate | |
| | `top_p` | number | ❌ | 1.0 | Nucleus sampling | |
| | `frequency_penalty` | number | ❌ | 0.0 | Frequency penalty (-2 to 2) | |
| | `presence_penalty` | number | ❌ | 0.0 | Presence penalty (-2 to 2) | |
| | `stream` | boolean | ❌ | false | Enable streaming | |
| | `stop` | string/array | ❌ | null | Stop sequences | |
| | `tools` | array | ❌ | [] | Available tools | |
| | `tool_choice` | string | ❌ | "auto" | Tool selection strategy | |
| | `user` | string | ❌ | - | User identifier | |
|
|
| #### Response (Non-Streaming) |
|
|
| ```json |
| { |
| "id": "chatcmpl-1234567890", |
| "object": "chat.completion", |
| "created": 1234567890, |
| "model": "qwen/qwen2.5-coder-32b", |
| "choices": [ |
| { |
| "index": 0, |
| "message": { |
| "role": "assistant", |
| "content": "def fibonacci(n):\n if n <= 1:\n return n\n return fibonacci(n-1) + fibonacci(n-2)" |
| }, |
| "finish_reason": "stop" |
| } |
| ], |
| "usage": { |
| "prompt_tokens": 25, |
| "completion_tokens": 35, |
| "total_tokens": 60 |
| }, |
| "system_fingerprint": "fp_1234567890" |
| } |
| ``` |
|
|
| #### Streaming Response Format |
|
|
| ```bash |
| curl -X POST http://localhost:3000/v1/chat/completions \ |
| -H "Authorization: Bearer YOUR_API_KEY" \ |
| -H "Content-Type: application/json" \ |
| -d '{"model": "qwen/qwen2.5-coder-32b", "messages": [{"role": "user", "content": "Hello"}], "stream": true}' |
| ``` |
|
|
| ```json |
| {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1234567890,"model":"qwen/qwen2.5-coder-32b","choices":[{"index":0,"delta":{"content":"Hello"},"finish_reason":null}]} |
| {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1234567890,"model":"qwen/qwen2.5-coder-32b","choices":[{"index":0,"delta":{"content":"!"},"finish_reason":null}]} |
| {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1234567890,"model":"qwen/qwen2.5-coder-32b","choices":[{"index":0,"delta":{"content":" How"},"finish_reason":null}]} |
| {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1234567890,"model":"qwen/qwen2.5-coder-32b","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]} |
| ``` |
|
|
| --- |
|
|
| ### Models List |
|
|
| **Endpoint:** `GET /models` |
|
|
| List available models. |
|
|
| #### Response |
|
|
| ```json |
| { |
| "object": "list", |
| "data": [ |
| { |
| "id": "qwen/qwen2.5-coder-32b", |
| "object": "model", |
| "created": 1234567890, |
| "owned_by": "openclaw", |
| "permission": [], |
| "root": "qwen/qwen2.5-coder-32b", |
| "parent": null, |
| "context_window": 131072, |
| "Capabilities": { |
| "streaming": true, |
| "tools": true, |
| "voice": true |
| } |
| }, |
| { |
| "id": "qwen/qwen2.5-coder-14b", |
| "object": "model", |
| "created": 1234567890, |
| "owned_by": "openclaw", |
| "permission": [], |
| "root": "qwen/qwen2.5-coder-14b", |
| "parent": null, |
| "context_window": 16384, |
| "capabilities": { |
| "streaming": true, |
| "tools": true, |
| "voice": false |
| } |
| } |
| ] |
| } |
| ``` |
|
|
| ### Get Model |
|
|
| **Endpoint:** `GET /models/{model_id}` |
|
|
| Get details about a specific model. |
|
|
| ```bash |
| curl http://localhost:3000/v1/models/qwen/qwen2.5-coder-32b |
| ``` |
|
|
| ```json |
| { |
| "id": "qwen/qwen2.5-coder-32b", |
| "object": "model", |
| "created": 1234567890, |
| "owned_by": "openclaw", |
| "context_window": 131072, |
| "capabilities": { |
| "streaming": true, |
| "tools": true, |
| "voice": true |
| } |
| } |
| ``` |
|
|
| --- |
|
|
| ## WebSocket Streaming |
|
|
| ### Connection |
|
|
| ```javascript |
| const ws = new WebSocket('wss://api.stack2.9.openclaw.org/v1/ws/chat'); |
| |
| ws.onopen = () => { |
| ws.send(JSON.stringify({ |
| type: 'start', |
| model: 'qwen/qwen2.5-coder-32b', |
| messages: [{role: 'user', content: 'Hello'}], |
| temperature: 0.7 |
| })); |
| }; |
| |
| ws.onmessage = (event) => { |
| const data = JSON.parse(event.data); |
| console.log(data.content); // Streamed content |
| }; |
| |
| ws.onerror = (error) => { |
| console.error('WebSocket error:', error); |
| }; |
| |
| ws.onclose = () => { |
| console.log('Connection closed'); |
| }; |
| ``` |
|
|
| ### WebSocket Message Types |
|
|
| #### Client → Server |
|
|
| | Type | Description | |
| |------|-------------| |
| | `start` | Start a new chat session | |
| | `stop` | Stop current generation | |
| | `ping` | Keep-alive ping | |
|
|
| #### Server → Client |
|
|
| | Type | Description | |
| |------|-------------| |
| | `content` | Streamed content chunk | |
| | `tool_call` | Tool invocation request | |
| | `tool_result` | Tool execution result | |
| | `done` | Generation complete | |
| | `error` | Error occurred | |
| | `pong` | Keep-alive response | |
|
|
| ### Full WebSocket Example |
|
|
| ```javascript |
| class Stack29WebSocket { |
| constructor(apiKey, model = 'qwen/qwen2.5-coder-32b') { |
| this.apiKey = apiKey; |
| this.model = model; |
| this.ws = null; |
| } |
| |
| connect() { |
| return new Promise((resolve, reject) => { |
| this.ws = new WebSocket('wss://api.stack2.9.openclaw.org/v1/ws/chat'); |
| |
| this.ws.onopen = () => resolve(); |
| this.ws.onerror = (e) => reject(e); |
| }); |
| } |
| |
| async sendMessage(messages, onChunk, onComplete) { |
| await this.connect(); |
| |
| this.ws.onmessage = (event) => { |
| const data = JSON.parse(event.data); |
| |
| if (data.type === 'content') { |
| onChunk(data.content); |
| } else if (data.type === 'done') { |
| onComplete(data); |
| this.ws.close(); |
| } else if (data.type === 'error') { |
| console.error('Error:', data.message); |
| } |
| }; |
| |
| this.ws.send(JSON.stringify({ |
| type: 'start', |
| model: this.model, |
| messages: messages, |
| temperature: 0.7 |
| })); |
| } |
| } |
| |
| // Usage |
| const client = new Stack29WebSocket('your-api-key'); |
| client.sendMessage( |
| [{role: 'user', content: 'Write a hello world function'}], |
| (chunk) => process.stdout.write(chunk), |
| (final) => console.log('\n\nDone!') |
| ); |
| ``` |
|
|
| --- |
|
|
| ## Request/Response Formats |
|
|
| ### Message Format |
|
|
| ```json |
| { |
| "role": "user|assistant|system", |
| "content": "Message content", |
| "name": "optional-name", |
| "tool_calls": [], |
| "tool_call_id": "optional-id" |
| } |
| ``` |
|
|
| ### Tool Call Format |
|
|
| ```json |
| { |
| "type": "function", |
| "id": "call_abc123", |
| "function": { |
| "name": "execute_code", |
| "description": "Execute code in a sandboxed environment", |
| "parameters": { |
| "type": "object", |
| "properties": { |
| "code": { |
| "type": "string", |
| "description": "The code to execute" |
| }, |
| "language": { |
| "type": "string", |
| "description": "Programming language" |
| } |
| }, |
| "required": ["code", "language"] |
| } |
| } |
| } |
| ``` |
|
|
| ### Tool Call Response Format |
|
|
| ```json |
| { |
| "tool_call_id": "call_abc123", |
| "output": "Hello, World!", |
| "error": null, |
| "execution_time_ms": 150 |
| } |
| ``` |
|
|
| ### Vision Support (Image Input) |
|
|
| ```json |
| { |
| "role": "user", |
| "content": [ |
| { |
| "type": "text", |
| "text": "What does this code do?" |
| }, |
| { |
| "type": "image_url", |
| "image_url": { |
| "url": "https://example.com/screenshot.png", |
| "detail": "low" |
| } |
| } |
| ] |
| } |
| ``` |
|
|
| --- |
|
|
| ## Error Handling |
|
|
| ### Error Response Format |
|
|
| ```json |
| { |
| "error": { |
| "message": "Invalid API key", |
| "type": "invalid_request_error", |
| "param": "authorization", |
| "code": 401 |
| } |
| } |
| ``` |
|
|
| ### Error Codes |
|
|
| | Code | Type | HTTP Status | Description | |
| |------|------|-------------|-------------| |
| | `invalid_api_key` | Authentication | 401 | API key is invalid | |
| | `account_disabled` | Authentication | 403 | Account disabled | |
| | `rate_limit_exceeded` | Rate Limit | 429 | Too many requests | |
| | `context_length_exceeded` | Invalid Request | 400 | Context too long | |
| | `invalid_request` | Invalid Request | 400 | Malformed request | |
| | `model_not_found` | Invalid Request | 404 | Model doesn't exist | |
| | `tool_error` | Tool Error | 422 | Tool execution failed | |
| | `internal_error` | Server Error | 500 | Server-side error | |
| | `service_unavailable` | Server Error | 503 | Service temporarily down | |
|
|
| ### Handling Errors in Code |
|
|
| ```python |
| import openai |
| from openai import APIError, RateLimitError |
| |
| client = openai.OpenAI(api_key="your-api-key") |
| |
| try: |
| response = client.chat.completions.create( |
| model="qwen/qwen2.5-coder-32b", |
| messages=[{"role": "user", "content": "Hello"}] |
| ) |
| except RateLimitError: |
| print("Rate limit exceeded. Please wait.") |
| # Implement backoff logic |
| except APIError as e: |
| print(f"API error: {e}") |
| # Handle error appropriately |
| ``` |
|
|
| ```javascript |
| try { |
| const response = await client.chat.completions.create({ |
| model: 'qwen/qwen2.5-coder-32b', |
| messages: [{role: 'user', content: 'Hello'}] |
| }); |
| } catch (error) { |
| if (error.status === 429) { |
| console.log('Rate limit exceeded'); |
| } else if (error.status === 401) { |
| console.log('Invalid API key'); |
| } else { |
| console.error('API error:', error.message); |
| } |
| } |
| ``` |
|
|
| --- |
|
|
| ## SDKs and Examples |
|
|
| ### Python SDK |
|
|
| ```bash |
| pip install openai |
| ``` |
|
|
| ```python |
| from openai import OpenAI |
| |
| client = OpenAI( |
| api_key="your-api-key", |
| base_url="https://api.stack2.9.openclaw.org/v1" |
| ) |
| |
| # Non-streaming |
| response = client.chat.completions.create( |
| model="qwen/qwen2.5-coder-32b", |
| messages=[ |
| {"role": "system", "content": "You are a coding assistant."}, |
| {"role": "user", "content": "Write a Python class for a stack data structure."} |
| ], |
| temperature=0.7, |
| max_tokens=1000 |
| ) |
| |
| print(response.choices[0].message.content) |
| |
| # Streaming |
| stream = client.chat.completions.create( |
| model="qwen/qwen2.5-coder-32b", |
| messages=[{"role": "user", "content": "Explain recursion"}], |
| stream=True |
| ) |
| |
| for chunk in stream: |
| if chunk.choices[0].delta.content: |
| print(chunk.choices[0].delta.content, end="", flush=True) |
| ``` |
|
|
| ### JavaScript/Node.js SDK |
|
|
| ```bash |
| npm install openai |
| ``` |
|
|
| ```javascript |
| import OpenAI from 'openai'; |
| |
| const client = new OpenAI({ |
| apiKey: 'your-api-key', |
| baseURL: 'https://api.stack2.9.openclaw.org/v1' |
| }); |
| |
| // Non-streaming |
| const response = await client.chat.completions.create({ |
| model: 'qwen/qwen2.5-coder-32b', |
| messages: [ |
| {role: 'system', content: 'You are a coding assistant.'}, |
| {role: 'user', content: 'Write a Python class for a stack data structure.'} |
| ], |
| temperature: 0.7, |
| max_tokens: 1000 |
| }); |
| |
| console.log(response.choices[0].message.content); |
| |
| // Streaming |
| const stream = await client.chat.completions.create({ |
| model: 'qwen/qwen2.5-coder-32b', |
| messages: [{role: 'user', content: 'Explain recursion'}], |
| stream: true |
| }); |
| |
| for await (const chunk of stream) { |
| process.stdout.write(chunk.choices[0].delta.content || ''); |
| } |
| ``` |
|
|
| ### cURL Examples |
|
|
| ```bash |
| # Basic chat completion |
| curl -X POST https://api.stack2.9.openclaw.org/v1/chat/completions \ |
| -H "Authorization: Bearer YOUR_API_KEY" \ |
| -H "Content-Type: application/json" \ |
| -d '{ |
| "model": "qwen/qwen2.5-coder-32b", |
| "messages": [{"role": "user", "content": "Hello"}] |
| }' |
| |
| # Streaming completion |
| curl -X POST https://api.stack2.9.openclaw.org/v1/chat/completions \ |
| -H "Authorization: Bearer YOUR_API_KEY" \ |
| -H "Content-Type: application/json" \ |
| -d '{"model": "qwen/qwen2.5-coder-32b", "messages": [{"role": "user", "content": "Hello"}], "stream": true}' |
| |
| # With system prompt |
| curl -X POST https://api.stack2.9.openclaw.org/v1/chat/completions \ |
| -H "Authorization: Bearer YOUR_API_KEY" \ |
| -H "Content-Type: application/json" \ |
| -d '{ |
| "model": "qwen/qwen2.5-coder-32b", |
| "messages": [ |
| {"role": "system", "content": "You are an expert Python programmer."}, |
| {"role": "user", "content": "Write a decorator that caches function results."} |
| ] |
| }' |
| |
| # With tools |
| curl -X POST https://api.stack2.9.openclaw.org/v1/chat/completions \ |
| -H "Authorization: Bearer YOUR_API_KEY" \ |
| -H "Content-Type: application/json" \ |
| -d '{ |
| "model": "qwen/qwen2.5-coder-32b", |
| "messages": [{"role": "user", "content": "Create a file called hello.py"}], |
| "tools": [ |
| { |
| "type": "function", |
| "function": { |
| "name": "write_file", |
| "description": "Write content to a file", |
| "parameters": { |
| "type": "object", |
| "properties": { |
| "path": {"type": "string"}, |
| "content": {"type": "string"} |
| } |
| } |
| } |
| } |
| ] |
| }' |
| ``` |
|
|
| ### OpenAI-Compatible Client Usage |
|
|
| Stack 2.9 is compatible with the OpenAI client library: |
|
|
| ```python |
| # Works with LangChain |
| from langchain.chat_models import ChatOpenAI |
| from langchain.schema import HumanMessage |
| |
| chat = ChatOpenAI( |
| model="qwen/qwen2.5-coder-32b", |
| openai_api_base="https://api.stack2.9.openclaw.org/v1", |
| openai_api_key="your-api-key" |
| ) |
| |
| response = chat([HumanMessage(content="Hello!")]) |
| ``` |
|
|
| --- |
|
|
| ## Webhooks |
|
|
| Configure webhooks for asynchronous events: |
|
|
| ```json |
| { |
| "webhook_url": "https://your-server.com/webhook", |
| "events": [ |
| "tool_call.started", |
| "tool_call.completed", |
| "tool_call.failed", |
| "generation.done" |
| ] |
| } |
| ``` |
|
|
| ### Webhook Payload |
|
|
| ```json |
| { |
| "event": "tool_call.completed", |
| "timestamp": "2026-04-01T12:00:00Z", |
| "data": { |
| "tool_call_id": "call_abc123", |
| "tool_name": "execute_code", |
| "execution_time_ms": 150, |
| "result": "Hello, World!" |
| } |
| } |
| ``` |
|
|
| --- |
|
|
| ## Best Practices |
|
|
| ### 1. Use Streaming for Better UX |
|
|
| Always use streaming for long-form content to provide real-time feedback to users. |
|
|
| ### 2. Implement Proper Error Handling |
|
|
| Always handle rate limits and authentication errors gracefully with exponential backoff. |
|
|
| ### 3. Cache Responses |
|
|
| Cache frequent queries to reduce API calls and improve response times. |
|
|
| ```python |
| from functools import lru_cache |
| |
| @lru_cache(maxsize=100) |
| def cached_completion(prompt_hash): |
| # Implement caching logic |
| pass |
| ``` |
|
|
| ### 4. Use Appropriate Temperature |
|
|
| | Task | Temperature | |
| |------|-------------| |
| | Code generation | 0.0 - 0.3 | |
| | Factual Q&A | 0.0 - 0.2 | |
| | Creative writing | 0.7 - 1.0 | |
| | brainstorming | 0.8 - 1.2 | |
|
|
| ### 5. Monitor Token Usage |
|
|
| Track usage to stay within rate limits: |
|
|
| ```python |
| response = client.chat.completions.create(...) |
| print(f"Tokens used: {response.usage.total_tokens}") |
| ``` |
|
|
| --- |
|
|
| ## Support |
|
|
| - **Documentation**: [docs/index.html](docs/index.html) |
| - **API Status**: [status.stack2.9.openclaw.org](https://status.stack2.9.openclaw.org) |
| - **Issues**: [GitHub Issues](https://github.com/openclaw/stack-2.9/issues) |
| - **Email**: api@stack2.9.openclaw.org |
|
|
| --- |
|
|
| **API Version**: 1.0 |
| **Last Updated**: 2026-04-01 |
| **Status**: Active |
|
|