Settings API

Settings control every aspect of Stagent’s behavior — from API key management and budget limits to model routing and runtime tuning. Each configuration domain lives under its own sub-route, so clients only fetch and update what they need.

Quick Start

Check provider status, configure an API key, and set a budget limit — a typical setup flow:

// 1. Get the current provider overview
const res1: Response = await fetch('/api/settings/providers');
const { providers, routingPreference }: { providers: Record<string, any>; routingPreference: string } = await res1.json();
console.log(`Anthropic configured: ${providers.anthropic.configured}`);
console.log(`Routing: ${routingPreference}`);

// 2. Set an Anthropic API key
await fetch('/api/settings', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ apiKey: 'sk-ant-api03-...' }),
});

// 3. Set a daily and monthly budget limit
await fetch('/api/settings/budgets', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ dailyLimit: 10.00, monthlyLimit: 200.00 }),
});

// 4. Route to the cheapest available model
await fetch('/api/settings/routing', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ preference: 'cost' }),
});

// 5. Test the connection
const testRes: Response = await fetch('/api/settings/test', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ runtime: 'claude-code' }),
});
const test: { connected: boolean } = await testRes.json();
console.log(`Connected: ${test.connected}`);

Base URL

/api/settings

Provider Configuration

Get Provider Overview

GET /api/settings/providers

Aggregated provider status including configured runtimes, auth methods, dual-billing detection, and routing preference.

Response Body

FieldTypeReqDescription
providers.anthropic.configuredboolean*Whether any Anthropic runtime is configured
providers.anthropic.authMethodstring*Current auth method (oauth, api-key, etc.)
providers.anthropic.hasKeyboolean*Whether an API key is present
providers.anthropic.dualBillingboolean*True when both OAuth and API key are active
providers.anthropic.runtimesobject[]*Runtime setup states for Claude Code and Anthropic Direct
providers.openai.configuredboolean*Whether any OpenAI runtime is configured
providers.openai.hasKeyboolean*Whether an OpenAI API key is present
providers.openai.runtimesobject[]*Runtime setup states for Codex and OpenAI Direct
routingPreferenceenum*cost, latency, quality, or manual
configuredProviderCountnumber*Number of providers with at least one runtime configured

Check which providers are ready before assigning tasks to agents:

// Check provider readiness before creating tasks
const res: Response = await fetch('http://localhost:3000/api/settings/providers');
const { providers, routingPreference, configuredProviderCount }: {
providers: Record<string, any>;
routingPreference: string;
configuredProviderCount: number;
} = await res.json();

console.log(`${configuredProviderCount} provider(s) configured`);
console.log(`Anthropic: ${providers.anthropic.configured ? 'ready' : 'not configured'}`);
console.log(`OpenAI: ${providers.openai.configured ? 'ready' : 'not configured'}`);
console.log(`Routing: ${routingPreference}`);

// Warn about dual billing
if (providers.anthropic.dualBilling) {
console.warn('Both OAuth and API key active — may cause double billing');
}

Example response:

{
  "providers": {
    "anthropic": {
      "configured": true,
      "authMethod": "api-key",
      "hasKey": true,
      "dualBilling": false,
      "runtimes": [
        { "id": "claude-code", "name": "Claude Code", "status": "ready" },
        { "id": "anthropic-direct", "name": "Anthropic Direct", "status": "ready" }
      ]
    },
    "openai": {
      "configured": false,
      "hasKey": false,
      "runtimes": [
        { "id": "openai-codex-app-server", "name": "Codex", "status": "not_configured" }
      ]
    }
  },
  "routingPreference": "quality",
  "configuredProviderCount": 1
}

Get Anthropic Auth Settings

GET /api/settings

Read current Anthropic authentication settings (key presence, auth method, API key source).

Response Body

FieldTypeReqDescription
methodstring*Auth method (oauth, api-key)
hasKeyboolean*Whether an API key is stored
apiKeySourcestringKey origin (manual, oauth, env)
// Read current Anthropic auth settings
const res: Response = await fetch('http://localhost:3000/api/settings');
const auth: { method: string; hasKey: boolean; apiKeySource?: string } = await res.json();
console.log(`Auth method: ${auth.method}, key present: ${auth.hasKey}`);

Example response:

