Base URL
http://localhost:3333/api/· Authentication · Conventions
Model Routes
Base path: /api/models
Also mounted at /api/openrouter for legacy backward compatibility (all routes below work at both base paths).
List Available Providers
To discover all available built-in providers, request models for an unknown provider name. The error response includes the full list:
GET /:provider/models (with an invalid provider name)
{
"success": false,
"error": "Unknown provider: invalid",
"availableProviders": [
"openai",
"anthropic",
"gemini",
"grokai",
"groq",
"deepseek",
"openrouter",
"togetherai",
"cerebras",
"kimi",
"minimax",
"zai",
"openai-codex",
"claude-code",
"gemini-cli"
]
}Alternatively, the Provider Health endpoint returns status for all providers, and Provider Templates lists additional custom-provider-ready templates.
Get Models by Provider
GET /:provider/models
- Authentication: Required (except for static-model providers like
openai-codexand CLI providers which use local auth) - Parameters:
provider(path): Provider key or display name. Keys:openai,anthropic,gemini,grokai(alias:grok),groq,deepseek,openrouter,togetherai,cerebras,kimi,minimax,zai,openai-codex,claude-code,gemini-cli. Display names like"Z-AI"or"Grok AI"are also resolved.category(query, optional): Filter by category —all(default),programming,creative,reasoninguseCache(query, optional): Use cached models —true(default) orfalseformat(query, optional): Response format —names(default, array of model ID strings) orfull(array of model objects with metadata)
- Description: Fetch available models from a specific provider
- Response (
format=names, default):
{
"success": true,
"models": ["gpt-4o", "gpt-4-turbo", "gpt-3.5-turbo"],
"cached": true,
"count": 3
}- Response (
format=full):
{
"success": true,
"models": [
{
"id": "gpt-4o",
"name": "gpt-4o",
"description": "",
"createdAt": "2024-05-13T00:00:00Z",
"ownedBy": "openai"
}
],
"cached": true,
"count": 1
}- Error (400):
{ "error": "Unknown provider: <name>", "availableProviders": [...] }if provider not found - Error (400): Provider-specific auth errors (e.g., CLI not connected, API key not found)
- Error (401):
{ "error": "Authentication required to fetch <provider> models" }if JWT missing for standard providers
Refresh Models Cache
POST /:provider/models/refresh
- Authentication: Required (same rules as GET)
- Parameters:
provider(path): Provider key or display name
- Description: Clear the models cache and fetch fresh models from the provider API
- Response:
{
"success": true,
"models": ["gpt-4o", "gpt-4-turbo"],
"count": 2,
"message": "openai models cache refreshed successfully"
}Get OpenRouter Models (Legacy)
GET /models
- Description: Legacy endpoint — redirects internally to
/:provider/modelswithprovider=openrouter - Response: Same as
GET /:provider/modelsfor openrouter
Refresh OpenRouter Models (Legacy)
POST /models/refresh
- Description: Legacy endpoint — redirects internally to
/:provider/models/refreshwithprovider=openrouter - Response: Same as
POST /:provider/models/refreshfor openrouter
Get Provider Metadata (All Models)
GET /:provider/metadata
- Authentication: None
- Parameters:
provider(path): Provider key
- Description: Get metadata (cost, context window, capabilities) for all models from a specific provider. Sourced from static configuration, not live API calls.
- Response:
{
"success": true,
"provider": "openai",
"metadata": {
"gpt-4o": {
"contextWindow": 128000,
"maxOutputTokens": 16384,
"inputCostPer1M": 2.5,
"outputCostPer1M": 10.0,
"supportsVision": true,
"supportsTools": true,
"reasoning": false
}
}
}Note: Returns null metadata for providers without configured model metadata.
Get Model Metadata (Single Model)
GET /:provider/metadata/:modelId
- Authentication: None
- Parameters:
provider(path): Provider keymodelId(path): Model IDinputTokens(query, optional): Input token count for cost estimateoutputTokens(query, optional): Output token count for cost estimate
- Description: Get metadata for a specific model, optionally with cost estimate. The
costfield is only included when bothinputTokensandoutputTokensare provided. - Response:
{
"success": true,
"provider": "openai",
"model": "gpt-4o",
"metadata": {
"contextWindow": 128000,
"maxOutputTokens": 16384,
"inputCostPer1M": 2.5,
"outputCostPer1M": 10.0,
"supportsVision": true,
"supportsTools": true
},
"reasoning": false,
"cost": {
"inputCost": 0.0025,
"outputCost": 0.01,
"totalCost": 0.0125
}
}- Response (model not found in metadata):
{ "success": true, "provider": "openai", "model": "unknown-model", "metadata": null }
Get Provider Health
GET /provider-health
- Authentication: None
- Description: Get cached provider health status for all configured providers. Returns the last-known status without making new API calls.
- Response:
{
"success": true,
"overall": "degraded",
"healthy": 8,
"degraded": 0,
"unhealthy": 1,
"unknown": 0,
"total": 9,
"providers": {
"openai": { "status": "healthy", "lastChecked": "2024-01-01T00:00:00Z" },
"anthropic": { "status": "healthy", "lastChecked": "2024-01-01T00:00:00Z" },
"gemini": { "status": "unhealthy", "error": "Invalid API key", "lastChecked": "2024-01-01T00:00:00Z" }
}
}The overall field is one of: healthy, degraded (some unhealthy/degraded), critical (all unhealthy), unknown.
Check Provider Health (Live)
POST /provider-health/check
- Authentication: Optional (if
Authorization: Bearer <JWT>is provided, the user's stored API keys are used for more accurate health checks) - Description: Run fresh live health checks against all configured providers. More expensive than the cached GET endpoint — makes actual API calls.
- Response:
{
"success": true,
"overall": "healthy",
"healthy": 9,
"degraded": 0,
"unhealthy": 0,
"unknown": 0,
"total": 9,
"providers": {
"openai": { "status": "healthy", "lastChecked": "2024-01-01T00:00:00Z" },
"anthropic": { "status": "healthy", "lastChecked": "2024-01-01T00:00:00Z" }
}
}Get Model Categories
GET /models/categories
- Authentication: None
- Description: Get available model categories for filtering
- Response:
{
"success": true,
"categories": [
{
"id": "all",
"name": "All Models",
"description": "All available models"
},
{
"id": "programming",
"name": "Programming",
"description": "Models optimized for code generation and programming tasks"
},
{
"id": "creative",
"name": "Creative",
"description": "Models optimized for creative writing and content generation"
},
{
"id": "reasoning",
"name": "Reasoning",
"description": "Models optimized for logical reasoning and problem solving"
}
]
}Get Schema Version
GET /schema-version
- Authentication: None
- Description: Hash of the current model schema — clients poll this to know when cached model lists are stale
- Response:
{
"success": true,
"version": "<schema-hash>"
}List Models (legacy OpenRouter shape)
GET /models
- Authentication: None
- Description: Legacy alias — forces
provider=openrouterand re-dispatches toGET /:provider/models
Refresh Models (legacy OpenRouter shape)
POST /models/refresh
- Authentication: None
- Description: Legacy alias — forces
provider=openrouterand re-dispatches toPOST /:provider/models/refresh
Get Model Categories
GET /models/categories
- Authentication: None
- Description: Static model category list
- Response:
{
"success": true,
"categories": [
{ "id": "all", "name": "All Models", "description": "All available models" },
{ "id": "programming", "name": "Programming", "description": "Models optimized for code generation and programming tasks" },
{ "id": "creative", "name": "Creative", "description": "Models optimized for creative writing and content generation" },
{ "id": "reasoning", "name": "Reasoning", "description": "Models optimized for logical reasoning and problem solving" }
]
}Get Model Metadata (provider)
GET /:provider/metadata
- Authentication: None
- Description: Metadata for all models of a provider
- Response:
{
"success": true,
"provider": "openai",
"metadata": []
}Get Model Metadata (single model)
GET /:provider/metadata/:modelId
- Authentication: None
- Parameters:
inputTokens,outputTokens(query, optional): when both are present the response includes acostestimate
- Response (
metadataisnullfor unknown models):
{
"success": true,
"provider": "openai",
"model": "gpt-4o",
"metadata": {},
"reasoning": false,
"cost": {}
}Get Provider Health
GET /provider-health
- Authentication: None
- Description: Current health status and summary for all providers
- Response:
{
"success": true,
"providers": {}
}Check Provider Health
POST /provider-health/check
- Authentication: Optional — the user is resolved from the bearer token when present
- Description: Triggers a fresh provider health check
- Response: Updated provider health
OpenRouter legacy mount
The same router is also mounted at /api/openrouter for backward compatibility (server.js line 189). Every endpoint in this section is reachable under either base — /api/models is canonical, /api/openrouter is the legacy alias.
Provider Auth Routes
Base path: /api/providers
All provider authentication is handled through a single unified router. The :providerId parameter identifies the provider (e.g., claude-code, openai-codex, gemini-cli, openai, anthropic, etc.). Local CLI providers use filesystem-backed credentials; remote providers proxy to agnt.gg.
Note: An unknown :providerId returns 404: { "success": false, "error": "Unknown provider: <id>" }.
Auth Dispatcher (AuthDispatcher.js) maps each provider's authScheme to an auth manager and a set of capabilities:
| Auth Scheme | Local | Capabilities |
|---|---|---|
claude-code |
Yes | status, connect-token, disconnect, refresh, oauth-pkce |
codex |
Yes | status, disconnect, device-auth |
gemini-cli |
Yes | status, connect-apikey, disconnect, refresh, oauth-loopback, set-auth-method, gcp-project |
bearer |
No | status, connect-apikey, disconnect |
api-key |
No | status, connect-apikey, disconnect |
query-param |
No | status, connect-apikey, disconnect |
Get Provider Auth Status
GET /:providerId/auth/status
- Authentication: Required
- Description: Check whether credentials exist for this provider and whether its API is usable. For local CLI providers, checks filesystem credentials. For remote providers, returns basic info (use the connection health endpoint for remote status).
- Response (local provider):
{
"success": true,
"available": true,
"apiUsable": true,
"hint": "Claude Code is connected and the Anthropic API is usable."
}For openai-codex, also includes codexWorkdir and toolRunner fields.
- Response (remote provider):
{
"success": true,
"available": false,
"providerId": "openai",
"local": false,
"hint": "Use connection health endpoint for remote provider status."
}Get Provider Capabilities
GET /:providerId/auth/capabilities
- Authentication: Required
- Description: Return the capabilities and metadata for a provider's auth scheme
- Response:
{
"success": true,
"providerId": "claude-code",
"providerName": "Claude Code",
"local": true,
"remote": false,
"capabilities": ["status", "connect-token", "disconnect", "refresh", "oauth-pkce"]
}Connect Provider
POST /:providerId/auth/connect
- Authentication: Required. (Historically unauthenticated for local providers;
authenticateTokennow rejects, so a token is needed for both local and remote.) - Description: Save credentials for a provider. Body and response vary by auth scheme.
- Body (claude-code — token):
{
"token": "sk-ant-..."
}- Body (gemini-cli — API key):
{
"apiKey": "AIza..."
}- Body (remote providers — proxied to agnt.gg):
{
"apiKey": "sk-..."
}- Response (claude-code):
{
"success": true,
"message": "Token saved successfully",
"apiUsable": true
}- Response (gemini-cli):
{
"success": true,
"message": "Gemini CLI connected successfully",
"apiUsable": true
}- Response (remote providers): Proxied from agnt.gg
- Error (400): Missing or invalid credentials for the auth scheme
- Error (400):
{ "error": "Connect not supported for <providerId>" }if local provider has no connect handler
Disconnect Provider
POST /:providerId/auth/disconnect
- Authentication: Required. (Historically unauthenticated for local providers;
authenticateTokennow rejects, so a token is needed for both local and remote.) - Description: Remove credentials for a provider. Local providers delete filesystem credentials; remote providers proxy to agnt.gg.
- Response:
{
"success": true
}Refresh Token
POST /:providerId/auth/refresh
- Authentication: Required
- Description: Refresh the access token using the stored refresh token. Only supported for local providers with the
refreshcapability (claude-code, gemini-cli). - Response (success):
{
"success": true,
"refreshed": true,
"available": true,
"apiUsable": true
}- Error (400):
{ "error": "Refresh not supported for this provider" }if provider lacksrefreshcapability - Error (401 — claude-code specific):
{ "code": "REAUTH_REQUIRED", "error": "..." }if refresh token is revoked - Error (502):
{ "code": "REFRESH_FAILED", "error": "..." }if token refresh fails upstream
Start OAuth Flow
GET /:providerId/auth/oauth/start
- Authentication: Required
- Description: Initiate an OAuth flow. For
claude-code, starts Anthropic PKCE OAuth. Forgemini-cli, starts Google loopback OAuth. Only supported for local providers. - Error (400):
{ "error": "OAuth start not supported for remote providers" }if provider is not local - Response:
{
"success": true,
"authUrl": "https://console.anthropic.com/oauth/authorize?...",
"sessionId": "session-uuid"
}Exchange OAuth Code (claude-code PKCE)
POST /:providerId/auth/oauth/exchange
- Authentication: Required
- Description: Submit the code#state string copied from Anthropic's callback page. Only supported for providers with
oauth-pkcecapability. - Body:
{
"sessionId": "session-uuid",
"codeState": "auth-code#state-value"
}- Response:
{
"success": true
}- Error (400):
{ "error": "OAuth exchange not supported for this provider" }if provider lacksoauth-pkcecapability - Error (400):
{ "error": "sessionId and codeState are required" }if body is incomplete - Error (400):
{ "error": "Could not parse the authorization code. Please copy the full code from the Anthropic page and try again." }if code/state parsing fails
Poll OAuth Status (gemini-cli loopback)
GET /:providerId/auth/oauth/status?sessionId=...
- Authentication: Required
- Parameters:
sessionId(query, required): The session ID from the OAuth start endpoint
- Description: Poll the loopback OAuth session state. Only supported for providers with
oauth-loopbackcapability. - Response:
{
"success": true,
"state": "pending|completed|expired|error"
}- Error (400):
{ "error": "Missing sessionId" }if query param missing - Error (400):
{ "error": "OAuth status polling not supported for this provider" }if provider lacksoauth-loopbackcapability
Start Device Auth (openai-codex)
POST /:providerId/auth/device/start
- Authentication: Required
- Description: Start device login flow. Returns a URL and code the user enters in a browser. Only supported for providers with
device-authcapability. - Response:
{
"success": true,
"sessionId": "session-uuid",
"deviceUrl": "https://auth.openai.com/device",
"deviceCode": "ABCD-1234",
"state": "pending",
"message": null,
"startedAt": "2024-01-01T00:00:00Z",
"expiresAt": "2024-01-01T00:15:00Z",
"hint": "Open the URL, enter the code, then return here. We will poll for completion."
}- Error (400):
{ "error": "Device auth not supported for this provider" }if provider lacksdevice-authcapability
Poll Device Auth Status (openai-codex)
GET /:providerId/auth/device/status?sessionId=...
- Authentication: Required
- Parameters:
sessionId(query, required): The session ID from the device start endpoint
- Description: Poll the device login session state. Only supported for providers with
device-authcapability. - Response:
{
"success": true,
"state": "pending|completed|expired|error"
}- Error (400):
{ "error": "sessionId is required" }if query param missing or not a string - Error (400):
{ "error": "Device auth not supported for this provider" }if provider lacksdevice-authcapability
Set Auth Method (gemini-cli)
POST /:providerId/auth/set-auth-method
- Authentication: Required
- Description: Switch between API key and OAuth authentication methods. When switching to
api-key, removes OAuth credentials from~/.gemini/oauth_creds.json. When switching tooauth, removes API key from~/.gemini/.env. Only supported for providers withset-auth-methodcapability. - Body:
{
"method": "api-key|oauth"
}- Response:
{
"success": true,
"available": true,
"apiUsable": true
}- Error (400):
{ "error": "method must be \"api-key\" or \"oauth\"" }if method is invalid - Error (400):
{ "error": "set-auth-method not supported for this provider" }if provider lacks capability
Set GCP Project (gemini-cli)
POST /:providerId/auth/gcp-project
- Authentication: Required
- Description: Set the Google Cloud Project ID (required for workspace/organization accounts). Only supported for providers with
gcp-projectcapability. - Body:
{
"projectId": "my-gcp-project"
}- Response:
{
"success": true,
"projectId": "my-gcp-project"
}- Error (400):
{ "error": "Missing projectId" }if body is incomplete - Error (400):
{ "error": "GCP project not supported for this provider" }if provider lacks capability
Custom Provider Routes
Base path: /api/custom-providers
Manage user-created custom OpenAI-compatible providers (e.g., local Ollama, LM Studio, or any OpenAI-compatible API endpoint).
Get All Custom Providers
GET /
- Authentication: Required
- Description: Retrieve all custom providers for the authenticated user
- Response:
{
"success": true,
"providers": [
{
"id": "uuid",
"user_id": "user-uuid",
"provider_name": "Local LM Studio",
"base_url": "http://localhost:1234",
"is_active": 1,
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
],
"count": 1
}Create Custom Provider
POST /
- Authentication: Required
- Body:
{
"provider_name": "Custom Provider",
"base_url": "https://api.example.com",
"api_key": "your-api-key"
}provider_name(required): Display namebase_url(required): API base URLapi_key(optional): API key for authenticationResponse (201 Created):
{
"success": true,
"provider": {
"id": "uuid",
"user_id": "user-uuid",
"provider_name": "Custom Provider",
"base_url": "https://api.example.com",
"is_active": 1,
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
},
"message": "Custom provider created successfully"
}- Error (400):
{ "error": "Missing required fields: provider_name, base_url" }
Get Custom Provider by ID
GET /:id
- Authentication: Required
- Parameters:
id(path): Provider UUID
- Description: Retrieve a specific custom provider by ID
- Response:
{
"success": true,
"provider": {
"id": "uuid",
"user_id": "user-uuid",
"provider_name": "Custom Provider",
"base_url": "https://api.example.com",
"is_active": 1,
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
}- Error (404):
{ "error": "Provider not found" }
Update Custom Provider
PUT /:id
- Authentication: Required
- Parameters:
id(path): Provider UUID
- Body (all fields optional):
{
"provider_name": "Updated Provider Name",
"base_url": "https://api.updated.com",
"api_key": "new-api-key",
"is_active": 1
}- Response:
{
"success": true,
"provider": {
"id": "uuid",
"user_id": "user-uuid",
"provider_name": "Updated Provider Name",
"base_url": "https://api.updated.com",
"is_active": 1,
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
},
"message": "Custom provider updated successfully"
}- Error (404): Provider not found
Delete Custom Provider
DELETE /:id
- Authentication: Required
- Parameters:
id(path): Provider UUID
- Description: Delete a custom provider by ID
- Response:
{
"success": true,
"message": "Custom provider deleted successfully"
}- Error (404): Provider not found
Get Provider Templates
GET /templates
- Authentication: None
- Description: Get all pre-configured provider templates for creating custom providers. Includes cloud APIs (Mistral, Fireworks, Perplexity, etc.) and local inference servers (Ollama, LM Studio).
- Response:
{
"success": true,
"templates": [
{
"key": "ollama",
"name": "Ollama (Local)",
"baseURL": "http://localhost:11434/v1",
"defaultModel": "llama3.2",
"supportsTools": true,
"supportsStreaming": true,
"requiresApiKey": false,
"description": "Ollama — Run open-source LLMs locally"
},
{
"key": "mistral",
"name": "Mistral AI",
"baseURL": "https://api.mistral.ai/v1",
"defaultModel": "mistral-large-latest",
"supportsTools": true,
"supportsVision": true,
"supportsStreaming": true,
"description": "Mistral AI — European AI lab with efficient, high-quality models"
}
],
"count": 10
}Test Custom Provider Connection
POST /test
- Authentication: Required
- Description: Test connection to a custom provider without saving it. Normalizes the URL (adds
/v1if needed) and calls the/modelsendpoint. - Body:
{
"base_url": "https://api.example.com",
"api_key": "your-api-key"
}base_url(required): API base URL to testapi_key(optional): API key for authenticationResponse (success):
{
"success": true,
"modelsCount": 15,
"models": ["model-1", "model-2", "model-3", "model-4", "model-5"]
}Note: Returns at most 5 model IDs as a preview.
- Response (connection failure):
{
"success": false,
"error": "HTTP 401: Unauthorized"
}- Error (400):
{ "error": "Missing required fields: base_url" }
Get Custom Provider Models
GET /:id/models
- Authentication: Required
- Parameters:
id(path): Provider UUID
- Description: Fetch all available models from a custom provider
- Response:
{
"success": true,
"models": ["model-1", "model-2", "model-3"],
"count": 3
}