Skip to main content

Overview

The Kuest admin panel provides comprehensive tools for managing your prediction market platform. Access it at /admin (requires admin wallet authorization).

Admin Dashboard Structure

// Admin layout from src/app/[locale]/admin/layout.tsx
<AdminLayout>
  <AdminHeader />  {/* Top navigation */}
  <AdminSidebar /> {/* Left navigation menu */}
  <Content />      {/* Main content area */}
</AdminLayout>
The admin sidebar provides access to all management sections:
  • General Settings: Platform identity and integrations
  • Theme Settings: Visual customization
  • Locales: Multi-language configuration
  • Affiliate Program: Referral system management
  • Market Context: AI-powered market insights
  • Users: User management and statistics
  • Events: Market visibility and sync control
  • Categories: Tag and category management
  • Create Event: Market creation interface

Admin Features

General Settings

Configure core platform settings at /admin (general settings page).

Brand Identity

1

Company Logo

Upload your platform logo:Supported formats:
  • SVG (recommended, scalable)
  • PNG, JPG, WebP (raster images)
Logo modes:
  • image: Use uploaded raster image
  • svg: Use SVG code (stored in database)
// Logo configuration
interface ThemeSiteSettings {
  logoMode: 'image' | 'svg'
  logoSvg: string           // SVG code
  logoImagePath: string     // S3/Supabase storage path
  logoImageUrl: string      // Public URL
}
2

Company Name & Description

Set your platform identity:
  • Company Name: Displayed in headers, wallet dialogs (max 80 chars)
  • Company Description: Used in metadata, SEO (max 180 chars)
siteName: "Your Market Platform"
siteDescription: "Decentralized prediction markets powered by Kuest"
3

PWA Icons

Configure Progressive Web App icons for mobile installation:
  • Icon 192x192: Standard app icon
  • Icon 512x512: High-resolution icon
Used by browser install prompts and home screen shortcuts.

Community and Analytics

Track platform usage with Google Analytics 4:
googleAnalyticsId: "G-XXXXXXXXXX"
The platform automatically initializes GA4 when configured.
Link to your Discord server:
discordLink: "https://discord.gg/your-invite"
Displayed in navigation and footer.

OpenRouter Integration

Configure AI-powered features:
// From AdminGeneralSettingsForm.tsx
interface OpenRouterSettings {
  apiKey: string              // OpenRouter API key
  model: string               // Preferred model (e.g., "perplexity/sonar")
  isApiKeyConfigured: boolean // Key is set
  modelOptions: Array<{
    id: string
    label: string
    contextWindow?: number
  }>
}
Features powered by OpenRouter:
  • AI-generated resolution rules
  • Automatic event translations
  • Market context generation
  • Content validation
1

Get API Key

Generate an API key at openrouter.ai/settings/keys
2

Configure Key

Enter your API key in the General Settings form. The platform never displays the full key after saving (shows ••••••••••••••••).
3

Select Model

Choose your preferred model:
  • Let OpenRouter decide: Automatic selection (default)
  • Specific model: Choose from available models
Recommended: Models with live browsing (e.g., perplexity/sonar) for best market context generation.
4

Refresh Models

Click the refresh button to fetch the latest available models from OpenRouter.

LI.FI Integration

Configure cross-chain swap and bridge functionality:
interface LiFiSettings {
  lifiIntegrator: string  // Your app ID from li.fi
  lifiApiKey: string      // API key (optional, increases rate limits)
}
Default Limits:
  • Without API key: 200 requests per 2 hours
  • With API key: 200 requests per minute (rolling 2-hour window)
Generate credentials at li.fi.

Market and Fee Settings

Set the Polygon wallet address to receive transaction fees:
feeRecipientWallet: "0xYourPolygonAddress"
This address receives platform fees from market trades.
Configure which wallet addresses can create markets:
// One address per line
marketCreators: `
0xCreator1Address
0xCreator2Address
0xCreator3Address
`
Important: Markets from these addresses only appear on your fork. Leave empty to show only main Kuest markets.

User Management

Manage platform users at /admin/users.

User Table

