Codapult
PricingPluginsDocs
Codapult

The SaaS Boilerplate for Builders

Product

  • Pricing
  • Plugins
  • Documentation

Company

  • Contact
  • GitHub

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Codapult. All rights reserved.

All articles

Getting Started

  • Introduction
  • Quick Start
  • Project Structure

Configuration

  • Environment Variables
  • App Configuration

Authentication

  • Authentication
  • OAuth Providers
  • Two-Factor & Passwordless
  • Enterprise SSO (SAML)

Database

  • Database
  • Migrations

Teams

  • Teams & Organizations
  • Permissions & RBAC

Payments

  • Payments & Billing
  • Stripe Setup
  • LemonSqueezy Setup
  • Polar Setup
  • Payment Webhooks

Api

  • API Layer
  • tRPC
  • GraphQL

Ai

  • AI Features

Email

  • Email
  • Email Templates

Infrastructure

  • Infrastructure
  • File Storage
  • Background Jobs

Ui

  • UI & Theming

I18n

  • Internationalization

Content Management

  • Content Management

Admin

  • Admin Panel

Security

  • Security

Monitoring

  • Analytics & Monitoring

Modules

  • Module Architecture

Plugins

  • Plugin System
  • AI Kit Plugin
  • CRM Plugin
  • Helpdesk Plugin
  • Email Marketing Plugin

Deployment

  • Deployment
  • Troubleshooting

Upgrading

  • Upgrading Codapult

Developer Tools

  • MCP Server
  • Testing
Configuration

App Configuration

Customize your brand, features, navigation, pricing, and AI settings through Codapult's configuration files.

Codapult's behavior is controlled through a small set of TypeScript configuration files. These are the files you'll edit most when making the product your own.

FilePurpose
src/config/app.tsBrand, feature toggles, auth, AI, payments, company links
src/config/navigation.tsDashboard and admin sidebar items
src/config/marketing.tsLanding page pricing, testimonials, stats, competitor comparison
src/lib/config.tsTyped env var access (hand-maintained Zod schema)

App identity

Edit src/config/app.ts to set your product identity:

const appUrl = process.env.NEXT_PUBLIC_APP_URL ?? 'http://localhost:3000';

export const appConfig: AppConfig = {
  appUrl,
  serverUrl: process.env.VERCEL_URL ? `https://${process.env.VERCEL_URL}` : appUrl,
  appName: process.env.NEXT_PUBLIC_APP_NAME ?? 'my-app',
  brand: {
    name: 'My App',
    description: 'The best project management tool for teams',
    logo: '/logo.svg',
    favicon: '/favicon.ico',
  },
  // ...
};
PropertySourceDescription
appUrlNEXT_PUBLIC_APP_URL env varCanonical public URL (auth callbacks, emails, SEO). Defaults to http://localhost:3000 for dev.
serverUrlVERCEL_URL → appUrlSSR self-reference URL for server-side fetches (tRPC). On Vercel, uses the deployment URL; otherwise falls back to appUrl.
appNameNEXT_PUBLIC_APP_NAME env varTechnical project name (lowercase). Used for OTEL service name and machine identifiers.
brand.nameHardcoded in sourceDisplay name (proper case). Shown in sidebar, emails, meta tags, PWA manifest. Edit directly in app.ts.
brand.descriptionHardcoded in sourceShort product description for SEO meta tags.
brand.logoHardcoded in sourcePath to logo image (relative to /public).
brand.faviconHardcoded in sourcePath to favicon (relative to /public).

The brand.name display name propagates to manifest.ts, layout.tsx metadata, and email templates automatically. The appUrl is used everywhere: robots.ts, sitemap.ts, auth callbacks, Stripe redirects, invite links, and email links. The serverUrl is used by tRPC for SSR data fetching — on Vercel preview deployments it points to the correct deployment URL instead of the production domain.


Feature toggles

The features object controls which modules are active. Set any toggle to false to disable a feature — it will be hidden from the navigation, and its routes become inaccessible.

features: {
  aiChat: true,
  teams: true,
  blog: true,
  helpCenter: true,
  waitlist: true,
  referrals: true,
  analytics: true,
  workflows: true,
  webhooks: true,
  auditLog: true,
  reports: true,
  featureRequests: true,
  changelog: true,
  onboarding: true,
  branding: true,
  sso: true,
  experiments: true,
  dripCampaigns: true,
},