{
  "method": "api-key",
  "hasKey": true,
  "apiKeySource": "manual"
}

Update Anthropic Auth Settings

POST /api/settings

Update Anthropic authentication configuration. Validated with Zod.

Response 200 — Updated auth settings object

Set or rotate the Anthropic API key:

// Set or rotate the Anthropic API key
await fetch('http://localhost:3000/api/settings', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ apiKey: 'sk-ant-api03-...' }),
});

Errors: 400 — Zod validation failure

Get OpenAI Auth Settings

GET /api/settings/openai

Read current OpenAI authentication settings.

Response Body

FieldTypeReqDescription
hasKeyboolean*Whether an OpenAI API key is stored
apiKeySourcestringKey origin (manual, env)
// Read current OpenAI auth settings
const res: Response = await fetch('http://localhost:3000/api/settings/openai');
const openai: { hasKey: boolean; apiKeySource?: string } = await res.json();
console.log(`OpenAI key present: ${openai.hasKey}`);

Update OpenAI Auth Settings

POST /api/settings/openai

Update OpenAI authentication configuration. Validated with Zod.

Response 200 — Updated OpenAI auth settings object

// Configure OpenAI for Codex runtime access
await fetch('http://localhost:3000/api/settings/openai', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ apiKey: 'sk-proj-...' }),
});

Errors: 400 — Zod validation failure

Budget Guardrails

Get Budget Snapshot

GET /api/settings/budgets

Retrieve the current budget guardrail snapshot including limits and spend-to-date.

Check current spend against limits before queuing expensive tasks:

// Check budget before running a large task
const res: Response = await fetch('http://localhost:3000/api/settings/budgets');
const budget: {
dailyLimit: number; monthlyLimit: number;
dailySpend: number; monthlySpend: number;
alertThreshold: number;
} = await res.json();

console.log(`Daily: $${budget.dailySpend.toFixed(2)} / $${budget.dailyLimit.toFixed(2)}`);
console.log(`Monthly: $${budget.monthlySpend.toFixed(2)} / $${budget.monthlyLimit.toFixed(2)}`);

if (budget.dailySpend / budget.dailyLimit > 0.8) {
console.warn('Approaching daily budget limit');
}

Example response:

{
  "dailyLimit": 10.00,
  "monthlyLimit": 200.00,
  "dailySpend": 4.23,
  "monthlySpend": 87.50,
  "alertThreshold": 0.8,
  "updatedAt": "2026-04-03T12:00:00.000Z"
}

Update Budget Policy

POST /api/settings/budgets

Set budget guardrail policy (daily/monthly limits, alert thresholds). Validated with Zod.

Response 200 — Updated budget snapshot

Set conservative budget limits for a team environment:

// Set budget guardrails
await fetch('http://localhost:3000/api/settings/budgets', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
  dailyLimit: 10.00,
  monthlyLimit: 200.00,
  alertThreshold: 0.8, // warn at 80% usage
}),
});

Errors: 400 — Zod validation failure

Model Routing

Get Routing Preference

GET /api/settings/routing

Read the current model routing preference.

Response Body

FieldTypeReqDescription
preferenceenum*cost, latency, quality, or manual
// Read current routing preference
const res: Response = await fetch('http://localhost:3000/api/settings/routing');
const { preference }: { preference: string } = await res.json();
console.log(`Current routing: ${preference}`);

Set Routing Preference

POST /api/settings/routing

Update the model routing strategy.

Request Body

FieldTypeReqDescription
preferenceenum*One of: cost, latency, quality, manual

Response 200{ "preference": "<value>" }

Switch between routing strategies depending on your current needs:

// Switch to cost-optimized routing
await fetch('http://localhost:3000/api/settings/routing', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ preference: 'cost' }),
});

Errors: 400 — Invalid preference value

Pricing Registry

Get Pricing Snapshot

GET /api/settings/pricing

Return the current pricing registry snapshot with per-model token costs.

View token pricing for all available models — useful for cost estimation before execution:

// Check model pricing for cost estimation
const res: Response = await fetch('http://localhost:3000/api/settings/pricing');
const pricing: { models: Array<{ modelId: string; inputPer1M: number; outputPer1M: number }> } = await res.json();

