Skip to main content
Manage user-specific settings including display preferences, themes, AI model configuration, and API keys.

GET /api/user-settings

Retrieve current user settings.

Response

userId
string
User ID
displayName
string
User’s display name
theme
string
UI theme preference: "light", "dark", or "system"Default: "system"
colorTheme
string
Color theme nameDefault: "better-auth"
colorMode
string
Color mode: "light" or "dark"Default: "dark"
ghostModel
string
AI model for Ghost chatDefault: "auto"
useOwnApiKey
boolean
Whether user is using their own OpenRouter API keyDefault: false
openrouterApiKey
string
Masked OpenRouter API key (last 4 characters visible)Returns null if not set, or "****XXXX" format if set
githubPat
string
Masked GitHub Personal Access Token (last 4 characters visible)Returns null if not set, or "****XXXX" format if set
codeThemeLight
string
Code syntax theme for light modeDefault: "vitesse-light"
codeThemeDark
string
Code syntax theme for dark modeDefault: "vitesse-black"
codeFont
string
Code font familyDefault: "default"
codeFontSize
number
Code font size in pixelsDefault: 13, Range: 8-32
onboardingDone
boolean
Whether user has completed onboardingDefault: false
updatedAt
string
Last update timestamp (ISO 8601)
const response = await fetch('/api/user-settings');
const settings = await response.json();

console.log(settings);
/*
{
  userId: "user_123",
  displayName: "John Doe",
  theme: "dark",
  colorTheme: "better-auth",
  colorMode: "dark",
  ghostModel: "auto",
  useOwnApiKey: false,
  openrouterApiKey: null,
  githubPat: "****abcd",
  codeThemeLight: "vitesse-light",
  codeThemeDark: "vitesse-black",
  codeFont: "default",
  codeFontSize: 13,
  onboardingDone: true,
  updatedAt: "2024-01-15T10:30:00Z"
}
*/
API keys are always masked in responses. To update them, you must provide the full key value in PATCH requests.

PATCH /api/user-settings

Update user settings. Only provided fields will be updated.
displayName
string
User’s display name (max 100 characters)
theme
string
UI theme: "light", "dark", or "system"
colorTheme
string
Color theme name (max 50 characters)
colorMode
string
Color mode: "light" or "dark"
ghostModel
string
AI model identifier (max 100 characters)Examples: "auto", "gpt-4", "claude-3-opus"
useOwnApiKey
boolean
Enable/disable using own OpenRouter API key
openrouterApiKey
string
OpenRouter API key (max 500 characters)Set to null to remove the key
githubPat
string
GitHub Personal Access Token (max 500 characters)Set to null to remove the token
codeThemeLight
string
Code theme for light mode (max 100 characters)
codeThemeDark
string
Code theme for dark mode (max 100 characters)
codeFont
string
Code font family (max 100 characters)
codeFontSize
number
Code font size in pixels (8-32)
onboardingDone
boolean
Mark onboarding as complete

Response

Returns the updated settings object with the same structure as GET.
const response = await fetch('/api/user-settings', {
  method: 'PATCH',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    theme: 'dark',
    colorMode: 'dark',
    codeThemeDark: 'github-dark'
  })
});

const updated = await response.json();
console.log('Theme updated:', updated.theme);

Settings Validation

Field Constraints

FieldTypeMinMaxDefault
displayNamestring-100 chars-
themeenum--“system”
colorThemestring-50 chars”better-auth”
colorModeenum--“dark”
ghostModelstring-100 chars”auto”
openrouterApiKeystring-500 charsnull
githubPatstring-500 charsnull
codeThemeLightstring-100 chars”vitesse-light”
codeThemeDarkstring-100 chars”vitesse-black”
codeFontstring-100 chars”default”
codeFontSizenumber83213

Theme Values

UI Theme (theme):
  • "light" - Always light mode
  • "dark" - Always dark mode
  • "system" - Follow system preference
Color Mode (colorMode):
  • "light" - Light color palette
  • "dark" - Dark color palette

AI Model Configuration

Ghost Model (ghostModel):
  • "auto" - Automatic model selection based on task
  • Specific model IDs (e.g., "gpt-4", "claude-3-opus")
When useOwnApiKey is true, the specified ghostModel will be used with the user’s openrouterApiKey.

Usage Examples

Theme Switcher

async function setTheme(theme: 'light' | 'dark' | 'system') {
  const response = await fetch('/api/user-settings', {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ theme })
  });
  
  if (response.ok) {
    // Apply theme to document
    document.documentElement.setAttribute('data-theme', theme);
  }
}

Settings Form

interface SettingsFormData {
  displayName: string;
  theme: 'light' | 'dark' | 'system';
  codeFont: string;
  codeFontSize: number;
}

async function saveSettings(data: SettingsFormData) {
  try {
    const response = await fetch('/api/user-settings', {
      method: 'PATCH',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    });
    
    if (!response.ok) {
      const error = await response.json();
      throw new Error(error.error);
    }
    
    const updated = await response.json();
    console.log('Settings saved:', updated);
    return updated;
  } catch (error) {
    console.error('Failed to save settings:', error);
    throw error;
  }
}

API Key Management

async function configureCustomApiKey(apiKey: string | null) {
  const response = await fetch('/api/user-settings', {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      useOwnApiKey: apiKey !== null,
      openrouterApiKey: apiKey
    })
  });
  
  if (!response.ok) {
    throw new Error('Failed to update API key');
  }
  
  return response.json();
}

// Enable custom key
await configureCustomApiKey('sk-or-v1-...');

// Disable custom key
await configureCustomApiKey(null);

Code Editor Settings

interface CodeEditorSettings {
  font: string;
  fontSize: number;
  themeLight: string;
  themeDark: string;
}

async function updateCodeEditor(settings: CodeEditorSettings) {
  const response = await fetch('/api/user-settings', {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      codeFont: settings.font,
      codeFontSize: settings.fontSize,
      codeThemeLight: settings.themeLight,
      codeThemeDark: settings.themeDark
    })
  });
  
  return response.json();
}

await updateCodeEditor({
  font: 'Fira Code',
  fontSize: 14,
  themeLight: 'github-light',
  themeDark: 'dracula'
});

Onboarding Flow

async function completeOnboarding(initialSettings: Partial<UserSettings>) {
  const response = await fetch('/api/user-settings', {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      ...initialSettings,
      onboardingDone: true
    })
  });
  
  if (response.ok) {
    // Redirect to main app
    window.location.href = '/dashboard';
  }
}

await completeOnboarding({
  displayName: 'John Doe',
  theme: 'dark',
  ghostModel: 'gpt-4'
});