All 18 toggles are true by default. Here's what each one controls:

ToggleFeature
aiChatAI chat assistant (Vercel AI SDK, streaming)
teamsOrganizations, multi-tenancy, team invitations
blogMDX blog with i18n, tags, and authors
helpCenterHelp center / documentation (MDX)
waitlistWaitlist with email confirmation
referralsReferral / affiliate program
analyticsSelf-serve analytics dashboard
workflowsEvent-triggered workflow automation
webhooksOutgoing webhooks (HMAC signed)
auditLogUser-facing activity / audit log
reportsScheduled email reports
featureRequestsPublic feature request voting board
changelogIn-app "What's new" changelog widget
onboardingInteractive onboarding tours
brandingPer-org white-labeling
ssoEnterprise SSO (SAML via BoxyHQ)
experimentsA/B testing framework
dripCampaignsEmail drip campaigns

Navigation items are automatically hidden when their linked feature is disabled — no manual sidebar editing needed.


Auth configuration

Control which sign-in methods appear on the auth pages:

auth: {
  oauthProviders: ['google', 'github'],
  magicLink: true,
  passkeys: true,
  twoFactor: true,
},
PropertyTypeDescription
oauthProvidersOAuthProvider[]OAuth buttons shown on sign-in/sign-up. Options: 'google', 'github', 'apple', 'discord', 'twitter', 'microsoft'
magicLinkbooleanPasswordless email sign-in via magic link
passkeysbooleanWebAuthn / passkey authentication
twoFactorbooleanTOTP-based two-factor authentication

You still need to configure the corresponding environment variables (e.g. GOOGLE_CLIENT_ID) for OAuth providers to function.


AI configuration

Customize the AI chat assistant behavior:

ai: {
  defaultModel: 'gpt-4o-mini',
  systemPrompt: 'You are a helpful AI assistant. Be concise, accurate, and helpful.',
  ragEnabled: true,
  ragMaxChunks: 3,
  ragMinScore: 0.4,
  allowedModels: [],
},
PropertyTypeDescription
defaultModelstringDefault model ID. Must match an entry in src/lib/ai/models.ts
systemPromptstringSystem prompt prepended to every conversation
ragEnabledbooleanEnable retrieval-augmented generation in chat
ragMaxChunksnumberMaximum context chunks injected from RAG
ragMinScorenumberMinimum cosine similarity score for RAG results (0–1)
allowedModelsstring[]Models shown in the chat model selector. Empty array = all models from models.ts

Payments configuration

Control Stripe Connect marketplace settings:

payments: {
  stripeConnectFeePercent: Number(process.env.NEXT_PUBLIC_STRIPE_CONNECT_FEE_PERCENT ?? 10),
},
PropertyTypeDescription
stripeConnectFeePercentnumberPlatform fee percentage shown in the Connect dashboard UI

Both the server-side fee (in src/lib/payments/connect.ts via env.stripeConnectFeePercent) and the client-side UI display read from appConfig.payments.stripeConnectFeePercent — a single source of truth. Override it per environment with NEXT_PUBLIC_STRIPE_CONNECT_FEE_PERCENT.


Company links

Set company contact details used in the Privacy Policy, Terms of Service, and footer:

company: {
  contactEmail: '[email protected]',
  githubUrl: 'https://github.com/myorg/myapp',
},
PropertyTypeDescription
contactEmailstringContact email used in legal pages (Privacy Policy, Terms)
githubUrlstringGitHub URL shown in the footer. Hidden if empty

Navigation

Edit src/config/navigation.ts to customize sidebar items for the dashboard and admin panel.

Each item has a href, label, icon (from Lucide), and an optional featureKey:

import type { FeaturesConfig } from './app';

export interface NavItem {
  href: string;
  label: string;
  icon: LucideIcon;
  featureKey?: keyof FeaturesConfig;
}

When featureKey is set, the item is automatically hidden if that feature is disabled in appConfig.features.

Dashboard sidebar

