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-codex and 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, reasoning
    • useCache (query, optional): Use cached models — true (default) or false
    • format (query, optional): Response format — names (default, array of model ID strings) or full (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/models with provider=openrouter
  • Response: Same as GET /:provider/models for openrouter

Refresh OpenRouter Models (Legacy)

POST /models/refresh

  • Description: Legacy endpoint — redirects internally to /:provider/models/refresh with provider=openrouter
  • Response: Same as POST /:provider/models/refresh for 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 key
    • modelId (path): Model ID
    • inputTokens (query, optional): Input token count for cost estimate
    • outputTokens (query, optional): Output token count for cost estimate
  • Description: Get metadata for a specific model, optionally with cost estimate. The cost field is only included when both inputTokens and outputTokens are 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=openrouter and re-dispatches to GET /:provider/models

Refresh Models (legacy OpenRouter shape)

POST /models/refresh

  • Authentication: None
  • Description: Legacy alias — forces provider=openrouter and re-dispatches to POST /: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 a cost estimate
  • Response (metadata is null for 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; authenticateToken now 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; authenticateToken now 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 refresh capability (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 lacks refresh capability
  • 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. For gemini-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-pkce capability.
  • Body:
{
  "sessionId": "session-uuid",
  "codeState": "auth-code#state-value"
}
  • Response:
{
  "success": true
}
  • Error (400): { "error": "OAuth exchange not supported for this provider" } if provider lacks oauth-pkce capability
  • 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-loopback capability.
  • 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 lacks oauth-loopback capability

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-auth capability.
  • 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 lacks device-auth capability

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-auth capability.
  • 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 lacks device-auth capability

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 to oauth, removes API key from ~/.gemini/.env. Only supported for providers with set-auth-method capability.
  • 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-project capability.
  • 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 name

  • base_url (required): API base URL

  • api_key (optional): API key for authentication

  • Response (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 /v1 if needed) and calls the /models endpoint.
  • Body:
{
  "base_url": "https://api.example.com",
  "api_key": "your-api-key"
}
  • base_url (required): API base URL to test

  • api_key (optional): API key for authentication

  • Response (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
}