View and filter users:
// User data structure
interface User {
  id: string
  address: string           // EVM wallet address
  username: string | null
  email: string | null
  email_verified: boolean
  is_admin: boolean
  created_at: Date
  updated_at: Date
  // Trading statistics
  total_volume?: number
  total_trades?: number
}
Available Actions:
  • Search by address or username
  • Filter by admin status
  • Sort by registration date, volume, trades
  • View user details

User Statistics

The table displays:
  • Total registered users
  • Active traders (users with trades)
  • Total trading volume
  • Admin user count

Event Management

Control market visibility and sync settings at /admin/events.

Event Table Features

Visibility Control

Show or hide events from the platform:
await updateEventVisibilityAction(eventId, isHidden)
Hidden events are not visible to users but remain in the database.

Sync Settings

Configure automatic event syncing:
await updateEventSyncSettings(eventId, {
  autoSync: boolean,
  syncInterval: number
})

Sports Finalization

Mark sports events as final:
await updateEventSportsFinalState(eventId, isFinal)
Prevents further updates to sports market scores.

Livestream URL

Add livestream links to events:
await updateEventLivestreamUrl(eventId, url)
Displayed on event pages for user engagement.

Auto-Deploy New Events

Toggle automatic deployment of synced events:
const autoDeployNewEventsEnabled = await loadAutoDeployNewEventsEnabled()

// When enabled, newly synced events from Kuest API
// are automatically made visible on your platform

Event Filters

Filter by:
  • Main category (Politics, Sports, Crypto, etc.)
  • Status (active, resolved, archived, draft)
  • Visibility (hidden/visible)
  • Date range
Search:
  • Event title
  • Event slug
  • Market questions

Category Management

Manage tags and categories at /admin/categories.

Category Operations

1

View Categories

The table displays:
  • Category name and slug
  • Associated main category
  • Event count
  • Creation date
2

Update Categories

Edit category properties:
await updateCategory(categoryId, {
  name: string,
  slug: string,
  mainCategorySlug: string
})
3

Manage Translations

Add category translations for internationalization:
await updateCategoryTranslations(categoryId, {
  en: "English Name",
  es: "Nombre en Español",
  fr: "Nom en Français"
})

Main Categories

Main categories organize events into broad topics:
interface MainCategory {
  id: number
  name: string
  slug: string
  childs: Array<{  // Subcategories
    name: string
    slug: string
  }>
}
Fetch via API: GET /admin/api/main-tags

Theme Settings

Customize platform appearance at /admin/theme. Customization Options:
  • Color schemes
  • Typography
  • Layout spacing
  • Component styling
  • Dark/light mode defaults

Locale Configuration

Manage supported languages at /admin/locales. Features:
  • Enable/disable languages
  • Set default locale
  • Configure locale-specific settings
  • Manage translations

Affiliate Program

Configure referral system at /admin/affiliate. Settings:
  • Commission rates
  • Payout thresholds
  • Referral tracking
  • Affiliate dashboard access

Market Context Settings

Configure AI-powered market insights at /admin/market-context.
interface MarketContextSettings {
  apiKey: string          // OpenRouter API key
  model: string           // AI model to use
  systemPrompt: string    // Context generation prompt
  maxTokens: number       // Response length limit
}
Market context provides users with:
  • Market background information
  • Historical data
  • Related events
  • AI-generated insights

Admin API Endpoints

The admin panel uses dedicated API routes:
// User management
GET  /admin/api/users
POST /admin/api/users/[id]/update

// Event management
GET  /admin/api/events
POST /admin/api/events/check-slug

// Category management
GET  /admin/api/categories
GET  /admin/api/main-tags

// Market creation
GET  /admin/api/create-event/allowed-creators
POST /admin/api/create-event/ai

// OpenRouter integration
POST /admin/api/openrouter-models

Data Tables

Admin tables use a consistent pattern:
// Table component structure
<DataTable
  columns={columns}        // Column definitions
  data={data}             // Table data
  searchPlaceholder="..." // Search field
  filters={filters}       // Filter controls
  pagination={pagination} // Page controls
