AI API
Endpoints for interacting with AI models through a unified provider layer. These endpoints are served by the backend API service, not the web application. They are available at the backend base URL, which may differ from the web API base URL depending on your deployment.
These endpoints are internal backend endpoints and are not exposed through the web application’s /api routes. The chat endpoint requires a valid subscription plan; other endpoints in this group do not require authentication.
Health check
Returns the availability status of configured AI providers.
Response
{
"status": "healthy",
"providers": {
"openrouter": true
},
"timestamp": "2026-03-19T00:00:00Z"
}
The status field is healthy when all providers are reachable and degraded when one or more are down.
Error response
When one or more providers fail, the response uses status: "error" and includes the error message:
{
"status": "error",
"error": "Provider connection failed"
}
| Code | Description |
|---|
| 503 | AI service unavailable |
List models
Returns all available AI models across providers.
Response
{
"models": [
{
"id": "anthropic/claude-sonnet-4-20250514",
"name": "Claude Sonnet",
"provider": "openrouter",
"description": "Fast, intelligent model for everyday tasks",
"tags": ["chat", "code"],
"inputCost": 0.003,
"outputCost": 0.015,
"contextWindow": 200000,
"available": true
}
],
"count": 1,
"openrouter": 1,
"timestamp": "2026-03-19T00:00:00Z"
}
Errors
| Code | Description |
|---|
| 500 | Failed to fetch models |
List models by provider
GET /api/ai/models/:provider
Path parameters
| Parameter | Type | Description |
|---|
provider | string | Provider name (for example, openrouter) |
Response
{
"provider": "openrouter",
"models": [],
"count": 0,
"timestamp": "2026-03-19T00:00:00Z"
}
Select model
POST /api/ai/models/select
Automatically selects the best model for a given task type.
Request body
| Field | Type | Required | Description |
|---|
taskType | string | No | Type of task (default: general) |
Response
{
"model": {
"id": "anthropic/claude-sonnet-4-20250514",
"provider": "openrouter"
},
"taskType": "general",
"timestamp": "2026-03-19T00:00:00Z"
}
Errors
| Code | Description |
|---|
| 404 | No models available |
Chat completion
Send a chat completion request through the unified AI provider layer. The model is auto-selected if not specified.
This endpoint requires a valid subscription plan. Requests without a recognized plan or active Stripe subscription receive a
402 response. The requested model must also be available on your plan — see
plan-based model access below.
The following headers are required for plan enforcement:
| Header | Type | Required | Description |
|---|
x-user-plan | string | Yes | Subscription plan name (label, solo, collective, or network) |
x-user-email | string | No | User email. Admin emails bypass plan restrictions. |
x-stripe-subscription-id | string | Yes | Active Stripe subscription ID |
Request body
| Field | Type | Required | Description |
|---|
messages | array | Yes | Array of message objects with role (user, assistant, or system) and content |
model | string | No | Model ID. Auto-selected based on taskType if omitted. Must be allowed by your plan. |
taskType | string | No | Used for auto-selection when model is omitted |
temperature | number | No | Sampling temperature |
top_p | number | No | Nucleus sampling parameter |
max_tokens | number | No | Maximum tokens in the response |
Example request
{
"messages": [
{ "role": "system", "content": "You are a helpful assistant." },
{ "role": "user", "content": "Hello!" }
],
"temperature": 0.7,
"max_tokens": 1024
}
Response
Returns a structured response with the following shape:
{
"id": "chatcmpl-abc123",
"model": "anthropic/claude-sonnet-4-20250514",
"provider": "openrouter",
"message": {
"role": "assistant",
"content": "Hello! How can I help you today?"
},
"usage": {
"promptTokens": 25,
"completionTokens": 10,
"totalTokens": 35
},
"timestamp": "2026-03-19T00:00:00Z"
}
Errors
| Code | Description |
|---|
| 400 | Messages array is required and must be non-empty |
| 402 | Valid subscription required. Returned when the plan header is missing or unrecognized (PLAN_REQUIRED), or when there is no active Stripe subscription (SUBSCRIPTION_REQUIRED). |
| 403 | Model not available on your plan (MODEL_RESTRICTED). The response includes an allowedModels array listing the models your plan supports. |
| 404 | No models available |
| 500 | AI provider error |
402 error example
{
"success": false,
"error": "Valid subscription required. Choose a plan at /pricing",
"code": "PLAN_REQUIRED"
}
403 error example
{
"error": "Model openai/gpt-4-turbo not available on your plan. Upgrade for more models.",
"code": "MODEL_RESTRICTED",
"allowedModels": ["openai/gpt-4o-mini", "google/gemini-2.0-flash"]
}
Plan-based model access
Each subscription plan grants access to a specific set of AI models. The chat endpoint enforces these limits automatically.
| Plan | Price | Models | Agent limit | Skill limit | A2A messages/day |
|---|
label | $29/mo | openai/gpt-4o-mini, google/gemini-2.0-flash | 1 | 3 | 100 |
solo | $79/mo | openai/gpt-4o-mini, openai/gpt-4o, google/gemini-2.0-flash, anthropic/claude-3.5-sonnet | 3 | 10 | 500 |
collective | $199/mo | openai/gpt-4o-mini, openai/gpt-4o, openai/gpt-4-turbo, google/gemini-2.0-flash, anthropic/claude-3.5-sonnet, anthropic/claude-3-opus | 10 | 25 | 2,000 |
network | $499/mo | All models | 100 | 100 | 10,000 |
Admin users are automatically granted network-level access regardless of their subscription plan.
Estimate cost
POST /api/ai/estimate-cost
Estimate the cost of a request based on token counts and model pricing.
Request body
| Field | Type | Required | Description |
|---|
model | string | Yes | Model ID |
inputTokens | number | Yes | Number of input tokens |
outputTokens | number | Yes | Number of output tokens |
Response
{
"model": "anthropic/claude-sonnet-4-20250514",
"inputTokens": 1000,
"outputTokens": 500,
"estimatedCost": 0.0045,
"currency": "USD",
"timestamp": "2026-03-19T00:00:00Z"
}
Errors
| Code | Description |
|---|
| 400 | Model, inputTokens, and outputTokens are all required |