pricing.models.forEach((m) => {
console.log(`${m.modelId}: $${m.inputPer1M}/M input, $${m.outputPer1M}/M output`);
});

Example response:

{
  "models": [
    { "modelId": "claude-sonnet-4-6-20250514", "inputPer1M": 3.00, "outputPer1M": 15.00 },
    { "modelId": "claude-haiku-4-20250414", "inputPer1M": 0.80, "outputPer1M": 4.00 }
  ],
  "updatedAt": "2026-04-03T00:00:00.000Z"
}

Refresh Pricing

POST /api/settings/pricing

Force a refresh of the pricing registry from upstream sources.

Response 200 — Refreshed pricing snapshot

// Force a pricing refresh after provider announcements
const res: Response = await fetch('http://localhost:3000/api/settings/pricing', { method: 'POST' });
const pricing: { models: Array<{ modelId: string }> } = await res.json();
console.log(`Pricing updated: ${pricing.models.length} models`);

Chat Settings

Get Chat Model

GET /api/settings/chat

Read the default chat model.

Response Body

FieldTypeReqDescription
defaultModelstring*Model ID used for new conversations
// Read the default chat model
const res: Response = await fetch('http://localhost:3000/api/settings/chat');
const { defaultModel }: { defaultModel: string } = await res.json();
console.log(`Chat model: ${defaultModel}`);

Set Chat Model

PUT /api/settings/chat

Update the default chat model.

Request Body

FieldTypeReqDescription
defaultModelstring*Valid model ID from the supported models list

Change the default model for new chat conversations:

// Switch the default chat model
await fetch('http://localhost:3000/api/settings/chat', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ defaultModel: 'claude-sonnet-4-6-20250514' }),
});

Errors: 400 — Invalid model ID

Runtime Settings

Get Runtime Config

GET /api/settings/runtime

Read SDK timeout and max-turns settings for agent execution.

Response Body

FieldTypeReqDescription
sdkTimeoutSecondsstring*SDK call timeout in seconds (default: 60)
maxTurnsstring*Maximum agent turns per execution (default: 10)
// Read SDK timeout and max-turns settings
const res: Response = await fetch('http://localhost:3000/api/settings/runtime');
const runtime: { sdkTimeoutSeconds: string; maxTurns: string } = await res.json();
console.log(`Timeout: ${runtime.sdkTimeoutSeconds}s, Max turns: ${runtime.maxTurns}`);

Update Runtime Config

POST /api/settings/runtime

Update SDK timeout and/or max-turns settings.

Request Body (all optional)

FieldTypeReqDescription
sdkTimeoutSecondsstringTimeout in seconds (10–300)
maxTurnsstringMax agent turns (1–50)

Increase timeout and turns for complex tasks that need more agent interaction:

// Increase limits for complex agent tasks
await fetch('http://localhost:3000/api/settings/runtime', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ sdkTimeoutSeconds: '120', maxTurns: '25' }),
});

Errors: 400 — Value out of range

Ollama Settings

Get Ollama Config

GET /api/settings/ollama

Read Ollama connection settings.

Response Body

FieldTypeReqDescription
baseUrlstring*Ollama server URL (default: http://localhost:11434)
defaultModelstring*Default model name (empty if not set)
// Read Ollama connection settings
const res: Response = await fetch('http://localhost:3000/api/settings/ollama');
const ollama: { baseUrl: string; defaultModel: string } = await res.json();
console.log(`Ollama: ${ollama.baseUrl}, model: ${ollama.defaultModel || '(not set)'}`);

Update Ollama Config

POST /api/settings/ollama

Update Ollama base URL and/or default model.

Request Body (all optional)

FieldTypeReqDescription
baseUrlstringOllama server URL
defaultModelstringDefault model name

Configure a local Ollama instance for offline agent execution:

// Point to a local Ollama instance
await fetch('http://localhost:3000/api/settings/ollama', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ baseUrl: 'http://localhost:11434', defaultModel: 'llama3.1' }),
});

Learning Context

Get Learning Settings

GET /api/settings/learning

Read the learning context character limit.

Response Body

FieldTypeReqDescription
contextCharLimitstring*Max characters for learning context (default: 8000)
// Read the learning context character limit
const res: Response = await fetch('http://localhost:3000/api/settings/learning');
const { contextCharLimit }: { contextCharLimit: string } = await res.json();
console.log(`Learning context limit: ${Number(contextCharLimit).toLocaleString()} chars`);