/>

// Column definition example
const columns = [
  {
    accessorKey: 'title',
    header: 'Event Title',
    cell: ({ row }) => (
      <a href={`/event/${row.original.slug}`}>
        {row.original.title}
      </a>
    ),
  },
  {
    accessorKey: 'status',
    header: 'Status',
    filterFn: 'equals',
  },
]
Common Features:
  • Sortable columns
  • Searchable fields
  • Filterable values
  • Pagination controls
  • Bulk actions
  • Export capabilities

Server Actions

Admin operations use Next.js server actions:
// Example: Update event visibility
'use server'

export async function updateEventVisibilityAction(
  eventId: string,
  isHidden: boolean
): Promise<UpdateEventVisibilityResult> {
  try {
    // 1. Verify admin access
    const currentUser = await UserRepository.getCurrentUser()
    if (!currentUser || !currentUser.is_admin) {
      return {
        success: false,
        error: 'Unauthorized. Admin access required.'
      }
    }

    // 2. Update database
    const { data, error } = await EventRepository.setEventHiddenState(
      eventId,
      isHidden
    )
    if (error || !data) {
      return { success: false, error: error ?? 'Update failed' }
    }

    // 3. Revalidate caches
    revalidatePath('/[locale]/admin/events', 'page')
    updateTag(cacheTags.eventsGlobal)
    updateTag(cacheTags.event(data.slug))

    return { success: true, data }
  } catch (error) {
    console.error('Server action error:', error)
    return { success: false, error: 'Internal server error' }
  }
}
Action Pattern:
  1. Authenticate request
  2. Validate input
  3. Perform database operation
  4. Invalidate relevant caches
  5. Return result

Security and Authorization

Admin Access Control

Admin access is controlled via environment variable:
# .env
ADMIN_WALLETS="0xAddress1,0xAddress2,0xAddress3"
// Authorization check
const currentUser = await UserRepository.getCurrentUser()
const isAdmin = currentUser?.is_admin === true

if (!isAdmin) {
  return { error: 'Unauthorized. Admin access required.' }
}

Route Protection

All admin routes are protected:
// Middleware checks admin status
export async function middleware(request: Request) {
  const user = await getCurrentUser()
  
  if (request.url.startsWith('/admin')) {
    if (!user?.is_admin) {
      return Response.redirect('/login')
    }
  }
  
  return NextResponse.next()
}

Best Practices

Regular Backups

Back up your database regularly, especially before bulk operations.

Test Changes

Test admin actions on development/staging before production.

Monitor Actions

Review admin action logs in Sentry or your monitoring tool.

Limit Admin Access

Only add trusted wallet addresses to ADMIN_WALLETS.

Troubleshooting

Cannot Access Admin Panel

Issue: /admin redirects to home page. Solution:
  1. Verify your wallet address is in ADMIN_WALLETS environment variable
  2. Ensure you’re connected with the correct wallet
  3. Check that your user record has is_admin = true in the database
  4. Clear browser cache and reconnect wallet

Settings Not Saving

Issue: Form submits but changes don’t persist. Solution:
  1. Check browser console for errors
  2. Verify database connection
  3. Check server action logs
  4. Ensure proper permissions on storage (S3/Supabase)
  5. Review Sentry error logs

Slow Table Loading

Issue: Admin tables take a long time to load. Solution:
  1. Add database indexes on frequently queried columns
  2. Implement pagination (already built-in)
  3. Use table filters to reduce result size
  4. Cache frequently accessed data
  5. Optimize database queries

Image Upload Fails

Issue: Logo or icon upload returns an error. Solution:
  1. Check file size (recommended: < 2MB)
  2. Verify file format (PNG, JPG, WebP, SVG)
  3. Ensure S3/Supabase credentials are correct
  4. Check storage bucket permissions
  5. Verify S3_PUBLIC_URL or SUPABASE_URL is accessible

Market Creation

Create and manage prediction markets

Monitoring

Track platform health and performance

Environment Config

Configure environment variables

Database Schema

Understand the database structure

Build docs developers (and LLMs) love