> ## Documentation Index
> Fetch the complete documentation index at: https://docs.switchbord.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Platform Architecture

> Service boundaries, runtime topology, and deployment model.

# Platform Architecture

Switchbord is deployed as a multi-tenant Next.js application on Vercel, backed by Supabase for persistence, auth, and realtime, with a Railway-hosted worker for background processing. There is no separate control-plane service — that architectural path was retired and all runtime responsibilities consolidated.

## Topology

* **`apps/app`**: operator product — inbox, contacts, templates, campaigns, Margaret AI, settings. Deployed to Vercel.
* **`apps/web`**: marketing site, QR/opt-in flows, gated landing pages. Deployed to Vercel.
* **`apps/api`**: Next.js API surface for Meta webhook ingress, public REST API v1 (OpenAPI spec at `/spec`), compatibility endpoints, and health/status routes. Deployed to Vercel.
* **`apps/docs`**: Mintlify-based documentation. Deployed to Mintlify.
* **`apps/worker`**: queue processing, retries, campaign schedulers, message dispatch, replay tooling. Deployed to Railway.
* **Supabase**: Postgres, Auth, Storage, Realtime, Vault. Single source of truth.

<Note>
  The `apps/control-plane` service has been removed. All webhook-oriented control-plane work now runs inside `apps/api` and `apps/worker`. Any local references to port 4000 or `apps/control-plane/.env.example` are legacy artifacts.
</Note>

## Core Runtime Paths

1. **Inbound webhook** — Meta delivers a webhook to `apps/api` → signature verified with `crypto.timingSafeEqual` → raw envelope stored in `webhook_events` → Supabase notifies `apps/worker` → worker routes to message, status, template, or journey processor.
2. **Outbound send** — Operator or automation creates a send intent → Supabase records to `outbox_jobs` → `apps/worker` schedules against throughput/pair limits → Meta response logged → status webhooks update the ledger.
3. **Broadcasts** — Audience snapshot created → worker fans out outbox jobs → Supabase Realtime pushes campaign progress to operator dashboard.
4. **Margaret AI** — Operator triggers AI reply draft → `apps/app` calls `/api/ai-assistant` → workspace LLM provider resolved from `llm_provider_configs` → PII pseudonymized before any prompt → response de-pseudonymized → draft surfaced in inbox. Agent execution (BORD-140–143) adds tool-use on top.

## Multi-Tenant Workspace Model

Every tenant is an isolated **workspace**. The core isolation guarantees:

* Workspace context is resolved server-side from `workspace_members` — never from env vars or client-supplied headers.
* All data queries are scoped by `workspace_id` derived from that resolution.
* Message dispatch reads Meta credentials from the owning workspace's Vault secrets — no cross-workspace credential leakage.
* Four RBAC roles enforced at every route: **owner**, **admin**, **developer**, **operator**.

See [Multi-Tenancy & Workspace Isolation](/security/multi-tenancy) for the full security model.

## Margaret AI Layer

The AI layer introduces three new runtime concerns:

* **LLM provider configs** — workspace-owned records in `llm_provider_configs` pointing at OpenAI, Anthropic, OpenRouter, Ollama, or vLLM. API keys stored in Supabase Vault via `workspace_secret_bindings`.
* **AI agents** — `ai_agents` table defines persona, model, tool permissions, and PII pseudonymization settings per workspace.
* **AI threads** — `ai_threads` and `ai_thread_messages` maintain conversation context for Margaret replies. `ai_agent_executions` log tool-use runs for auditability.

## Consent Ledger

The consent ledger (BORD-193) provides a durable, auditable record of every opt-in and opt-out event per contact:

* **`consent_events` table** — append-only log of consent transitions (opt-in / opt-out / revoked). An `AFTER INSERT` trigger on this table denormalizes the latest state into `contacts.consent_state` for fast reads.
* **STOP / START keyword detection** — `packages/whatsapp` parses inbound messages for localized opt-out (`STOP`, `BLOQUEAR`, `FERMA`, `PARAR`) and opt-in (`START`, `INICIO`, `INIZIO`, `COMEÇAR`) keywords in English, Italian, Portuguese, and Spanish.
* **Campaign dispatch opt-out gate** — the broadcast engine checks `contacts.consent_state` before enqueuing any outbox job. Contacts in `opted_out` or `revoked` state are silently skipped.
* **REST endpoints** — `GET /api/v1/contacts/:id/consent` returns the current state and event history; `POST /api/v1/contacts/:id/consent` records a manual consent transition (owner/admin only).

