# Settings & Administration

**Task 016** — centralized admin module for users, roles, mailboxes, AI configuration, system branding, and import tuning.

## Access control

- **UI:** `/settings/*` — layout redirects non-admins to `/dashboard`
- **API:** all `/api/settings/**` routes use `requireAdminApiSession()` → `403 Forbidden` if role lacks `settings.manage`
- **Sidebar:** Settings link visible only when `canAccessSettings(role)` (admin)
- **Helpers:** `src/lib/permissions.ts` — `hasPermission()`, `hasRole()`, `canAccessSettings()`

## Roles

| Role | Scope |
|------|--------|
| `admin` | Full access including settings |
| `manager` | CRM, campaigns, inbox, leads, pipeline, tasks, analytics |
| `sales` | Leads, pipeline, inbox, tasks, market read |
| `marketing` | Campaigns, segments, templates, analytics, market read |
| `viewer` | Read-only across major modules |

## System settings (`system_settings`)

Singleton document `key: "global"` via `SystemSettingModel`.

| Field group | Fields |
|-------------|--------|
| Branding (legacy) | `appName`, `companyName` — synced from `branding.platformName` / `branding.companyName` |
| Branding (white-label) | `branding.*` — logos, colors, website, support contacts — see [28-white-label-branding.md](./28-white-label-branding.md) |
| System | `defaultLanguage`, `timezone` |
| Email defaults | `defaultSenderName`, `defaultSenderEmail` |
| Email policy | `emailPolicy.duplicateWindowDays` (default `7`, `0` = disabled), `emailPolicy.duplicateAction` (`warn` \| `skip` \| `allow`) |
| Import | `duplicateThreshold` (default `0.85`), `companyMatchFields`, `personMatchFields`, `contactMatchFields`, `autoDuplicateDetection`, `autoDataQualityChecks` |
| AI overrides | `aiProvider`, `openaiModel`, `geminiModel`, `anthropicModel` (no API keys in DB) |

Service: `src/server/services/settings.service.ts`

## Users

Service: `src/server/services/settings-user.service.ts`

- List, create, update, activate/deactivate, reset password
- Passwords hashed with bcrypt; never returned in API responses
- Fields exposed: `name`, `email`, `role`, `active`, `lastLoginAt`

## Mailboxes

Service: `src/server/services/mailbox-settings.service.ts`

- Multiple SMTP/IMAP profiles per org
- Passwords stored as `smtpPasswordEncrypted` / `imapPasswordEncrypted` (AES-256-GCM via `NEXTAUTH_SECRET` in `src/lib/secret-crypto.ts`)
- API never returns decrypted passwords; `hasSmtpPassword` / `hasImapPassword` flags only
- Default env SMTP/IMAP (`SMTP_*`, `IMAP_*`) remain fallback for legacy send/sync until campaigns/inbox select `mailboxId`
- `Campaign` and `EmailMessage` include optional `mailboxId` ref for future routing

## AI settings

- API keys **only** in environment variables (`OPENAI_API_KEY`, etc.)
- Admin UI shows configuration status and allows DB override of provider/model
- Runtime classification uses `resolveAIProvider()` (DB overrides merged with env) in `ai-provider.factory.ts`

## UI routes

| Route | Purpose |
|-------|---------|
| `/settings` | Dashboard cards |
| `/settings/users` | User CRUD |
| `/settings/mailboxes` | Mailbox CRUD, verify SMTP/IMAP, set default |
| `/settings/ai` | Provider/model + test classification |
| `/settings/branding` | White-label: logos, colors, platform name, preview |
| `/settings/system` | Language, timezone, sender defaults |
| `/settings/email-policy` | Duplicate send window and action (campaign protection) |
| `/settings/imports` | Dedup threshold and match fields |
| `/settings/data-cleanup` | Data cleanup & reset (Task 017) — see [19-data-cleanup-reset.md](./19-data-cleanup-reset.md) |

## API routes (admin only)

| Method | Path |
|--------|------|
| GET | `/api/settings/branding` (public read) |
| PATCH | `/api/settings/branding` |
| POST | `/api/settings/branding/upload` |
| GET | `/api/branding/assets/[filename]` |
| GET, PATCH | `/api/settings/system` |
| GET, PATCH | `/api/settings/email-policy` |
| GET, PATCH | `/api/settings/imports` |
| GET, PATCH, POST | `/api/settings/ai` (POST = test classify) |
| GET, POST | `/api/settings/mailboxes` |
| GET, PATCH, DELETE | `/api/settings/mailboxes/[id]` |
| POST | `/api/settings/mailboxes/[id]/verify-smtp` |
| POST | `/api/settings/mailboxes/[id]/verify-imap` |
| POST | `/api/settings/mailboxes/[id]/set-default` |
| GET, POST | `/api/settings/users` |
| GET, PATCH | `/api/settings/users/[id]` |
| POST | `/api/settings/users/[id]/reset-password` |

## Known limitations

- Import settings are persisted but not yet consumed by all import duplicate matchers (env/hardcoded paths may still apply).
- Campaign send and IMAP sync still primarily use env transport unless `mailboxId` wiring is completed in send/inbox services.
- Gemini/Anthropic providers remain placeholders; OpenAI + fallback are production-ready.

## Related

- [11-security.md](./11-security.md)
- [10-environment-variables.md](./10-environment-variables.md)
- [03-api-specification.md](./03-api-specification.md)
- [04-ui-pages.md](./04-ui-pages.md)
