OpenAI-Compatible REST API¶
OmniAgent exposes an OpenAI-compatible REST API that allows you to use standard OpenAI client libraries to interact with your agent. The API supports SSE streaming, tool calling, and includes interactive documentation.
Quick Start¶
Start the gateway server:
The API is available at http://localhost:18789/openai/v1.
Using curl¶
curl http://localhost:18789/openai/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $API_KEY" \
-d '{
"model": "omniagent",
"messages": [{"role": "user", "content": "Hello!"}],
"stream": true
}'
Using Python¶
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:18789/openai/v1",
api_key="your-api-key"
)
# Streaming chat completion
response = client.chat.completions.create(
model="omniagent",
messages=[{"role": "user", "content": "What tools do you have?"}],
stream=True
)
for chunk in response:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="")
Using JavaScript/TypeScript¶
import OpenAI from 'openai';
const client = new OpenAI({
baseURL: 'http://localhost:18789/openai/v1',
apiKey: 'your-api-key',
});
const stream = await client.chat.completions.create({
model: 'omniagent',
messages: [{ role: 'user', content: 'Hello!' }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content || '');
}
API Endpoints¶
Chat Completions¶
POST /openai/v1/chat/completions¶
Create a chat completion with optional streaming.
Request:
{
"model": "omniagent",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello!"}
],
"stream": true,
"temperature": 0.7,
"max_tokens": 4096
}
Response (non-streaming):
{
"id": "chatcmpl-abc123",
"object": "chat.completion",
"created": 1719360000,
"model": "omniagent",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello! How can I help you today?"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 10,
"completion_tokens": 8,
"total_tokens": 18
}
}
Response (streaming):
Server-Sent Events (SSE) with data: prefixed JSON chunks:
data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}
data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"Hello"},"finish_reason":null}]}
data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"!"},"finish_reason":null}]}
data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]
Models¶
GET /openai/v1/models¶
List available models.
Response:
{
"object": "list",
"data": [
{
"id": "omniagent",
"object": "model",
"created": 1719360000,
"owned_by": "plexusone"
}
]
}
GET /openai/v1/models/{model}¶
Get details for a specific model.
Tools¶
GET /api/v1/tools¶
List all registered tools.
Response:
{
"object": "list",
"data": [
{
"name": "web_search",
"description": "Search the web for information",
"category": "search",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "Search query"}
},
"required": ["query"]
}
}
]
}
GET /api/v1/tools/usage¶
Get tool usage statistics summary.
Query Parameters:
since- Start time (RFC3339 or relative:1h,24h,7d,30d)until- End time (RFC3339, defaults to now)
Response:
{
"total_calls": 42,
"by_tool": {
"web_search": {
"tool_name": "web_search",
"call_count": 15,
"last_used": "2026-06-29T10:30:00Z",
"avg_latency_ms": 250.5,
"success_rate": 0.95
}
},
"top_tools": [...]
}
GET /api/v1/tools/{name}/stats¶
Get usage statistics for a specific tool.
Response:
{
"tool_name": "web_search",
"call_count": 15,
"last_used": "2026-06-29T10:30:00Z",
"avg_latency_ms": 250.5,
"success_rate": 0.95
}
Status¶
GET /api/v1/status¶
Get gateway status and version information.
Response:
{
"status": "ok",
"version": {
"version": "0.12.0",
"commit": "abc1234",
"build_date": "2026-06-29",
"go_version": "go1.26.4",
"platform": "darwin/arm64"
}
}
Agents¶
GET /api/v1/agents¶
List all configured agents.
Response:
{
"object": "list",
"data": [
{
"id": "default",
"name": "OmniAgent",
"description": "Default agent",
"model": "claude-sonnet-4-20250514",
"provider": "anthropic",
"enabled": true,
"created_at": "2026-06-20T00:00:00Z"
}
]
}
POST /api/v1/agents¶
Create a new agent.
Request:
{
"id": "research",
"name": "Research Agent",
"description": "Agent specialized for research tasks",
"provider": "openai",
"model": "gpt-4o",
"system_prompt": "You are a research assistant.",
"allowed_tools": ["web_search", "read_url"],
"denied_tools": ["shell"]
}
GET /api/v1/agents/{id}¶
Get agent details.
PUT /api/v1/agents/{id}¶
Update an agent.
DELETE /api/v1/agents/{id}¶
Delete an agent.
Cron Jobs¶
GET /api/v1/cron/jobs¶
List all scheduled jobs.
POST /api/v1/cron/jobs¶
Create a new job.
GET /api/v1/cron/jobs/{id}¶
Get job details.
DELETE /api/v1/cron/jobs/{id}¶
Delete a job.
POST /api/v1/cron/jobs/{id}/trigger¶
Trigger immediate execution.
Images¶
Image generation endpoints are available when configured. See Image Generation Guide for setup.
POST /openai/v1/images/generations¶
Generate images from a text prompt.
Request:
{
"model": "gpt-image-2",
"prompt": "A serene mountain landscape at sunset",
"n": 1,
"size": "1024x1024",
"quality": "standard",
"response_format": "url"
}
Response:
{
"created": 1719360000,
"data": [
{
"url": "https://...",
"revised_prompt": "A serene mountain landscape..."
}
]
}
POST /openai/v1/images/edits¶
Edit an existing image with a mask.
POST /openai/v1/images/variations¶
Create variations of an existing image.
Usage¶
GET /api/v1/usage¶
Get usage statistics.
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
start_time |
string | Start time (RFC3339) |
end_time |
string | End time (RFC3339) |
agent_id |
string | Filter by agent ID |
Health¶
GET /health¶
Health check endpoint.
Response:
Authentication¶
API Authentication (Bearer Token)¶
The API supports Bearer token authentication:
Configure API keys in your config file:
Or via environment variable:
Web UI Authentication (OAuth SSO)¶
The embedded web UI supports OAuth 2.0 SSO with GitHub and Google. When enabled, users must authenticate before accessing the chat interface.
export AUTH_ENABLED=true
export AUTH_SESSION_SECRET="your-32-byte-secret-key-here!!!"
export AUTH_GITHUB_CLIENT_ID="your-client-id"
export AUTH_GITHUB_CLIENT_SECRET="your-secret"
omniagent openai serve --web-ui --address :8080
See Web UI Authentication for full setup instructions.
API Documentation¶
Interactive API documentation is available at /docs using Scalar UI:
The OpenAPI 3.1 specification is available at:
- JSON:
http://localhost:18789/openapi.json - YAML:
http://localhost:18789/openapi.yaml
Generating Static Spec¶
Generate a static OpenAPI specification file:
omniagent openai spec --output docs/api/openapi.yaml
omniagent openai spec --format json --output docs/api/openapi.json
Tool Calling¶
When tools are available, the API supports OpenAI-style tool calling:
response = client.chat.completions.create(
model="omniagent",
messages=[{"role": "user", "content": "Search for recent AI news"}],
)
# If the model wants to call a tool
if response.choices[0].message.tool_calls:
tool_call = response.choices[0].message.tool_calls[0]
print(f"Tool: {tool_call.function.name}")
print(f"Args: {tool_call.function.arguments}")
Tool results are automatically processed by the agent when using OmniAgent's streaming endpoint.
Session Management¶
Use the session_id parameter to maintain conversation context:
{
"model": "omniagent",
"messages": [{"role": "user", "content": "Remember my name is Alice"}],
"session_id": "user-123"
}
Subsequent requests with the same session_id will have access to conversation history.
Multi-Agent Routing¶
When multiple agents are configured, specify the agent using model:
{
"model": "research",
"messages": [{"role": "user", "content": "Search for papers on transformers"}]
}
Or use the default agent:
See Multi-Agent Guide for configuring multiple agents.
Rate Limiting¶
The API includes built-in rate limiting. Configure limits per API key:
gateway:
rate_limits:
default:
requests_per_minute: 60
tokens_per_minute: 100000
premium:
requests_per_minute: 600
tokens_per_minute: 1000000
Rate limit headers are included in responses:
Error Handling¶
Errors follow the OpenAI error format:
{
"error": {
"message": "Invalid API key",
"type": "invalid_request_error",
"code": "invalid_api_key"
}
}
| HTTP Status | Error Type | Description |
|---|---|---|
| 400 | invalid_request_error |
Malformed request |
| 401 | authentication_error |
Invalid or missing API key |
| 404 | not_found_error |
Resource not found |
| 429 | rate_limit_error |
Too many requests |
| 500 | api_error |
Internal server error |
Architecture¶
The API is built on a hybrid architecture:
- Chi Router: Base HTTP router with middleware
- ogen: OpenAI-compatible endpoints generated from OpenAPI spec
- Huma: Custom endpoints with automatic OpenAPI generation
- Scalar: Interactive API documentation
Chi Router (base)
├── /openai/v1/chat/completions → ogen (SSE streaming)
├── /openai/v1/models → ogen
├── /openai/v1/images/* → Huma (image generation)
├── /api/v1/status → Huma (gateway status)
├── /api/v1/tools → Huma
├── /api/v1/tools/usage → Huma (tool statistics)
├── /api/v1/agents/* → Huma
├── /api/v1/cron/jobs/* → Huma
├── /api/v1/usage/* → Huma
├── /api/health → Huma
├── /api/openapi.json → Merged spec
└── /docs → Scalar UI