## Command Palette

The command palette (BORD-204) is a ⌘K surface for navigation, search, and actions across the operator product:

* **`command-palette.tsx`** — the palette shell, triggered by <kbd>⌘K</kbd> / <kbd>Ctrl+K</kbd>. Renders a searchable list of commands grouped by category.
* **`command-palette-provider.tsx`** — React context that aggregates available commands from each module (inbox, contacts, campaigns, templates, journeys, settings) and manages open/close state.
* **`command-palette-hint.tsx`** — inline hint badge rendered in the sidebar and page headers to discover the shortcut.

The palette is extensible: any module can register commands via the provider's `registerCommands` hook.

## Graphical Journey Editor

A React Flow canvas at `/journeys/[id]` for building multi-step automations visually:

* **9 node types** — Trigger, Condition, Wait, Send Template, Update Attribute, Add/Remove Tag, Outbound Webhook, AI Agent Step, and Exit.
* **Validation** — the editor validates required fields, edge connectivity, and cycle detection before publish. Errors surface inline on each node.
* **Publish pipeline** — validated graphs are serialized to the `journeys` table as versioned JSON. A `publish` mutation creates an immutable snapshot and activates the journey for live execution by `apps/worker`.

## Visual Template Builder

A 3-pane builder at `/templates/new` for composing WhatsApp message templates:

* **7 editors** — Header, Body, Footer, Buttons, Examples, Language, and Category.
* **`parameter_format` column** — stored on `message_templates` to declare the expected type and order of template variables.
* **Submit-to-Meta pipeline** — the builder POSTs the composed template to the Meta Business API and tracks approval status via template status webhooks.
* **Phone-frame renderer** — a live preview component renders the template as it would appear on a device, with placeholder substitution for variables.

## Auto-Trigger Agent

The auto-trigger agent (BORD-192) extends Margaret AI to draft replies automatically on inbound WhatsApp messages:

* **Agent run trigger** — when an inbound message arrives and the workspace has an auto-trigger agent configured, `agent.run` is invoked automatically by the worker.
* **Draft accept / reject API** — `POST /api/v1/contacts/:id/drafts/:draftId/accept` and `POST /api/v1/contacts/:id/drafts/:draftId/reject` let operators approve or discard AI-generated drafts.
* **Draft banner in inbox** — a prominent banner in the conversation view alerts operators when a draft is pending, with one-click accept/reject actions.

## Asset Storage

Asset storage (BORD-187) provides workspace-scoped file uploads for templates, journeys, and knowledge assets:

* **`asset_files` table** — records file metadata (name, MIME type, size, workspace scope) and points to the stored object.
* **S3-compatible pipeline** — uploads flow through a presigned-URL pipeline to any S3-compatible object store. The worker retrieves objects at dispatch time for media sends.
* **Workspace isolation** — every file is scoped by `workspace_id`; cross-workspace access is rejected at the API layer.

## Account Alerts

Account alerts (BORD-189) surface Meta account-level issues to operators:

* **Webhook handler** — `apps/api` processes `account_alerts` webhook events from Meta and stores them in `account_alert_events`.
* **Operator UI panel** — a dedicated panel in settings displays active alerts (e.g. policy violations, rate limit warnings, credential expiry) with severity and resolution steps.

## Audit Logs

Audit logs (BORD-158) record operator actions for compliance and forensics:

* **`audit_logs` table** — workspace-scoped, append-only log of actor, action, target resource, and diff payload.
* **`writeAuditLog()`** — a server-side helper that every mutation route calls before committing. Guarantees consistent structure and prevents accidental omission.

## GDPR Data Retention

GDPR data retention (BORD-165 / BORD-167) enforces per-workspace retention limits:

* **`data_retention_days`** — a workspace-level setting (default 365). Controls how long webhook envelopes, message content, and consent events are retained.
* **`retention_sweep` job** — a scheduled worker job that deletes records older than the workspace's retention window. Runs daily; logs sweep metrics to `audit_logs`.

