Settings API
Settings control every aspect of ainative-business’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
/api/settings/providers Aggregated provider status including configured runtimes, auth methods, dual-billing detection, and routing preference.
Response Body
| Field | Type | Req | Description |
|---|---|---|---|
| providers.anthropic.configured | boolean | * | Whether any Anthropic runtime is configured |
| providers.anthropic.authMethod | string | * | Current auth method (oauth, api-key, etc.) |
| providers.anthropic.hasKey | boolean | * | Whether an API key is present |
| providers.anthropic.dualBilling | boolean | * | True when both OAuth and API key are active |
| providers.anthropic.runtimes | object[] | * | Runtime setup states for Claude Code and Anthropic Direct |
| providers.openai.configured | boolean | * | Whether any OpenAI runtime is configured |
| providers.openai.hasKey | boolean | * | Whether an OpenAI API key is present |
| providers.openai.runtimes | object[] | * | Runtime setup states for Codex and OpenAI Direct |
| routingPreference | enum | * | cost, latency, quality, or manual |
| configuredProviderCount | number | * | 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
/api/settings Read current Anthropic authentication settings (key presence, auth method, API key source).
Response Body
| Field | Type | Req | Description |
|---|---|---|---|
| method | string | * | Auth method (oauth, api-key) |
| hasKey | boolean | * | Whether an API key is stored |
| apiKeySource | string | — | Key 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
/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
/api/settings/openai Read current OpenAI authentication settings.
Response Body
| Field | Type | Req | Description |
|---|---|---|---|
| hasKey | boolean | * | Whether an OpenAI API key is stored |
| apiKeySource | string | — | Key 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
/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
/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
/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
/api/settings/routing Read the current model routing preference.
Response Body
| Field | Type | Req | Description |
|---|---|---|---|
| preference | enum | * | 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
/api/settings/routing Update the model routing strategy.
Request Body
| Field | Type | Req | Description |
|---|---|---|---|
| preference | enum | * | 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
/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
/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
/api/settings/chat Read the default chat model.
Response Body
| Field | Type | Req | Description |
|---|---|---|---|
| defaultModel | string | * | 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
/api/settings/chat Update the default chat model.
Request Body
| Field | Type | Req | Description |
|---|---|---|---|
| defaultModel | string | * | 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
/api/settings/runtime Read SDK timeout and max-turns settings for agent execution.
Response Body
| Field | Type | Req | Description |
|---|---|---|---|
| sdkTimeoutSeconds | string | * | SDK call timeout in seconds (default: 60) |
| maxTurns | string | * | 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
/api/settings/runtime Update SDK timeout and/or max-turns settings.
Request Body (all optional)
| Field | Type | Req | Description |
|---|---|---|---|
| sdkTimeoutSeconds | string | — | Timeout in seconds (10–300) |
| maxTurns | string | — | Max 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
/api/settings/ollama Read Ollama connection settings.
Response Body
| Field | Type | Req | Description |
|---|---|---|---|
| baseUrl | string | * | Ollama server URL (default: http://localhost:11434) |
| defaultModel | string | * | 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
/api/settings/ollama Update Ollama base URL and/or default model.
Request Body (all optional)
| Field | Type | Req | Description |
|---|---|---|---|
| baseUrl | string | — | Ollama server URL |
| defaultModel | string | — | Default 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
/api/settings/learning Read the learning context character limit.
Response Body
| Field | Type | Req | Description |
|---|---|---|---|
| contextCharLimit | string | * | 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
/api/settings/learning Update the learning context character limit.
Request Body
| Field | Type | Req | Description |
|---|---|---|---|
| contextCharLimit | string | * | 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
/api/settings/browser-tools Read MCP browser tool enablement and configuration.
Response Body
| Field | Type | Req | Description |
|---|---|---|---|
| chromeDevtoolsEnabled | boolean | * | Whether Chrome DevTools MCP is enabled |
| playwrightEnabled | boolean | * | Whether Playwright MCP is enabled |
| chromeDevtoolsConfig | string | * | Chrome DevTools MCP config (JSON string or empty) |
| playwrightConfig | string | * | 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
/api/settings/browser-tools Enable/disable browser tools and update their MCP configurations.
Request Body (all optional)
| Field | Type | Req | Description |
|---|---|---|---|
| chromeDevtoolsEnabled | boolean | — | Toggle Chrome DevTools MCP |
| playwrightEnabled | boolean | — | Toggle Playwright MCP |
| chromeDevtoolsConfig | string | — | Chrome DevTools MCP config JSON |
| playwrightConfig | string | — | Playwright 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,
}),
}); Web Search
Get Web Search Settings
/api/settings/web-search Read Exa web search MCP enablement.
Response Body
| Field | Type | Req | Description |
|---|---|---|---|
| exaSearchEnabled | boolean | * | Whether Exa Search MCP is enabled |
// Read Exa web search enablement
const res: Response = await fetch('http://localhost:3000/api/settings/web-search');
const { exaSearchEnabled }: { exaSearchEnabled: boolean } = await res.json();
console.log(`Web search: ${exaSearchEnabled ? 'enabled' : 'disabled'}`); Update Web Search Settings
/api/settings/web-search Enable or disable Exa web search MCP.
Request Body
| Field | Type | Req | Description |
|---|---|---|---|
| exaSearchEnabled | boolean | * | 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
Test Runtime Connection
/api/settings/test Test connectivity to a specific agent runtime. Returns connection status and runtime capabilities.
Request Body
| Field | Type | Req | Description |
|---|---|---|---|
| runtime | string | — | Runtime ID to test (default: system default runtime) |
Response Body
| Field | Type | Req | Description |
|---|---|---|---|
| connected | boolean | * | Whether the runtime responded successfully |
| runtime | string | * | Runtime ID that was tested |
| capabilities | object | * | Runtime capability flags |
| error | string | — | Error 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
}
} Chat Pins
Pinned entities appear at the top of the chat mention popover so frequently-used tasks, projects, documents, and profiles are always one click away. Pins store a denormalized label and status snapshot so the popover renders without a round-trip — the canonical entity is still resolved by ID when selected.
Get Chat Pins
/api/settings/chat/pins Return the current list of pinned chat mention entries.
Response Body
| Field | Type | Req | Description |
|---|---|---|---|
| pins | object[] | * | Array of pinned entity entries |
| pins[].id | string | * | Entity UUID |
| pins[].type | enum | * | Entity type: task, project, workflow, document, schedule, table, or profile |
| pins[].label | string | * | Display label (denormalized from the entity at pin time) |
| pins[].description | string | — | Short description snapshot |
| pins[].status | string | — | Entity status snapshot |
| pins[].pinnedAt | ISO 8601 | * | Timestamp when the entity was pinned |
// Load pinned entities to pre-populate the mention popover
const res: Response = await fetch('http://localhost:3000/api/settings/chat/pins');
const { pins }: { pins: Array<{ id: string; type: string; label: string; pinnedAt: string }> } = await res.json();
console.log(`${pins.length} pinned entities`);
pins.forEach((p) => console.log(` [${p.type}] ${p.label}`)); Example response:
{
"pins": [
{
"id": "task-9d4e-a1b2",
"type": "task",
"label": "Analyze Q4 revenue trends",
"status": "completed",
"pinnedAt": "2026-04-10T09:00:00.000Z"
},
{
"id": "proj-8f3a-4b2c",
"type": "project",
"label": "Marketing Analysis",
"description": "Quarterly marketing data review",
"pinnedAt": "2026-04-08T14:30:00.000Z"
}
]
} Update Chat Pins
/api/settings/chat/pins Replace the full list of pinned chat mention entries. The client is the source of truth — read-modify-write on the client side, then PUT the updated array. Duplicate IDs are de-duplicated server-side (last write wins for pinnedAt).
Request Body
| Field | Type | Req | Description |
|---|---|---|---|
| pins | object[] | * | Complete replacement list of pinned entries |
| pins[].id | string | * | Entity UUID |
| pins[].type | enum | * | Entity type: task, project, workflow, document, schedule, table, or profile |
| pins[].label | string | * | Display label |
| pins[].description | string | — | Short description |
| pins[].status | string | — | Entity status |
| pins[].pinnedAt | ISO 8601 | * | Pin timestamp |
Response 200 — De-duplicated pins array
Add a new task to the pinned list — fetch current pins first, append, then PUT:
// Pin a new task: read → append → write
const existing = await fetch('http://localhost:3000/api/settings/chat/pins')
.then((r: Response) => r.json());
const updated = [
...existing.pins,
{
id: 'task-c3d4-e5f6',
type: 'task',
label: 'Deploy v2.1 release',
status: 'queued',
pinnedAt: new Date().toISOString(),
},
];
await fetch('http://localhost:3000/api/settings/chat/pins', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ pins: updated }),
}); Errors: 400 — Invalid JSON body or Zod validation failure
Chat Saved Searches
Saved searches let users store named filter combinations for the chat popover and ⌘K palette. Each search is scoped to a surface (task, project, workflow, etc.) and stores the raw filter input string so it can be replayed instantly.
Get Saved Searches
/api/settings/chat/saved-searches Return the current list of saved search filter combinations.
Response Body
| Field | Type | Req | Description |
|---|---|---|---|
| searches | object[] | * | Array of saved search entries |
| searches[].id | string | * | Search entry UUID |
| searches[].surface | enum | * | Search surface: task, project, workflow, document, skill, or profile |
| searches[].label | string | * | Display name for the saved search (max 120 chars) |
| searches[].filterInput | string | * | Stored filter string to replay (max 500 chars) |
| searches[].createdAt | ISO 8601 | * | Timestamp when the search was saved |
// Load saved searches for the ⌘K palette
const res: Response = await fetch('http://localhost:3000/api/settings/chat/saved-searches');
const { searches }: { searches: Array<{ id: string; surface: string; label: string; filterInput: string }> } = await res.json();
console.log(`${searches.length} saved searches`);
searches.forEach((s) => console.log(` [${s.surface}] ${s.label}: "${s.filterInput}"`)); Example response:
{
"searches": [
{
"id": "srch-1a2b-3c4d",
"surface": "task",
"label": "Running tasks",
"filterInput": "status:running",
"createdAt": "2026-04-09T11:00:00.000Z"
},
{
"id": "srch-5e6f-7g8h",
"surface": "document",
"label": "Design specs",
"filterInput": "design spec",
"createdAt": "2026-04-07T16:45:00.000Z"
}
]
} Update Saved Searches
/api/settings/chat/saved-searches Replace the full list of saved searches. Duplicate IDs are de-duplicated server-side. Malformed stored values recover to an empty list rather than erroring.
Request Body
| Field | Type | Req | Description |
|---|---|---|---|
| searches | object[] | * | Complete replacement list of saved searches |
| searches[].id | string | * | Search entry UUID |
| searches[].surface | enum | * | Surface: task, project, workflow, document, skill, or profile |
| searches[].label | string | * | Display name (max 120 chars) |
| searches[].filterInput | string | * | Filter string (max 500 chars) |
| searches[].createdAt | ISO 8601 | * | Creation timestamp |
Response 200 — De-duplicated searches array
Save a new filter combination — read first, append the new search, then PUT:
import { v4 as uuidv4 } from 'uuid';
// Save a new search: read → append → write
const existing = await fetch('http://localhost:3000/api/settings/chat/saved-searches')
.then((r: Response) => r.json());
const updated = [
...existing.searches,
{
id: uuidv4(),
surface: 'task',
label: 'P0 tasks this week',
filterInput: 'priority:0 status:queued',
createdAt: new Date().toISOString(),
},
];
await fetch('http://localhost:3000/api/settings/chat/saved-searches', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ searches: updated }),
}); Errors: 400 — Invalid JSON body or Zod validation failure
OpenAI OAuth
ainative-business supports both API-key and OAuth authentication for OpenAI. The login/logout routes manage the OAuth browser-based flow for the ChatGPT/Codex App Server integration.
Get OpenAI Login State
/api/settings/openai/login Return the current OpenAI OAuth login state — useful for polling until the browser-based flow completes.
Response Body
| Field | Type | Req | Description |
|---|---|---|---|
| status | enum | * | Login state: idle, pending, success, or error |
| loginUrl | string | — | OAuth URL to open in a browser (present while pending) |
| error | string | — | Error message if the flow failed |
// Poll the login state while the browser flow is in progress
const res: Response = await fetch('http://localhost:3000/api/settings/openai/login');
const state: { status: string; loginUrl?: string; error?: string } = await res.json();
if (state.status === 'pending' && state.loginUrl) {
console.log(`Open in browser: ${state.loginUrl}`);
} else if (state.status === 'success') {
console.log('OpenAI OAuth login complete');
} else if (state.status === 'error') {
console.error(`Login failed: ${state.error}`);
} Start OpenAI OAuth Login
/api/settings/openai/login Initiate the OpenAI ChatGPT OAuth browser flow. Sets the auth method to 'oauth' and returns a login URL to open in a browser. Poll GET /api/settings/openai/login to track completion.
Response 200 — Login state object with loginUrl
// Kick off the OAuth flow — open the returned URL in a browser
const res: Response = await fetch('http://localhost:3000/api/settings/openai/login', {
method: 'POST',
});
const state: { status: string; loginUrl?: string } = await res.json();
if (state.loginUrl) {
// Open the URL in the user's default browser
console.log(`Navigate to: ${state.loginUrl}`);
// Then poll GET /api/settings/openai/login until status === "success"
} Cancel OpenAI OAuth Login
/api/settings/openai/login Cancel an in-progress OpenAI OAuth login flow and reset the login state to idle.
Response 200 — Updated login state object with status: "idle"
// Cancel the pending OAuth flow (e.g. user closed the dialog)
const res: Response = await fetch('http://localhost:3000/api/settings/openai/login', {
method: 'DELETE',
});
const state: { status: string } = await res.json();
console.log(`Login state: ${state.status}`); // "idle" Logout OpenAI (Codex)
/api/settings/openai/logout Log out of the OpenAI Codex App Server session and clear stored Codex credentials.
Response 200 — { "success": true }
// Sign out of the Codex App Server session
await fetch('http://localhost:3000/api/settings/openai/logout', { method: 'POST' });
console.log('Codex session cleared'); Environment Settings
Get Environment Settings
/api/settings/environment Read environment-level settings such as the auto-promote skills flag.
Response Body
| Field | Type | Req | Description |
|---|---|---|---|
| autoPromoteSkills | boolean | * | Whether agent-generated skills are automatically promoted to the skills library |
// Check whether auto-promote is enabled
const res: Response = await fetch('http://localhost:3000/api/settings/environment');
const { autoPromoteSkills }: { autoPromoteSkills: boolean } = await res.json();
console.log(`Auto-promote skills: ${autoPromoteSkills ? 'on' : 'off'}`); Example response:
{
"autoPromoteSkills": false
} Update Environment Settings
/api/settings/environment Update environment-level settings. Only provided fields are updated.
Request Body (all optional)
| Field | Type | Req | Description |
|---|---|---|---|
| autoPromoteSkills | boolean | — | Toggle automatic skill promotion |
Response 200 — Updated environment settings object
Enable automatic skill promotion so agents can contribute reusable skills without manual approval:
// Enable auto-promote so agent-generated skills are added to the library automatically
await fetch('http://localhost:3000/api/settings/environment', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ autoPromoteSkills: true }),
}); Routing Preferences
| Value | Description |
|---|---|
| cost | Route to the cheapest available model that meets the task requirements. |
| latency | Route to the fastest responding model. |
| quality | Route to the highest capability model. |
| manual | User explicitly selects the model per task. |