Update Learning Settings

POST /api/settings/learning

Update the learning context character limit.

Request Body

FieldTypeReqDescription
contextCharLimitstring*Limit in characters (2,000–32,000, step 1,000)

Increase the learning context window so agents retain more project-specific knowledge:

// Double the learning context window
await fetch('http://localhost:3000/api/settings/learning', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ contextCharLimit: '16000' }),
});

Errors: 400 — Value out of range or not a multiple of 1,000

Browser Tools

Get Browser Tool Settings

GET /api/settings/browser-tools

Read MCP browser tool enablement and configuration.

Response Body

FieldTypeReqDescription
chromeDevtoolsEnabledboolean*Whether Chrome DevTools MCP is enabled
playwrightEnabledboolean*Whether Playwright MCP is enabled
chromeDevtoolsConfigstring*Chrome DevTools MCP config (JSON string or empty)
playwrightConfigstring*Playwright MCP config (JSON string or empty)
// Read browser tool enablement
const res: Response = await fetch('http://localhost:3000/api/settings/browser-tools');
const tools: { chromeDevtoolsEnabled: boolean; playwrightEnabled: boolean } = await res.json();
console.log(`Chrome DevTools: ${tools.chromeDevtoolsEnabled ? 'on' : 'off'}`);
console.log(`Playwright: ${tools.playwrightEnabled ? 'on' : 'off'}`);

Update Browser Tool Settings

POST /api/settings/browser-tools

Enable/disable browser tools and update their MCP configurations.

Request Body (all optional)

FieldTypeReqDescription
chromeDevtoolsEnabledbooleanToggle Chrome DevTools MCP
playwrightEnabledbooleanToggle Playwright MCP
chromeDevtoolsConfigstringChrome DevTools MCP config JSON
playwrightConfigstringPlaywright MCP config JSON

Enable Chrome DevTools for agents that need browser automation:

// Enable Chrome DevTools MCP for browser-based tasks
await fetch('http://localhost:3000/api/settings/browser-tools', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
  chromeDevtoolsEnabled: true,
  playwrightEnabled: false,
}),
});

Get Web Search Settings

Update Web Search Settings

POST /api/settings/web-search

Enable or disable Exa web search MCP.

Request Body

FieldTypeReqDescription
exaSearchEnabledboolean*Toggle Exa Search MCP
// Enable web search for research-oriented agents
await fetch('http://localhost:3000/api/settings/web-search', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ exaSearchEnabled: true }),
});

Utility

Get Default Author

GET /api/settings/author-default

Return the OS username as the default task author.

Response Body

FieldTypeReqDescription
authorstring*OS username of the current user
// Get the OS username as default author
const res: Response = await fetch('http://localhost:3000/api/settings/author-default');
const { author }: { author: string } = await res.json();
console.log(`Default author: ${author}`);

Test Runtime Connection

POST /api/settings/test

Test connectivity to a specific agent runtime. Returns connection status and runtime capabilities.

Request Body

FieldTypeReqDescription
runtimestringRuntime ID to test (default: system default runtime)

Response Body

FieldTypeReqDescription
connectedboolean*Whether the runtime responded successfully
runtimestring*Runtime ID that was tested
capabilitiesobject*Runtime capability flags
errorstringError message if connection failed

Test connectivity before running tasks — helpful for diagnosing configuration issues:

// Test runtime connectivity
const res: Response = await fetch('http://localhost:3000/api/settings/test', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ runtime: 'claude-code' }),
});
const result: { connected: boolean; runtime: string; capabilities: Record<string, any>; error?: string } = await res.json();

if (result.connected) {
console.log(`${result.runtime} is ready`);
console.log('Capabilities:', result.capabilities);
} else {
console.error(`Connection failed: ${result.error}`);
}

Example response:

{
  "connected": true,
  "runtime": "claude-code",
  "capabilities": {
    "streaming": true,
    "toolUse": true,
    "maxTokens": 128000
  }
}

Routing Preferences

ValueDescription
costRoute to the cheapest available model that meets the task requirements.
latencyRoute to the fastest responding model.
qualityRoute to the highest capability model.
manualUser explicitly selects the model per task.