## API Key Generation

API key generation (BORD-178) allows workspace owners to provision long-lived API keys:

* **`swb_`-prefixed keys** — generated server-side with a cryptographically random 32-byte secret. The prefix enables fast visual identification and programmatic detection in logs.
* **Key metadata** — stored in `api_keys` with creation timestamp, last-used timestamp, and optional description. Keys are hashed at rest; the full secret is shown only once at creation.

## Guided Wizard

The guided wizard (BORD-194) provides a first-run-to-first-message onboarding flow:

* **Step sequence** — connect WhatsApp number → configure LLM provider → create first template → send first message.
* **Progress tracking** — stored in `workspace_onboarding_state`. The wizard auto-advances and is dismissible after completion.
* **Entry point** — newly created workspaces redirect to `/welcome` on first login. The wizard is also accessible from Settings.

## Design System Primitives

New design-system primitives shipped in `packages/design-system`:

* **Design tokens** — semantic color, spacing, and typography tokens in `tokens.css`, consumed by all shadcn/ui components.
* **Motion** — consistent transition and animation utilities in `motion.ts` (durations, easings, enter/exit presets).
* **Skeleton** — `Skeleton` component with pulse animation, used as loading placeholders across the operator product.

## Brand Updates

* **Transparent wordmark** — the expanded sidebar logo now uses a transparent-background SVG wordmark for clean rendering on any surface.
* **Black-background favicon** — `favicon.ico` updated to a black-background variant for high visibility in browser tabs.

## Data & Security Principles

* Every webhook envelope is immutable; raw body + metadata retained with configurable retention controls.
* Delivery history is append-only (`outbox_jobs`, `message_status_events`) — never overwritten.
* RBAC, feature flags, and provider secrets remain server-side only.
* Private Supabase Realtime Broadcast topics power operator realtime feeds; no Postgres Changes broadcast to avoid scaling issues.
* Audit log on every operator action and credential rotation.
* Consent ledger enforces opt-out before any campaign or automation dispatch.
* Data retention sweeps enforce GDPR deletion timelines per workspace.

See [Security Overview](/security/overview) for the full control implementation status.

## Operator UI — Sidebar & Navigation

The operator UI (`apps/app`) uses a sidebar built on shadcn/ui's `Sidebar` primitives.

### Component

* **File**: `apps/app/components/operator-sidebar.tsx` (\~165 lines)
* **Pattern**: `collapsible='icon'` — collapses to an icon-only rail; expands on hover or toggle. Props interface is stable.
* **Layout**: Each page that needs the sidebar wraps its content with `SidebarProvider`.

### Navigation structure

* **Top section (flat nav)**: Inbox / Contacts / Broadcasts / Templates / Operations
* **Bottom section (footer)**: Settings / Docs / User account

### Theming

The sidebar uses standard shadcn light CSS variable defaults — there is no custom dark theme or `className='dark'` applied to the sidebar element.

Relevant variables (defined under `:root` in `packages/design-system/styles/globals.css`):

* `--sidebar`: near-white background
* `--sidebar-foreground`: near-black text

The dark near-black custom sidebar theme from before PR #270 has been removed. The sidebar now inherits from the workspace's standard light design-system tokens.

### Logo

* **Expanded**: full wordmark
* **Collapsed**: icon-only mark

## Monitoring + Deployment

* Health endpoints: `/health`, `/ready`, `/internal/runtime`.
* Operational endpoints: `/internal/webhooks`, `/internal/webhook-rejections`, `/internal/webhooks/:id/replay`.
* OpenAPI spec + Scalar explorer: `/spec`.
* Deploy `apps/app`, `apps/web`, `apps/api`, and `apps/docs` to Vercel/Mintlify. Deploy `apps/worker` to Railway. All surfaces share the same Supabase project.
* CI validates builds, migrations, docs lint, and secret scans. See [CI/CD](/operations/cicd).

## Read next

* [Security Overview](/security/overview)
* [Multi-Tenancy & Workspace Isolation](/security/multi-tenancy)
* [Data Model](/platform/data-model)
* [Feature Matrix](/platform/feature-matrix)
* [Operations Runbook](/operations/runbook)