export const dashboardNavItems: NavItem[] = [
  { href: '/dashboard', label: 'Dashboard', icon: LayoutDashboard },
  { href: '/dashboard/ai-chat', label: 'AI Chat', icon: MessageSquare, featureKey: 'aiChat' },
  { href: '/dashboard/analytics', label: 'Analytics', icon: BarChart3, featureKey: 'analytics' },
  { href: '/dashboard/billing', label: 'Billing', icon: CreditCard },
  // ...add your own items here
];

Admin sidebar

export const adminNavItems: NavItem[] = [
  { href: '/admin', label: 'Overview', icon: LayoutDashboard },
  { href: '/admin/users', label: 'Users', icon: Users },
  { href: '/admin/subscriptions', label: 'Subscriptions', icon: CreditCard },
  { href: '/admin/feature-flags', label: 'Feature Flags', icon: Flag },
  // ...
];

To add a custom page, create the route in src/app/(dashboard)/dashboard/your-page/page.tsx and add a corresponding entry to dashboardNavItems.


Marketing configuration

Edit src/config/marketing.ts to customize the landing page content.

Pricing tiers

export const pricingTiers: PricingTier[] = [
  {
    key: 'starter',
    price: '$49',
    href: process.env.CHECKOUT_URL_STARTER || '/sign-up',
    featureKeys: [
      'tierFeatureFullCode',
      'tierFeatureAllModules',
      'tierFeatureProject1',
      'tierFeatureUpdates6',
      'tierFeatureCommunity',
    ],
  },
  // ... pro ($99, featured), enterprise ($149)
];

Feature labels are stored in messages/en.json under the "ProductPricing" namespace, so they support i18n. The key maps to a translation key for the tier name.

Set featured: true on a tier to visually highlight it. The href property links to an external checkout (configured via CHECKOUT_URL_* env vars) or falls back to /sign-up.

Premium plugins

Premium plugins are configured in the premiumPlugins array and pluginBundle object. Each plugin has a href that links to an external checkout URL (configured via CHECKOUT_URL_PLUGIN_* env vars):

  • CHECKOUT_URL_PLUGIN_AI_KIT — AI Kit plugin
  • CHECKOUT_URL_PLUGIN_CRM — CRM plugin
  • CHECKOUT_URL_PLUGIN_HELPDESK — Helpdesk plugin
  • CHECKOUT_URL_PLUGIN_EMAIL_MARKETING — Email Marketing plugin
  • CHECKOUT_URL_PLUGIN_BUNDLE — All plugins bundle (discounted)

When set, plugin cards on the pricing page and the /plugins page show "Buy" buttons linking to external checkout. Without these env vars, buttons link to /#plugins.

Testimonials

export const testimonials: Testimonial[] = [
  {
    name: 'Alex Chen',
    role: 'Founder, StartupXYZ',
    content: 'Codapult saved me weeks of setup time.',
    initials: 'AC',
  },
  // ...add real customer quotes
];

Stats and competitor comparison

The stats array powers the "by the numbers" section on the landing page. The comparisonFeatures array generates the competitor comparison table — edit it to reflect your product's feature matrix.


Typed env var access

src/lib/config.ts provides a typed env object for reading environment variables in server code:

import { env } from '@/lib/config';

// Type-safe access with defaults
env.authProvider; // 'better-auth' | 'kinde' | 'none'
env.paymentProvider; // 'stripe' | 'lemonsqueezy'
env.storageProvider; // 'local' | 's3' | 'r2'
env.notificationTransport; // 'poll' | 'sse' | 'ws'
env.vectorStoreProvider; // 'sqlite' | 'memory'
env.sso.product; // string — SSO product identifier
env.defaultMonthlyCredits; // number — monthly AI credits
env.stripeConnectFeePercent; // number — Stripe Connect fee
env.turso.url; // string
env.stripe.secretKey; // string

Important: Server-side code should use env.* for adapter/provider settings and appConfig.* for app identity (appUrl, serverUrl, appName, brand). Never read process.env directly for values available through these objects.

This file is hand-maintained. When adding a new environment variable, add a Zod field to the schema and a corresponding property to the env object in the same file.

For the full list of environment variables and their descriptions, see the Environment Variables reference.

Environment VariablesAuthentication