+ );
+}
diff --git a/docs/admin/README.md b/docs/admin/README.md
new file mode 100644
index 000000000..d41d51d64
--- /dev/null
+++ b/docs/admin/README.md
@@ -0,0 +1,132 @@
+# Admin App - Feature Assessment & Roadmap
+
+## Executive Summary
+
+The admin app has **25+ feature areas** but is **~95% read-only data display**. Most pages follow the same pattern: a table with filters, sorting, and pagination, but no ability to act on data.
+
+### What Works Well (6 features with real admin capability)
+1. **Content/CMS** - Full page builder with 12 block types, live preview, drag-reorder, publish/unpublish, image library, nav editor
+2. **Site Notifications** - Full CRUD: create, edit, toggle, delete
+3. **Support Tickets** - Ticket detail with reply, claim, status management
+4. **Support Chat** - Real-time chat with queue, threading, presence
+5. **Settings** - Platform name, support email, maintenance mode, feature flags (AI matching, fraud, automation, escrow)
+6. **AI Usage** - Usage dashboard, model config editing/testing, usage logs
+
+### What's Partially There (5 features with limited actions)
+7. **Jobs** - Status transitions (activate/pause/close/reopen), detail page
+8. **Recruiters** - Approve/suspend with confirmation
+9. **Firms** - Marketplace approve/revoke
+10. **Automation** - Toggle rules active/inactive
+11. **Escrow** - Release holds
+
+### What's Purely Read-Only (14+ features)
+Everything else - Users, Candidates, Companies, Organizations, Applications, Matches, Placements, Assignments, Payouts, Billing Profiles, Chat Moderation, Fraud, Reputation, Ownership, Decision Log, Activity, Metrics
+
+## Common Gaps Across All Features
+
+1. **No detail pages** - Most entities have no click-through detail view
+2. **No editing** - Almost no ability to edit existing records
+3. **No entity creation** - Can't create records from admin (except notifications)
+4. **No bulk actions** - No multi-select, no batch operations
+5. **No internal notes** - No admin-only notes on any entity
+6. **No entity linking** - Can't navigate between related entities (e.g., recruiter -> their jobs -> placements)
+7. **No audit trail visibility** - Actions taken aren't logged or visible
+8. **Data display issues** - Some tables show UUIDs instead of names (assignments shows recruiter_id)
+
+## Feature Documents
+
+### Core Entities
+| Document | Feature | Current | Gap |
+|----------|---------|---------|-----|
+| [users.md](users.md) | Users | List + search | No detail, no role mgmt, no suspend |
+| [recruiters.md](recruiters.md) | Recruiters | List + approve/suspend | No detail, no editing, no performance |
+| [candidates.md](candidates.md) | Candidates | List + smart resume | No detail, no profile mgmt |
+| [companies.md](companies.md) | Companies | List + search | Fully read-only, no detail |
+| [firms.md](firms.md) | Firms | List + marketplace approve | No detail, no member mgmt |
+| [organizations.md](organizations.md) | Organizations | List + search | Fully read-only, no detail |
+
+### Business Operations
+| Document | Feature | Current | Gap |
+|----------|---------|---------|-----|
+| [jobs.md](jobs.md) | Jobs | List + status actions + detail | No editing, no pipeline view |
+| [applications.md](applications.md) | Applications | List + stage filter | No detail, no stage mgmt |
+| [matches.md](matches.md) | Matches | List + detail page | No filters, no actions, no pagination |
+| [placements.md](placements.md) | Placements | List + status filter | No detail, no fee mgmt |
+| [assignments.md](assignments.md) | Assignments | List (shows UUIDs!) | Broken display, no actions |
+
+### Financial
+| Document | Feature | Current | Gap |
+|----------|---------|---------|-----|
+| [payouts-billing.md](payouts-billing.md) | Payouts & Billing | List + detail modal + escrow release | No approval workflow, no manual payouts |
+
+### Platform & Communication
+| Document | Feature | Current | Gap |
+|----------|---------|---------|-----|
+| [chat-moderation.md](chat-moderation.md) | Chat Moderation | List flagged messages | No moderation actions! |
+| [notifications.md](notifications.md) | Notifications | Full CRUD | Add targeting + scheduling |
+| [support.md](support.md) | Support | Tickets + live chat | Add priority, SLA, assignment |
+| [automation.md](automation.md) | Automation | List + toggle | No create/edit, no history |
+
+### Trust & Security
+| Document | Feature | Current | Gap |
+|----------|---------|---------|-----|
+| [fraud-trust.md](fraud-trust.md) | Fraud & Trust | Three list views | No actions, no investigation workflow |
+| [decision-log.md](decision-log.md) | Decision Log | List view | No detail, no override capability |
+
+### System & Analytics
+| Document | Feature | Current | Gap |
+|----------|---------|---------|-----|
+| [dashboard-analytics.md](dashboard-analytics.md) | Dashboard & Analytics | Stats + charts + activity | KPIs not actionable, no revenue dashboard |
+| [settings.md](settings.md) | Settings | Basic form | Feature flags, fee config, maintenance mode |
+| [content.md](content.md) | CMS | Page builder + images + nav | Publishing workflow, versioning |
+
+## Suggested Phase Order
+
+### Phase 1: Entity Detail Pages & Core Actions
+**Goal:** Every major entity has a clickable detail page and basic management actions.
+- User detail page + role management + suspend
+- Recruiter detail page + profile editing + rejection workflow
+- Candidate detail page + resume viewer
+- Company detail page
+- Firm detail page + member management
+- Application detail page + stage management
+- Placement detail page + fee management
+- Fix assignments table (show names, not UUIDs)
+
+### Phase 2: Financial Management
+**Goal:** Admins can fully manage the financial pipeline.
+- Payout approval workflow
+- Manual payout creation
+- Refund processing
+- Revenue dashboard
+- Fee structure management
+
+### Phase 3: Moderation & Trust Actions
+**Goal:** Admins can act on flagged content and trust signals.
+- Chat moderation actions (clear/remove/warn)
+- Fraud investigation workflow + resolution
+- Reputation management + overrides
+- Ownership verification actions
+
+### Phase 4: Automation & Intelligence
+**Goal:** Admin can configure and monitor automated systems.
+- Automation rule creation and editing
+- Decision log detail + override capability
+- AI usage budget management
+- Matching engine controls
+
+### Phase 5: Analytics & Reporting
+**Goal:** Data-driven decision making.
+- Revenue analytics
+- Funnel analytics
+- Actionable dashboard KPIs
+- Export/reporting capabilities
+- Scheduled reports
+
+### Phase 6: Advanced Operations
+**Goal:** Power tools for mature platform operations.
+- Bulk actions across all entities
+- Internal notes system
+- GDPR compliance tools
+- Content publishing workflow
+- Integration/webhook management
diff --git a/docs/admin/applications.md b/docs/admin/applications.md
new file mode 100644
index 000000000..af4203aed
--- /dev/null
+++ b/docs/admin/applications.md
@@ -0,0 +1,49 @@
+**COMPLETED**
+
+# Applications Management
+
+## Current State
+
+**Route:** `/secure/applications`
+**Data source:** `/ats/admin/applications`
+
+### What Exists
+- **Table columns:** Candidate (name/email), Job title, Stage (badge), Applied date
+- **Filters:** Stage pills (16 stages from AI Review through Withdrawn)
+- **Stats banner:** Applications, Jobs, Candidates, Placements counts
+- **Pagination:** Standard prev/next
+- **Actions:** "Generate Tailored Resume" button per row
+- **No detail page** - No click-through to individual application
+
+### What's Missing
+
+#### Critical (Phase Priority: High)
+- **Application detail page** - Full view: candidate info, job info, stage history, notes, timeline, resume, match score
+- **Stage management** - Move application between stages with reason/notes (admin override)
+- **Application rejection** - Reject with categorized reason, optional feedback to candidate
+- **Application notes** - Admin notes on specific applications
+- **Stage history/timeline** - Visual timeline showing stage transitions with timestamps and who made the change
+- **Resume view** - View candidate's resume and tailored resume for this specific application
+
+#### Important (Phase Priority: Medium)
+- **Bulk stage transitions** - Select multiple applications, move to a stage
+- **AI review results** - View AI screening results, scores, flags
+- **Interview scheduling** - View/manage interview schedules for this application
+- **Offer management** - View/edit offer details (salary, start date, benefits)
+- **Communication thread** - All messages related to this application
+- **Application scoring** - View match score, screening score, interview scores
+- **Duplicate application detection** - Flag when same candidate applies to same/similar job
+- **Application search by candidate** - Find all applications for a specific candidate
+- **Export** - Export application data (CSV/Excel) with filters
+
+#### Nice to Have
+- **Application funnel analytics** - Conversion rates between stages, drop-off analysis
+- **Stage duration analytics** - Average time in each stage, bottleneck identification
+- **Comparison view** - Compare multiple candidates for the same job side-by-side
+- **Application scoring override** - Admin ability to adjust AI scores with justification
+
+## Implementation Notes
+- Stage transitions should be logged to create an audit trail
+- AI review results should show the specific criteria and scores
+- Stage management should respect the stage flow rules (see application-stages pattern)
+- Bulk operations should validate each transition is valid before executing
diff --git a/docs/admin/assignments.md b/docs/admin/assignments.md
new file mode 100644
index 000000000..3bb486f71
--- /dev/null
+++ b/docs/admin/assignments.md
@@ -0,0 +1,41 @@
+# Assignments Management
+
+## Current State
+
+**Route:** `/secure/assignments`
+**Data source:** `/ats/admin/assignments`
+
+### What Exists
+- **Table columns:** Recruiter ID (truncated UUID!), Job Title (or "Unknown Job"), Status (badge), Created date
+- **Filters:** Status pills (All, Active, Pending, Completed, Cancelled)
+- **Search:** Text search
+- **Pagination:** Standard prev/next
+- **Actions:** None - completely read-only
+- **No detail page** - No click-through
+
+### What's Missing
+
+#### Critical (Phase Priority: High)
+- **Show recruiter name** - Currently shows truncated UUID instead of recruiter name (data join issue)
+- **Assignment detail page** - Full view: recruiter info, job info, fee terms, activity on this assignment, applications submitted
+- **Assignment management** - Create, reassign, cancel assignments
+- **Fee terms view** - What split percentage was agreed for this assignment
+- **Performance tracking** - How many candidates has this recruiter submitted for this job
+
+#### Important (Phase Priority: Medium)
+- **Assignment creation** - Admin can assign a recruiter to a job (with fee terms)
+- **Reassignment** - Transfer an assignment from one recruiter to another
+- **Assignment notes** - Internal notes on the assignment
+- **Activity tracking** - What actions has the recruiter taken on this assignment
+- **Bulk management** - Bulk cancel inactive assignments, bulk reassign
+- **Duplicate detection** - Flag if a recruiter is assigned to overlapping/competing jobs
+
+#### Nice to Have
+- **Assignment analytics** - Fill rates by recruiter, average time-to-fill
+- **Recruiter workload view** - How many active assignments does each recruiter have
+- **Auto-assignment rules** - Configure rules for automatic recruiter-job matching
+
+## Implementation Notes
+- PRIORITY FIX: Replace recruiter_id UUID display with recruiter name (needs a data join in the admin view)
+- Assignment creation should validate: recruiter is active, job is active, no duplicate assignment
+- Fee terms should be stored on the assignment, not just inherited from defaults
diff --git a/docs/admin/automation.md b/docs/admin/automation.md
new file mode 100644
index 000000000..fd17746af
--- /dev/null
+++ b/docs/admin/automation.md
@@ -0,0 +1,47 @@
+# Automation Rules Management
+
+## Current State
+
+**Route:** `/secure/automation`
+**Data source:** `/automation/admin/rules`
+
+### What Exists
+- **Table columns:** Rule Name/Description, Trigger (badge), Action (badge), Run Count, Last Run date, Active toggle
+- **Sorting:** By name, run count
+- **Actions:** Toggle active/inactive (PATCH to update `is_active`)
+- **Empty state:** "No automation rules" placeholder
+- **No detail view** - No click-through to rule details
+- **No create/edit** - Cannot create or modify rules from UI
+
+### What's Missing
+
+#### Critical (Phase Priority: High)
+- **Rule creation** - Form to create new automation rules with trigger, conditions, and action configuration
+- **Rule editing** - Edit existing rules (name, description, trigger, conditions, actions)
+- **Rule detail page** - View full rule configuration, execution history, error log
+- **Execution history** - See every time a rule fired: timestamp, trigger data, action result, success/failure
+- **Rule testing** - Dry-run a rule to see what it would do without executing
+
+#### Important (Phase Priority: Medium)
+- **Trigger configuration** - Configure trigger events (e.g., "when application reaches stage X", "when recruiter signs up")
+- **Condition builder** - Visual condition builder (if X AND Y, then do Z)
+- **Action configuration** - Configure actions (send email, change status, create notification, assign recruiter)
+- **Rule ordering/priority** - Set execution order when multiple rules match
+- **Error monitoring** - Alert when rules fail, show error details
+- **Rule templates** - Pre-built rule templates for common automations
+- **Execution log** - Filterable log of all rule executions with results
+- **Rule versioning** - Track changes to rules over time
+- **Bulk enable/disable** - Toggle multiple rules at once
+
+#### Nice to Have
+- **Rule analytics** - Which rules fire most, which save most time
+- **Rule dependencies** - Show which rules depend on others
+- **Webhooks** - Configure webhook actions for external integrations
+- **Scheduled rules** - Rules that fire on a schedule (cron-like)
+
+## Implementation Notes
+- Rule creation needs a structured form with: trigger event selector, condition builder, action configurator
+- Triggers should map to RabbitMQ events that already exist in the system
+- Actions should be pluggable (email, status change, notification, webhook)
+- Execution history is critical for debugging automation issues
+- Test/dry-run should show exactly what would happen without side effects
diff --git a/docs/admin/candidates.md b/docs/admin/candidates.md
new file mode 100644
index 000000000..a04ab342d
--- /dev/null
+++ b/docs/admin/candidates.md
@@ -0,0 +1,46 @@
+# Candidates Management
+
+## Current State
+
+**Route:** `/secure/candidates`
+**Data source:** `/ats/admin/candidates`
+
+### What Exists
+- **Table columns:** Name/Email, Phone, Location, Resume Status (badge), Created date
+- **Filters:** Resume status dropdown (All, Uploaded, Pending, Processing, Failed, None)
+- **Search:** Text search
+- **Pagination:** Standard numbered pagination
+- **Actions:** "Build Smart Resume" button per row
+- **No detail page** - No click-through to individual candidate
+
+### What's Missing
+
+#### Critical (Phase Priority: High)
+- **Candidate detail page** - Full profile: resume, skills, work history, applications, matches, placements, activity
+- **Application history** - All applications this candidate has, with stages and outcomes
+- **Resume management** - View parsed resume, re-trigger parsing, download original, view smart resume
+- **Profile editing** - Edit candidate details (contact info, location, skills, etc.)
+- **Candidate status management** - Active/inactive/blacklisted status with reason tracking
+
+#### Important (Phase Priority: Medium)
+- **Match history** - All AI matches for this candidate, scores, outcomes
+- **Interview tracking** - See all interviews scheduled/completed across all applications
+- **Candidate notes** - Internal admin notes
+- **Communication log** - Emails sent, messages received
+- **Duplicate detection** - Flag potential duplicate candidates (same email, similar name+location)
+- **GDPR tools** - Data export, anonymization, consent management
+- **Bulk resume re-processing** - Select multiple candidates, re-trigger resume parsing
+- **Source tracking** - How the candidate entered the system (direct, recruiter upload, API)
+
+#### Nice to Have
+- **Candidate journey timeline** - Visual timeline from signup to placement
+- **Skills taxonomy management** - Normalize/merge skill tags across candidates
+- **Availability status** - Currently looking, not looking, open to offers
+- **Salary expectations** - Admin view of candidate salary data for market analysis
+- **Placement success rate** - How often this candidate's placements stick through guarantee
+
+## Implementation Notes
+- Detail page tabs: Overview, Applications, Matches, Resume, Activity, Notes
+- Resume viewer should show both original and parsed/smart versions
+- Blacklisting should be soft-delete with reason and should cascade to active applications
+- Duplicate detection could leverage the existing matching engine
diff --git a/docs/admin/chat-moderation.md b/docs/admin/chat-moderation.md
new file mode 100644
index 000000000..f18709274
--- /dev/null
+++ b/docs/admin/chat-moderation.md
@@ -0,0 +1,44 @@
+# Chat Moderation
+
+## Current State
+
+**Route:** `/secure/chat`
+**Data source:** `/chat/admin/flagged`
+
+### What Exists
+- **Table columns:** Severity (high/medium/low), From/To (sender + recipient), Message Preview (truncated), Flag Reason, Status (pending/reviewed/removed/cleared), Flagged date
+- **Filters:** Status dropdown (All, Pending, Reviewed, Removed, Cleared)
+- **Sorting:** By sender name, flagged date
+- **Actions:** None - completely read-only despite being a "moderation" page
+- **No detail view** - No way to see full message, conversation context, or user profiles
+
+### What's Missing
+
+#### Critical (Phase Priority: High)
+- **Moderation actions** - Mark as reviewed/clear/remove with one click per flagged message
+- **Full message view** - Click to see complete message (not just preview), and surrounding conversation context
+- **Conversation context** - Show messages before/after the flagged message for context
+- **User action from moderation** - Warn user, suspend user, or escalate directly from flagged message
+- **Bulk moderation** - Select multiple flagged messages, batch clear or batch remove
+
+#### Important (Phase Priority: Medium)
+- **All conversations view** - Browse all conversations (not just flagged), search by participant
+- **Conversation monitoring** - Real-time view of active conversations
+- **User chat history** - View all chat messages from a specific user
+- **Flagging rules management** - Configure what triggers automatic flagging (keywords, patterns, AI detection)
+- **Message deletion** - Delete individual messages from conversations
+- **User communication ban** - Temporarily or permanently ban a user from chat
+- **Moderation stats** - Flagged messages per day, resolution time, false positive rate
+- **Escalation workflow** - Escalate flagged content to senior admin with notes
+
+#### Nice to Have
+- **AI moderation tuning** - Adjust sensitivity of automatic flagging
+- **Content pattern analysis** - Identify trending problematic content patterns
+- **Moderation queue prioritization** - Auto-sort by severity, time pending
+- **Canned responses** - Pre-written warning messages for common violations
+
+## Implementation Notes
+- Moderation actions should be immediate (no confirmation needed for clear/review)
+- Remove action should soft-delete the message and replace with "[Message removed by admin]"
+- User warnings should be tracked and auto-escalate after N warnings
+- Full conversation context should show 5-10 messages before/after the flagged message
diff --git a/docs/admin/companies.md b/docs/admin/companies.md
new file mode 100644
index 000000000..ecaebd011
--- /dev/null
+++ b/docs/admin/companies.md
@@ -0,0 +1,45 @@
+# Companies Management (Recruiter Companies / Employers)
+
+## Current State
+
+**Route:** `/secure/companies`
+**Data source:** `/network/admin/recruiter-companies`
+
+### What Exists
+- **Table columns:** Logo + Company Name, Recruiter (who added it), Industry, Location (city/state), Jobs count, Placements count, Created date
+- **Search:** Text search
+- **Stats banner:** Companies count, Recruiters count, Pending count (reuses network counts)
+- **Pagination:** Standard prev/next
+- **Actions:** None - completely read-only
+- **No detail page** - No click-through
+
+### What's Missing
+
+#### Critical (Phase Priority: High)
+- **Company detail page** - Full profile: jobs posted, placements, recruiters working with this company, billing info, contacts
+- **Company editing** - Edit company name, industry, location, logo, description, website
+- **Company verification** - Verify company legitimacy (domain verification, business registration)
+- **Active jobs view** - All current open jobs for this company with status
+- **Recruiter assignments** - Which recruiters are assigned to this company's jobs
+- **Fee structure** - Default fee percentages, payment terms for this company
+
+#### Important (Phase Priority: Medium)
+- **Contact management** - Multiple contacts per company (hiring managers, HR, billing)
+- **Company notes** - Internal admin notes
+- **Company status management** - Active/suspended/archived with reason
+- **Billing relationship** - Link to billing profile, invoice history, payment status
+- **Communication log** - All admin communications with this company
+- **Job template management** - Saved job templates for this company
+- **Company merge** - Merge duplicate company records (common with recruiter-created companies)
+
+#### Nice to Have
+- **Company health score** - Derived from: payment speed, job fill rate, candidate satisfaction
+- **Hiring trends** - Charts showing hiring patterns over time
+- **Industry benchmarks** - How this company compares to others in same industry
+- **Integration status** - ATS integration health, webhook status
+
+## Implementation Notes
+- Companies come from recruiter_companies, which is a junction of recruiters and companies
+- Need to distinguish between the company entity and the recruiter-company relationship
+- Company detail should link to all related recruiters, not just the one who created it
+- Fee structure should be configurable per company and override defaults
diff --git a/docs/admin/content.md b/docs/admin/content.md
new file mode 100644
index 000000000..6d2eae7f6
--- /dev/null
+++ b/docs/admin/content.md
@@ -0,0 +1,64 @@
+# Content Management (CMS)
+
+## Current State
+
+**Routes:** `/secure/content/pages`, `/secure/content/images`, `/secure/content/navigation`
+
+### Pages (Well-Built)
+- **Page list** with stats cards (total, published, draft), search, filters (app, status, type)
+- **Create Page** modal with title, slug (auto-generated), app, page_type, description
+- **Import JSON** for bulk page creation
+- **Delete pages** with confirmation
+- **Page editor** (`/secure/content/pages/{id}`) - comprehensive block editor:
+ - **Left panel:** Block list with drag-to-reorder, add block, edit/delete per block
+ - **Center:** Live preview pane with desktop/mobile toggle (renders via BaselArticleRenderer)
+ - **Right:** Page settings (title, slug, description, OG image, type, author, read time, app, tags)
+ - **Block types (12):** Hero, Article Body, Benefits Cards, CTA, FAQ, Feature Grid, Full Bleed Image, Inline Image, Pull Quote, Split Editorial, Stats Bar, Timeline
+ - **Block picker** modal with type selection -> dynamic block form
+ - **Publish/Unpublish** toggle, Save, Discard Changes, unsaved changes indicator
+
+### Images (Well-Built)
+- **Grid view** with stats, search, MIME type filter
+- **Upload modal** - File upload (max 10MB), alt text, tags, preview
+- **Image detail modal** - Full preview, edit alt text/tags, delete
+- **Copy URL** on hover for quick sharing
+
+### Navigation (Well-Built)
+- **App selector** tabs (Portal, Candidate, Corporate)
+- **Location selector** (Header, Footer)
+- **Header nav editor** - Items with label, href, sub-items, drag-to-reorder, add/delete
+- **Footer nav editor** - 4 sections: Link Sections, Social Links, Trust Stats, Legal Links
+- **Import/Export** JSON config, Download Schema
+- **Unsaved changes** warning modal
+
+### What's Missing
+
+#### Important (Phase Priority: Medium) - *Most critical CMS features already exist*
+- **Page versioning** - Save versions, revert to previous versions
+- **Content scheduling** - Schedule content to go live/expire at specific dates
+- **Multi-step review** - Draft -> Review -> Published with approval workflow
+
+#### Important (Phase Priority: Medium)
+- **Page templates** - Create page from template (landing page, about page, etc.)
+- **Content scheduling** - Schedule content to go live/expire at specific dates
+- **Global content blocks** - Reusable blocks shared across pages (e.g., CTA that appears everywhere)
+- **Media management** - Better media library with folders, tags, search, bulk upload
+- **Image optimization** - Auto-resize, format conversion, CDN purge
+- **URL/slug management** - Custom URLs, redirects
+- **Content search** - Full-text search across all page content
+- **Content analytics** - Page views, time on page, bounce rate per content page
+- **Blog/article management** - If the platform has a blog, manage posts, categories, authors
+
+#### Nice to Have
+- **Visual A/B testing** - Create variant pages and split traffic
+- **Content localization** - Multi-language content management
+- **Content approval workflow** - Multi-step approval with roles
+- **Broken link checker** - Scan for broken internal/external links
+- **Content calendar** - Visual calendar of scheduled content
+
+## Implementation Notes
+- The page builder is already well-built with many block types
+- Publishing workflow needs: draft (save without publishing), preview (view as published), publish
+- Page versioning should store full page JSON snapshots
+- Multi-site should use a site selector dropdown in the content editor
+- SEO metadata should follow the existing SEO patterns in the corporate app
diff --git a/docs/admin/dashboard-analytics.md b/docs/admin/dashboard-analytics.md
new file mode 100644
index 000000000..0d3c4cc37
--- /dev/null
+++ b/docs/admin/dashboard-analytics.md
@@ -0,0 +1,73 @@
+# Dashboard & Analytics
+
+## Current State
+
+### Dashboard
+**Route:** `/secure` (main page)
+
+- **Stats tiles:** KPI stats from `useAdminStats` hook (time-period aware)
+- **Charts:** Via `useAdminChartData` hook with time period selector
+- **Activity feed:** Recent platform activity
+- **Actions panel:** Quick action shortcuts
+- **Health checks:** Platform health status (service-level)
+- **Time period selector:** 7d, 30d, 90d, 1y
+- **Refresh button:** Manual refresh
+
+### Activity Log
+**Route:** `/secure/activity`
+**Data source:** `/identity/admin/activity`
+
+- **Table columns:** Timestamp, Actor, Action (badge), Entity Type, Entity ID (truncated), Details
+- **Filters:** Search, Entity Type dropdown (User, Recruiter, Job, Application, Placement, Payout)
+- **Pagination:** Standard numbered pagination
+- **Read-only** audit trail
+
+### Metrics
+**Route:** `/secure/metrics`
+
+- **Charts:** User Signups (line), Job Postings (line), Application Volume (bar)
+- **Time period selector:** 7d, 30d, 90d, 1y
+- **Uses shared-charts package** (LineChart, BarChart)
+- **Read-only** visualization
+
+### AI Usage
+**Route:** `/secure/ai-usage`
+
+- **Tabs:** Usage Dashboard, Model Config, Usage Log
+- **Usage Dashboard** - Cost and usage charts
+- **Model Config** - Configure AI providers per operation
+- **Usage Log** - Detailed usage entries
+
+### What's Missing
+
+#### Critical (Phase Priority: High)
+- **Dashboard KPIs should be actionable** - Click any stat to drill into the underlying data (e.g., click "Pending Payouts" -> goes to payouts page filtered to pending)
+- **Revenue dashboard** - Total revenue, MRR/ARR, revenue by recruiter/firm/company, revenue trend
+- **Alerts & anomalies** - Highlight unusual patterns (spike in signups, drop in applications, payment failures)
+- **Activity log: actor links** - Click actor name to go to user profile
+- **Activity log: entity links** - Click entity ID to go to the actual entity
+
+#### Important (Phase Priority: Medium)
+- **Customizable dashboard** - Drag-and-drop dashboard widgets, personalized layouts
+- **Cohort analysis** - User retention, recruiter activation rates, placement success rates over time
+- **Funnel analytics** - Full funnel: signup -> profile complete -> first job -> first placement -> first payout
+- **Geographic analytics** - Map view of jobs, candidates, recruiters by location
+- **Comparison periods** - Compare this month vs last month, this quarter vs last quarter
+- **Export reports** - Export dashboard data as PDF or CSV
+- **Scheduled reports** - Auto-email weekly/monthly reports to admins
+- **Real-time stats** - WebSocket-updated live counters for critical metrics
+- **AI usage budget management** - Set spending limits, alerts when approaching limits
+- **AI model performance tracking** - Accuracy, latency, cost per operation
+
+#### Nice to Have
+- **Custom report builder** - Build custom reports from available data
+- **Dashboard sharing** - Share specific dashboard views via URL
+- **Trend annotations** - Mark events on charts (e.g., "Launched feature X here")
+- **Predictive analytics** - Forecast future trends based on historical data
+
+## Implementation Notes
+- Actionable KPIs: each stat card should link to filtered list view
+- Revenue dashboard is business-critical for a marketplace
+- Activity log links need the entity type to determine the correct route
+- Anomaly detection could start simple (% change from average) before getting to ML
+- AI usage budget should integrate with provider dashboards (OpenAI, Anthropic)
diff --git a/docs/admin/decision-log.md b/docs/admin/decision-log.md
new file mode 100644
index 000000000..7393f1bf3
--- /dev/null
+++ b/docs/admin/decision-log.md
@@ -0,0 +1,41 @@
+# Decision Log (Automated Decisions Audit)
+
+## Current State
+
+**Route:** `/secure/decision-log`
+**Data source:** `/decisions/admin/log`
+
+### What Exists
+- **Table columns:** Date, Decision Type (badge), Entity name/type, Outcome (approved/rejected/flagged/escalated/pending), Confidence (progress bar + percentage), Decided By
+- **Filters:** Decision type dropdown (All, Match Scoring, Fraud Check, Recruiter Approval, Payment Hold)
+- **Sorting:** By date, decision type, entity, confidence
+- **Actions:** None - completely read-only
+- **No detail view** - No click-through to decision details
+
+### What's Missing
+
+#### Critical (Phase Priority: High)
+- **Decision detail page** - Full view: input data, reasoning breakdown, confidence factors, related entity, outcome, who/what made the decision
+- **Decision override** - Admin ability to override automated decisions with justification
+- **Decision appeal tracking** - Track when users contest automated decisions
+- **Link to source** - Direct link to the entity affected by the decision (recruiter, match, payout)
+
+#### Important (Phase Priority: Medium)
+- **Decision analytics** - Accuracy rates, override rates, confidence distribution, decision volume trends
+- **Decision comparison** - Compare similar decisions to check consistency
+- **Decision rules management** - Configure the rules that drive automated decisions
+- **Override audit trail** - Track all admin overrides with before/after state
+- **False positive/negative tracking** - Track decision quality over time
+- **Reasoning transparency** - Expandable reasoning for each decision showing the logic chain
+- **Export** - Export decision log for compliance reporting
+
+#### Nice to Have
+- **Decision tree visualization** - Visual representation of decision logic
+- **A/B testing decisions** - Compare different decision models
+- **Decision SLA** - Track how long automated decisions take
+
+## Implementation Notes
+- Decision override should create a new log entry linked to the original
+- Reasoning should be stored as structured data (not just a text blob) for analysis
+- Decision analytics are critical for tuning automated systems
+- Export should include full reasoning for compliance/audit purposes
diff --git a/docs/admin/firms.md b/docs/admin/firms.md
new file mode 100644
index 000000000..e5a6611bb
--- /dev/null
+++ b/docs/admin/firms.md
@@ -0,0 +1,45 @@
+# Firms Management (Recruiting Firms)
+
+## Current State
+
+**Route:** `/secure/firms`
+**Data source:** `/network/admin/firms`
+
+### What Exists
+- **Table columns:** Firm Name / Owner Email, Status (active/suspended), Marketplace status (Approved/Pending/Not Listed), Slug, Created date
+- **Filters:** Marketplace status pills (All, Pending Approval, Approved, Not Listed)
+- **Search:** Text search
+- **Stats banner:** Total Firms, Pending Approval, Marketplace Active
+- **Pagination:** Standard prev/next
+- **Actions:** Marketplace Approve (for pending), Marketplace Revoke (for approved) - with confirmation modals
+- **No detail page** - No click-through
+
+### What's Missing
+
+#### Critical (Phase Priority: High)
+- **Firm detail page** - Full profile: members, jobs worked, placements, revenue, marketplace listing, reputation
+- **Firm suspension** - Suspend entire firm (cascade to all members) with reason
+- **Member management** - View all firm members, their roles within the firm, add/remove members
+- **Firm profile editing** - Edit firm details (name, description, specialties, logo, website)
+- **Marketplace listing management** - Edit the firm's public marketplace profile, preview public listing
+
+#### Important (Phase Priority: Medium)
+- **Revenue tracking** - Total revenue, revenue by recruiter, revenue trend
+- **Compliance management** - Track firm-level compliance (business license, insurance, bond)
+- **Firm notes** - Internal admin notes
+- **Billing management** - Firm-level billing profile, invoice history
+- **Firm-level fee overrides** - Override platform default split for this firm
+- **Activity log** - Firm-wide activity (all member actions)
+- **Invitation management** - See pending firm invitations, revoke invitations
+
+#### Nice to Have
+- **Firm analytics dashboard** - Mini-dashboard per firm with key metrics
+- **Firm comparison** - Compare firms side-by-side
+- **Firm directory management** - Manage how firm appears in directory/search
+- **Onboarding progress** - Track firm onboarding completion
+
+## Implementation Notes
+- Firm suspension should cascade: suspend all member recruiters, pause all active jobs
+- Marketplace approval should trigger email notification
+- Member management needs role context (owner, admin, member)
+- Revenue tracking should pull from placement/payout data
diff --git a/docs/admin/fraud-trust.md b/docs/admin/fraud-trust.md
new file mode 100644
index 000000000..cf0e212ca
--- /dev/null
+++ b/docs/admin/fraud-trust.md
@@ -0,0 +1,67 @@
+# Fraud Detection & Trust Management
+
+## Current State
+
+### Fraud Detection
+**Route:** `/secure/fraud`
+**Data source:** `/fraud/admin/signals`
+
+- **Table columns:** Severity (icon + badge), Entity name/type, Signal Type (badge), Description, Detected date, Status (Active/Resolved)
+- **Filters:** Severity dropdown (All, Critical, High, Medium, Low), Resolved status (Active Only, Resolved Only, All)
+- **Actions:** None - completely read-only
+- **No detail view** - No click-through
+
+### Reputation
+**Route:** `/secure/reputation`
+**Data source:** `/trust/admin/reputation`
+
+- **Table columns:** Entity name/type (with tier icon), Tier (platinum/gold/silver/bronze/new), Score (progress bar), Positive Rate, Total Reviews, Updated date
+- **Filters:** Tier dropdown, Entity type dropdown (Recruiters, Companies, Candidates)
+- **Sorting:** By entity name, tier, score, positive rate, reviews, updated
+- **Actions:** None - completely read-only
+- **No detail view** - No click-through
+
+### Ownership Audit
+**Route:** `/secure/ownership`
+**Data source:** `/trust/admin/ownership`
+
+- **Table columns:** Verification Status (icon + badge), Entity name/type, Owner name/type, Verified date, Created date
+- **Filters:** Verification status dropdown (All, Verified, Pending, Disputed, Failed)
+- **Sorting:** By status, entity, owner, dates
+- **Actions:** None - completely read-only
+- **No detail view** - No click-through
+
+### What's Missing
+
+#### Critical (Phase Priority: High)
+- **Fraud signal investigation** - Click-through to full signal details: entity profile, evidence, related signals, risk assessment
+- **Fraud resolution workflow** - Resolve signals with action taken (dismissed, warning sent, account suspended, referred to legal)
+- **Entity suspension from fraud** - Direct action to suspend a user/recruiter/company from a fraud signal
+- **Reputation detail page** - Click-through: review breakdown, score history, factor analysis
+- **Ownership verification actions** - Approve/reject pending verifications, request additional documentation
+- **Fraud alerts** - Real-time alerts for critical/high severity fraud signals
+
+#### Important (Phase Priority: Medium)
+- **Reputation score override** - Admin ability to adjust reputation scores with justification
+- **Reputation score history** - Chart showing score changes over time
+- **Fraud investigation notes** - Internal notes on fraud investigations
+- **Related signals grouping** - Group related fraud signals (same entity, same pattern)
+- **Fraud rules management** - Configure what triggers fraud signals, adjust thresholds
+- **Ownership dispute resolution** - Workflow for resolving ownership disputes
+- **Trust dashboard** - Overview: fraud signal trends, reputation distribution, verification backlog
+- **Watchlist management** - Add entities to a monitoring watchlist
+- **IP/device tracking** - Track suspicious IP addresses and device fingerprints
+- **Bulk verification** - Batch approve/reject pending ownership verifications
+
+#### Nice to Have
+- **Fraud pattern analysis** - ML-based pattern detection across signals
+- **Risk scoring model** - Configurable risk scoring weights
+- **Fraud reporting** - Generate fraud reports for compliance
+- **Reputation leaderboard** - Top/bottom reputation entities for recognition/intervention
+
+## Implementation Notes
+- Fraud resolution should cascade: if suspending an entity, update all their active records
+- Reputation score adjustments should be audit-logged with reason
+- Ownership verification may require document upload/review capability
+- Fraud alerts should integrate with notification system (and potentially external channels like Slack)
+- All trust actions should be dual-logged (admin activity + trust-specific audit)
diff --git a/docs/admin/jobs.md b/docs/admin/jobs.md
new file mode 100644
index 000000000..20a7069d3
--- /dev/null
+++ b/docs/admin/jobs.md
@@ -0,0 +1,53 @@
+**COMPLETED**
+
+# Jobs Management
+
+## Current State
+
+**Route:** `/secure/jobs`
+**Data source:** `/ats/admin/jobs`
+
+### What Exists
+- **Table columns:** Title/Company, Status (badge), Commute type, Level, Created date
+- **Filters:** Search, Status dropdown, Commute dropdown, Level dropdown
+- **Stats banner:** Active, Draft, Paused, Closed counts
+- **Pagination:** Standard numbered pagination
+- **Row click:** Routes to `/secure/jobs/{id}` (detail page exists)
+- **Actions:** Status transitions (Activate, Pause, Close, Reopen) with confirmation modals
+
+### Detail Page (`/secure/jobs/{id}`)
+- Job overview with metadata
+- Candidate list for this job
+- Read-only display
+
+### What's Missing
+
+#### Critical (Phase Priority: High)
+- **Job editing** - Edit all job fields (title, description, requirements, salary, location, commute type, level)
+- **Application pipeline view** - Visual pipeline of applications by stage for this job (kanban or funnel view)
+- **Recruiter assignment management** - Assign/unassign recruiters to this job, set split percentages
+- **Job duplication** - Clone a job to create a similar posting
+- **Feature/boost management** - Featured job placement, boost visibility
+
+#### Important (Phase Priority: Medium)
+- **Application stage management** - Move applications between stages from the job detail
+- **Job analytics** - Views, applications, conversion rates, time-in-stage averages
+- **Matching controls** - Trigger AI matching manually, view match quality distribution
+- **Job notes** - Internal admin notes
+- **Company contact for job** - Who at the company is the hiring manager
+- **Job expiration management** - Set/extend expiration dates, auto-close rules
+- **Salary benchmarking** - Compare salary range to market data
+- **Bulk job actions** - Select multiple jobs, bulk pause/close/reopen
+
+#### Nice to Have
+- **Job performance comparison** - Compare similar jobs' performance
+- **SEO preview** - Preview how job appears in search results
+- **Distribution tracking** - Where job is syndicated, performance per channel
+- **Application source attribution** - Which recruiters/channels drove applications
+- **Job template creation** - Save as template for this company
+
+## Implementation Notes
+- Job editing should validate against required fields before allowing activate
+- Pipeline view should use stages from application_stages enum
+- Recruiter assignment should respect firm-level agreements
+- Analytics should show trend over time, not just current numbers
diff --git a/docs/admin/matches.md b/docs/admin/matches.md
new file mode 100644
index 000000000..1b318c408
--- /dev/null
+++ b/docs/admin/matches.md
@@ -0,0 +1,48 @@
+# Matches Management
+
+## Current State
+
+**Route:** `/secure/matches`
+**Data source:** `/matching/admin/matches`
+
+### What Exists
+- **Table columns:** Candidate Name, Job Title, Company, Score (progress bar + percentage), Status (pending/accepted/rejected/expired), Created date
+- **Sorting:** By score (desc default), candidate, job, company, created date
+- **Row click:** Routes to `/secure/matches/{id}` (detail page exists)
+- **No filters** - No search, no status filter, no pagination
+- **No actions** - Completely read-only
+
+### Detail Page (`/secure/matches/{id}`)
+- Match overview with metadata
+- Match factors breakdown
+- Read-only display
+
+### What's Missing
+
+#### Critical (Phase Priority: High)
+- **Filters and search** - Filter by status, score range, date range, candidate, job, company
+- **Pagination** - Currently loads all matches without pagination
+- **Match detail improvements** - Show full factor breakdown with weights, explain why this score
+- **Manual match creation** - Admin ability to create a manual match (bypass AI)
+- **Match status management** - Override match status (e.g., force-expire, re-activate)
+
+#### Important (Phase Priority: Medium)
+- **Match quality monitoring** - Dashboard showing match quality distribution, acceptance rates, false positive/negative rates
+- **Matching engine controls** - View/adjust matching parameters, trigger re-scoring
+- **Match feedback loop** - Track outcomes of accepted matches (did they lead to placements?)
+- **Bulk operations** - Bulk expire old matches, bulk re-score
+- **Match comparison** - Compare multiple matches for same job or same candidate
+- **Score threshold management** - Set minimum score thresholds for different match types
+- **Match notifications** - View/manage which matches triggered notifications
+
+#### Nice to Have
+- **A/B testing for matching** - Compare different matching algorithms
+- **Match explanations** - Human-readable explanations of why a match scored high/low
+- **Match trends** - Charts showing matching quality over time
+- **Feedback collection** - Track recruiter feedback on match quality
+
+## Implementation Notes
+- Match factors should show: skill match, experience match, location match, salary match, culture fit, etc.
+- Manual matches should be flagged as "admin-created" in audit trail
+- Match quality monitoring is critical for validating the AI engine
+- Re-scoring should queue via RabbitMQ, not block the UI
diff --git a/docs/admin/notifications.md b/docs/admin/notifications.md
new file mode 100644
index 000000000..26c95d557
--- /dev/null
+++ b/docs/admin/notifications.md
@@ -0,0 +1,40 @@
+# Site Notifications Management
+
+## Current State
+
+**Route:** `/secure/notifications`
+**Data source:** `/notification/admin/site-notifications`
+
+### What Exists
+- **Table columns:** Title/Message, Type (badge), Severity (info/warning/critical), Active toggle, Expires date, Created date
+- **Filters:** Active status pills (All, Active, Inactive), Severity dropdown
+- **Pagination:** Standard prev/next
+- **Actions:**
+ - **Create** - Button to create new notification (opens form)
+ - **Edit** - Edit existing notification
+ - **Toggle active** - Toggle switch to activate/deactivate
+ - **Delete** - Delete notification with confirmation
+- **This is the most complete feature in the admin app** - Full CRUD
+
+### What's Missing
+
+#### Important (Phase Priority: Medium)
+- **Notification preview** - Preview how notification looks to users before publishing
+- **Targeting** - Target notifications to specific user segments (role, firm, etc.)
+- **Scheduling** - Schedule notification to go live at a future date/time
+- **Notification analytics** - View count, dismiss rate, click-through rate
+- **Notification templates** - Save and reuse notification templates
+- **Rich content** - Support markdown or basic HTML in notification body
+- **Priority ordering** - If multiple active, set display order
+
+#### Nice to Have
+- **A/B testing** - Test different notification messages
+- **Auto-expiration rules** - Auto-deactivate after N days or N views
+- **Notification history** - Archive of all past notifications with performance data
+- **Multi-channel** - Send same notification as in-app, email, or push
+
+## Implementation Notes
+- This feature is already well-built with full CRUD
+- Priority additions: targeting and scheduling
+- Preview should show exact rendering across portal, candidate, and corporate apps
+- Analytics tracking needs frontend event emission (notification viewed, dismissed, clicked)
diff --git a/docs/admin/organizations.md b/docs/admin/organizations.md
new file mode 100644
index 000000000..0dddf6cfe
--- /dev/null
+++ b/docs/admin/organizations.md
@@ -0,0 +1,39 @@
+# Organizations Management
+
+## Current State
+
+**Route:** `/secure/organizations`
+**Data source:** `/identity/admin/organizations`
+
+### What Exists
+- **Table columns:** Name/Slug, Type, Member Count, Owner Name, Created date, Status (active/inactive/suspended)
+- **Filters:** Status dropdown (All, Active, Inactive, Suspended)
+- **Search:** Text search
+- **Pagination:** Standard numbered pagination
+- **Actions:** None - completely read-only
+- **No detail page** - No click-through
+
+### What's Missing
+
+#### Critical (Phase Priority: High)
+- **Organization detail page** - Full profile: members, type, settings, associated firm, activity
+- **Status management** - Suspend/reactivate organization with reason and audit trail
+- **Member management** - View members, their roles, add/remove members, transfer ownership
+- **Organization editing** - Edit name, slug, type, settings
+
+#### Important (Phase Priority: Medium)
+- **Clerk org sync** - Show sync status with Clerk organizations, trigger re-sync
+- **Organization notes** - Internal admin notes
+- **Associated entities** - Link to firm, recruiters, billing profile
+- **Invitation management** - View pending invitations, revoke
+- **Activity log** - Organization-level activity trail
+
+#### Nice to Have
+- **Organization merge** - Merge duplicate organizations
+- **SSO configuration** - View/manage SSO settings for enterprise orgs
+- **Usage analytics** - How active is this organization
+
+## Implementation Notes
+- Organizations map to Clerk organizations - changes should sync
+- Suspension should cascade to org members' access
+- Ownership transfer needs confirmation from both parties
diff --git a/docs/admin/payouts-billing.md b/docs/admin/payouts-billing.md
new file mode 100644
index 000000000..6bb25bf36
--- /dev/null
+++ b/docs/admin/payouts-billing.md
@@ -0,0 +1,68 @@
+# Payouts & Billing Management
+
+## Current State
+
+**Route:** `/secure/payouts` (tabbed: Transactions, Audit, Escrow, Schedules)
+**Route:** `/secure/billing-profiles`
+**Data sources:** `/billing/admin/payouts`, `/billing/admin/escrow`, `/billing/admin/payouts/audit`, `/billing/admin/schedules`, `/billing/admin/billing-profiles`
+
+### Payouts (Transactions Tab)
+- **Table columns:** Truncated ID, Recruiter name/email, Amount (currency), Status (badge), Type, Created date
+- **Filters:** Search, Status dropdown (Pending, Processing, Paid, Failed, On Hold, Reversed)
+- **Stats banner:** Pending Payouts, Active Escrow, Billing Profiles
+- **Row click:** Opens detail modal
+- **Detail modal:** ID, Recruiter, Email, Amount, Type, Placement ID, Stripe Transfer ID, Failure Reason, Timeline (created/completed/updated)
+- **Actions:** None - detail modal is read-only
+
+### Escrow Tab
+- **Table columns:** Escrow ID, Placement ID, Amount, Status, Hold Reason, Hold Date, Release Date
+- **Filters:** Search, Status dropdown
+- **Actions:** "Release" button for active holds (opens modal)
+
+### Audit Tab
+- **Table columns:** Action, Payout ID, Actor, Details, Timestamp
+- **Read-only** audit trail
+
+### Schedules Tab
+- **Table columns:** Schedule Name, Frequency, Next Run, Last Run, Status
+- **Read-only** display
+
+### Billing Profiles
+- **Table columns:** Profile Name, Entity type, Profile Type, Status, Created date
+- **Search:** Text search
+- **Read-only** display
+
+### What's Missing
+
+#### Critical (Phase Priority: High)
+- **Payout approval workflow** - Approve/reject pending payouts, require dual approval for large amounts
+- **Manual payout creation** - Create ad-hoc payouts (adjustments, bonuses, refunds)
+- **Payout hold/release** - Place a payout on hold with reason, release held payouts
+- **Payout retry** - Retry failed payouts, with ability to update payment method
+- **Escrow management** - Create manual escrow holds, adjust hold amounts, extend release dates
+- **Financial dashboard** - Total pending payouts, monthly disbursement, escrow balance, revenue, failed payment rate
+
+#### Important (Phase Priority: Medium)
+- **Billing profile editing** - Edit billing profile details, payment methods, tax info
+- **Billing profile creation** - Create billing profiles for entities
+- **Invoice generation** - Generate and send invoices for placements
+- **Refund processing** - Process refunds for cancelled placements
+- **Payment method management** - View/manage Stripe connected accounts, verify bank details
+- **Schedule management** - Create/edit/pause payout schedules
+- **Batch processing** - Process multiple payouts in batch
+- **Reconciliation tools** - Compare Stripe records vs platform records, flag discrepancies
+- **Tax reporting** - Generate 1099 data, tax withholding management
+- **Dispute management** - Handle payment disputes, chargebacks
+
+#### Nice to Have
+- **Revenue analytics** - Revenue by period, by recruiter, by company, by firm
+- **Cash flow forecasting** - Upcoming payouts, expected revenue, escrow release calendar
+- **Payment health monitoring** - Track failed payment rates, average processing time
+- **Stripe dashboard link** - Direct links to Stripe dashboard for transactions
+
+## Implementation Notes
+- Payout approval should enforce: amount validation, recruiter active status, placement verified
+- Dual approval threshold should be configurable in settings
+- Refunds should create a negative payout record and link to the original
+- Reconciliation should run as a scheduled job with admin-visible results
+- Financial data is sensitive - all actions should be audit-logged
diff --git a/docs/admin/placements.md b/docs/admin/placements.md
new file mode 100644
index 000000000..3d02c736f
--- /dev/null
+++ b/docs/admin/placements.md
@@ -0,0 +1,45 @@
+# Placements Management
+
+## Current State
+
+**Route:** `/secure/placements`
+**Data source:** `/ats/admin/placements`
+
+### What Exists
+- **Table columns:** Candidate name (or truncated ID), Job ID (truncated), Company name, Start Date, Status (badge), Fee (formatted currency)
+- **Filters:** Status pills (All, Active, Pending, Completed, Cancelled)
+- **Search:** Text search
+- **Pagination:** Standard prev/next
+- **Actions:** None - completely read-only
+- **No detail page** - No click-through
+
+### What's Missing
+
+#### Critical (Phase Priority: High)
+- **Placement detail page** - Full view: candidate, job, company, recruiter(s), fee breakdown, split details, guarantee period, payout status, timeline
+- **Fee management** - View/adjust fee amount, split percentages between recruiters, override calculations
+- **Status management** - Transition placement status with reason (e.g., cancel placement, mark completed)
+- **Guarantee period tracking** - Show guarantee period dates, days remaining, risk assessment
+- **Payout linkage** - Direct link to associated payout(s), escrow holds, billing
+
+#### Important (Phase Priority: Medium)
+- **Split details view** - Who gets what percentage, calculated amounts per party
+- **Placement editing** - Edit start date, end date, fee, salary details
+- **Placement notes** - Internal admin notes (e.g., "Candidate requested delayed start")
+- **Falloff tracking** - If candidate leaves during guarantee, track reason and handle refund
+- **Placement verification** - Confirm candidate actually started, verify with company
+- **Revenue recognition** - Track when revenue is recognized vs when payment is collected
+- **Placement timeline** - Full history: created, verified, paid, completed/cancelled
+- **Related placements** - Show if this candidate was placed before, or if this job had other placements
+
+#### Nice to Have
+- **Placement analytics** - Success rate, average fee, average time-to-placement
+- **Guarantee risk scoring** - AI-based prediction of falloff risk
+- **Bulk status updates** - Mark multiple placements as completed
+- **Export** - Export placement data with financial details
+
+## Implementation Notes
+- Placement detail must show the full financial picture: fee, splits, payouts, escrow
+- Guarantee tracking is business-critical (escrow release depends on it)
+- Falloff workflow: cancel placement -> trigger escrow refund -> notify all parties
+- Job ID column should show job title (currently shows truncated UUID - this is a data join issue)
diff --git a/docs/admin/recruiters.md b/docs/admin/recruiters.md
new file mode 100644
index 000000000..3b88d02e1
--- /dev/null
+++ b/docs/admin/recruiters.md
@@ -0,0 +1,49 @@
+**COMPLETED**
+
+# Recruiters Management
+
+## Current State
+
+**Route:** `/secure/recruiters`
+**Data source:** `/network/admin/recruiters`
+
+### What Exists
+- **Table columns:** Name/Email, Status (badge), Tagline (truncated), Joined date
+- **Filters:** Status pills (All, Pending, Active, Suspended)
+- **Search:** Text search
+- **Stats banner:** Total Recruiters, Pending Approval, Companies
+- **Pagination:** Standard prev/next
+- **Actions:** Approve (pending/suspended -> active), Suspend (active -> suspended) with confirmation modal
+- **No detail page** - No click-through to individual recruiter
+
+### What's Missing
+
+#### Critical (Phase Priority: High)
+- **Recruiter detail page** - Full profile view: bio, specialties, firm, activity, jobs worked, placements, earnings, reputation score
+- **Profile editing** - Admin ability to edit recruiter profile fields (tagline, bio, specialties, etc.)
+- **Rejection with reason** - When denying pending recruiter, require and store a rejection reason; notify the recruiter
+- **Verification status** - Identity/credential verification workflow (license verification, background check status)
+- **Performance metrics** - Per-recruiter: placements made, revenue generated, average time-to-fill, active jobs
+
+#### Important (Phase Priority: Medium)
+- **Firm association management** - View/change which firm a recruiter belongs to
+- **Commission/split override** - Override default split percentages for specific recruiters
+- **Recruiter notes** - Internal admin notes (e.g., "Warned about behavior on 3/15")
+- **Communication log** - See all admin-to-recruiter communications
+- **Bulk approve/reject** - Select multiple pending recruiters and batch process
+- **Re-verify** - Trigger re-verification for compliance
+- **Earnings history** - Full payout history for this recruiter
+- **Compliance flags** - Track compliance issues (expired licenses, incomplete profiles)
+
+#### Nice to Have
+- **Recruiter comparison** - Side-by-side comparison of recruiter performance
+- **Onboarding progress** - Track what onboarding steps are complete
+- **Referral tracking** - Who referred this recruiter, who have they referred
+- **Territory/specialty tags** - Admin-managed tags for categorization
+- **Leaderboard position** - Where this recruiter ranks overall
+
+## Implementation Notes
+- Detail page should show: Overview tab, Jobs tab, Placements tab, Earnings tab, Activity tab, Notes tab
+- Status changes should fire RabbitMQ events for notification
+- Rejection reason should be emailed to the recruiter
+- Compliance flags should appear as alerts on the detail page
diff --git a/docs/admin/settings.md b/docs/admin/settings.md
new file mode 100644
index 000000000..b19293b99
--- /dev/null
+++ b/docs/admin/settings.md
@@ -0,0 +1,51 @@
+# Platform Settings
+
+## Current State
+
+**Route:** `/secure/settings`
+**Data source:** `/identity/admin/settings`
+
+### What Exists
+- **Settings form** - Loads settings from API, renders a form via `SettingsForm` component
+- **Graceful fallback** - Uses defaults if settings endpoint is unavailable
+- **General Settings section:**
+ - Platform Name (text input, default "Splits Network")
+ - Support Email (email input)
+ - Maintenance Mode toggle (default off)
+- **Feature Flags section:**
+ - AI Matching toggle (default on)
+ - Fraud Detection toggle (default on)
+ - Automation toggle (default off)
+ - Escrow toggle (default on)
+- **Save button** with loading state and toast notifications
+
+### What's Missing
+
+#### Critical (Phase Priority: High)
+- **Platform configuration** - Fee defaults (platform cut, recruiter split), guarantee period length, minimum payout threshold
+- **Additional feature flags** - Chat, marketplace listing, candidate self-service, notifications
+- **Email configuration** - Default sender, reply-to, notification preferences
+- **Registration settings** - Open/closed registration, recruiter approval required, invite-only mode
+
+#### Important (Phase Priority: Medium)
+- **Branding settings** - Platform name, logo, colors (if multi-tenant)
+- **Integration settings** - API keys for third-party services (viewable status, not raw keys)
+- **Rate limiting** - Configure API rate limits
+- **Webhook configuration** - Manage outbound webhooks
+- **Audit logging settings** - Configure what gets logged, retention period
+- **Email template management** - Preview/test email templates
+- **Payment settings** - Stripe configuration, payout schedule defaults, currency settings
+- **Security settings** - Password policies, 2FA requirements, session timeout
+- **Notification defaults** - Default notification channels, frequencies
+
+#### Nice to Have
+- **Settings history** - Track who changed what setting and when
+- **Environment comparison** - Compare settings between staging and production
+- **Settings import/export** - Backup and restore configuration
+- **Feature flag scheduling** - Schedule feature toggles for future dates
+
+## Implementation Notes
+- Settings changes should be audit-logged with before/after values
+- Feature flags should take effect immediately without deployment
+- Sensitive settings (API keys) should show masked values with reveal toggle
+- Maintenance mode should be easily accessible (maybe in header bar)
diff --git a/docs/admin/support.md b/docs/admin/support.md
new file mode 100644
index 000000000..2b4f8d38f
--- /dev/null
+++ b/docs/admin/support.md
@@ -0,0 +1,67 @@
+# Support Management (Tickets + Live Chat)
+
+## Current State
+
+### Support Tickets
+**Route:** `/secure/support-tickets`
+**Data source:** `/support/admin/support/tickets`
+
+**List page:**
+- **Table columns:** (via TicketTable component)
+- **Filters:** Status pills (All, Open, In Progress, Resolved, Closed)
+- **Search:** Text search
+- **Stats banner:** Open, In Progress, Resolved, Closed counts
+- **Pagination:** Standard prev/next
+
+**Detail page (`/secure/support-tickets/{id}`):**
+- **Original message** display
+- **Replies timeline** - Threaded replies with sender type badges (admin/visitor)
+- **Reply form** - Textarea to compose and send replies (emails visitor)
+- **Sidebar:** Ticket ID, Category, Source app, Visitor name/email, Authenticated status, Page URL, Created date, Resolved date
+- **Status management** - Buttons to change status (open, in_progress, resolved, closed)
+- **Claim ticket** - Assign yourself as the handler
+- **This is the second-most complete feature** - Has real workflow management
+
+### Support Chat
+**Route:** `/secure/support-chat`
+**Data source:** `/support/admin/support/conversations`
+
+- **Queue panel** - Real-time conversation list with status filter, unread tracking
+- **Thread panel** - Full chat thread with message composition
+- **Real-time** - WebSocket-based with presence tracking
+- **Admin presence** - Shows admin is available
+- **This is well-built** - Real-time chat with good UX
+
+### What's Missing
+
+#### Critical (Phase Priority: High)
+- **Ticket assignment** - Assign tickets to specific admins (not just claim)
+- **Ticket priority** - Priority levels (urgent, high, normal, low) with SLA tracking
+- **Ticket categories management** - Configure available categories
+- **Internal notes** - Admin-only notes on tickets (not visible to visitor)
+- **Canned responses** - Pre-written responses for common questions
+
+#### Important (Phase Priority: Medium)
+- **SLA monitoring** - Response time targets, resolution time targets, SLA breach alerts
+- **Auto-assignment rules** - Route tickets to specific admins based on category/source
+- **Ticket merging** - Merge duplicate tickets from same visitor
+- **Related tickets** - Show other tickets from same user
+- **Customer satisfaction** - Post-resolution satisfaction survey tracking
+- **Support analytics** - Response times, resolution rates, volume by category, by source app
+- **Ticket tags** - Custom tags for categorization and tracking
+- **Escalation workflow** - Escalate to senior support with full context
+- **Chat-to-ticket** - Convert a live chat into a support ticket for follow-up
+- **Knowledge base integration** - Suggest KB articles while composing replies
+
+#### Nice to Have
+- **Support dashboard** - Overview of all support metrics, active conversations, pending tickets
+- **Agent performance** - Track individual admin support performance
+- **Macro/automation** - Auto-respond to common queries, auto-categorize
+- **Multi-channel inbox** - Unified view of support from chat, tickets, and email
+
+## Implementation Notes
+- Ticket detail page is well-built but needs internal notes and priority
+- SLA tracking needs defined thresholds per priority level
+- Canned responses should be shared across all admins, with personal favorites
+- Chat-to-ticket conversion should preserve the full conversation history
+- Support analytics should break down by source_app (portal, candidate, corporate)
diff --git a/docs/admin/users.md b/docs/admin/users.md
new file mode 100644
index 000000000..c8b9072fb
--- /dev/null
+++ b/docs/admin/users.md
@@ -0,0 +1,48 @@
+**COMPLETED**
+
+# Users Management
+
+## Current State
+
+**Route:** `/secure/users`
+**Data source:** `/identity/admin/users`
+
+### What Exists
+- **Table columns:** Avatar, Name, Email, Roles (badge list), Created, Last Active
+- **Search:** Text search on users
+- **Stats banner:** Total Users, Admins count
+- **Pagination:** Standard prev/next
+- **Row click:** Routes to `/secure/users/{id}` (detail page may not exist yet)
+- **Actions:** None - completely read-only
+
+### What's Missing
+
+#### Critical (Phase Priority: High)
+- **User detail page** - Click-through to full user profile with all related data (recruiter profile, candidate profile, activity, sessions)
+- **Role management** - Ability to assign/remove roles (admin, recruiter, candidate, etc.)
+- **Account suspension/ban** - Suspend or ban user accounts with reason tracking
+- **Impersonation** - "View as user" for debugging (read-only impersonation)
+- **Force password reset** - Trigger password reset for security incidents
+- **Activity history** - Show user's recent actions, login history, IP addresses
+
+#### Important (Phase Priority: Medium)
+- **Merge duplicate accounts** - Tool to merge when same person has multiple accounts
+- **Export user data** - GDPR-compliant data export for a specific user
+- **Delete/anonymize user** - GDPR right-to-be-forgotten compliance
+- **Bulk actions** - Select multiple users, bulk assign roles, bulk suspend
+- **User notes** - Internal admin notes attached to user profiles
+- **Email user** - Send direct email from admin interface
+- **Registration source tracking** - See how users found the platform
+- **Clerk sync status** - Show if user is in sync with Clerk, trigger re-sync
+
+#### Nice to Have
+- **User timeline** - Visual timeline of all user events (signup, first job, first placement, etc.)
+- **Linked accounts** - Show all related entities (recruiter profile, candidate profile, firm memberships)
+- **Session management** - View active sessions, force logout
+- **Login attempt log** - Failed login attempts for security monitoring
+
+## Implementation Notes
+- User detail page should be a tabbed layout: Overview, Activity, Roles, Security, Notes
+- Role changes should be audit-logged
+- Suspension should cascade (suspend recruiter profile too)
+- Impersonation must be logged and time-limited
diff --git a/docs/social-media-30-day-prompts.md b/docs/social-media-30-day-prompts.md
new file mode 100644
index 000000000..52e59a864
--- /dev/null
+++ b/docs/social-media-30-day-prompts.md
@@ -0,0 +1,407 @@
+# 30-Day Social Media Content Prompts
+
+> **90 Claude prompts** — 1 per audience per day for 30 days.
+> Paste any prompt into Claude to generate a ready-to-post social media caption.
+>
+> **Audiences:** Candidate | Recruiter | Company
+> **Platform:** Splits Network / Applicant Network / Employment Networks
+
+---
+
+## Day 1 — Ghosting
+
+**Candidate:**
+Write a short, punchy LinkedIn post (under 200 words) from the perspective of a job seeker who is tired of being ghosted after interviews. Reference the stat that 90% of job seekers experience post-interview ghosting. Pivot to how Splits Network solves this — every application stage transition triggers automatic notifications, candidates see real-time status updates on a visual timeline, and stale applications get flagged by automation. End with a call to action to check out Applicant Network. Tone: empathetic but empowering, not bitter. No hashtag spam — 3 max.
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) aimed at recruiters about the reputational cost of ghosting candidates. Reference the stat that 90% of job seekers report being ghosted. Position Splits Network as the solution — automated stage-transition notifications mean candidates stay informed without the recruiter lifting a finger, and the gamification system rewards responsiveness with XP and badges that build your public reputation score. Tone: direct, professional, no-nonsense. 3 hashtags max.
+
+**Company:**
+Write a LinkedIn post (under 200 words) targeting hiring managers about how candidate ghosting damages employer brand. Reference the stat that weak employer branding doubles cost-per-hire. Position the Employment Networks ecosystem as a transparency layer — structured pipelines with defined stages, automatic notifications at every transition, and real-time visibility for all parties. Tone: strategic, boardroom-appropriate. 3 hashtags max.
+
+---
+
+## Day 2 — Ghost Jobs
+
+**Candidate:**
+Write a social media post (under 200 words) for job seekers frustrated by "ghost jobs" — unfilled postings that waste their time. Reference that 18-22% of job listings are ghost jobs, spiking to 31% in corporate services. Position Applicant Network as different: every job on the platform has a recruiter actively working to fill it with real financial incentive (split-fee arrangements). Recruiters have visible reputation scores and badge histories so you know they're legit. Tone: validating frustration, then offering a real alternative.
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) for recruiters about how ghost job postings poison the talent marketplace for everyone. Explain that when candidates lose trust in job boards, even legitimate postings get ignored. Position Splits Network as a trust-verified marketplace where every listing is backed by recruiter reputation scores, financial commitment, and platform accountability. Tone: thought leadership, "we need to fix our industry."
+
+**Company:**
+Write a LinkedIn post (under 200 words) for employers about the hidden cost of ghost job postings — eroded candidate trust means your real openings get fewer quality applicants. Position Employment Networks as a verified marketplace where active roles are tied to accountable recruiters. Tone: data-driven, strategic.
+
+---
+
+## Day 3 — Application Abandonment
+
+**Candidate:**
+Write a social media post (under 150 words) aimed at job seekers about how they shouldn't have to spend 45 minutes filling out forms they've already completed. Reference that 60% of candidates abandon lengthy applications. Introduce Applicant Network's Smart Resume — upload your resume once, AI parses it into a structured profile, and you can apply with a click. You can even apply through ChatGPT. Tone: casual, relatable, "there's a better way."
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) for recruiters about how clunky application processes cost them top talent — 60% of candidates abandon applications when forms feel too long. Position Splits Network's AI-powered resume parsing and streamlined 3-step candidate onboarding as a competitive advantage that delivers more completed applications and higher-quality candidate data. Tone: practical, ROI-focused.
+
+**Company:**
+Write a LinkedIn post (under 200 words) for hiring managers about the cost of application abandonment. Frame the 60% abandonment rate as a leaky funnel that's burning their recruiting budget. Position Employment Networks' frictionless application process as the fix — candidates import a smart resume in one step, AI handles the data extraction, and recruiters get rich structured profiles instead of incomplete forms. Tone: business case, CFO-friendly.
+
+---
+
+## Day 4 — ATS Keyword Filtering
+
+**Candidate:**
+Write a social media post (under 200 words) for job seekers about the frustrating reality that ATS keyword filters block 70% of resumes from ever being seen by a human. Explain that Applicant Network uses AI-powered semantic matching instead — it understands that "managed client relationships" matches "account management" even without the exact keywords. Your experience matters more than your vocabulary. Tone: informative, hopeful.
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) for recruiters about how traditional ATS keyword filters are costing them great candidates. Reference the 70% stat. Introduce Splits Network's three-layer matching engine: rule-based scoring for hard requirements, skills matching for competency analysis, and AI-powered semantic embeddings that understand context and meaning. Find candidates you'd miss with keyword search. Tone: technical credibility without jargon.
+
+**Company:**
+Write a LinkedIn post (under 200 words) for employers about the talent they're losing to outdated ATS technology. Frame keyword filtering as a diversity and quality problem — it favors candidates who know the "right words" over those with the right skills. Position Employment Networks' AI matching as a competitive advantage for finding overlooked talent. Tone: forward-thinking, innovation-focused.
+
+---
+
+## Day 5 — Lack of Feedback
+
+**Candidate:**
+Write a social media post (under 200 words) for job seekers about the frustration of applying to jobs and never hearing why they weren't selected. Reference that 52% of candidates cite lack of feedback as their top frustration. Introduce Applicant Network's AI Review Panel — get real-time visibility into how your profile matches against a role (strong fit, good fit, fair fit) plus direct messaging with your recruiter. You'll never wonder where you stand. Tone: empathetic, empowering.
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) for recruiters about why providing candidate feedback is both an ethical obligation and a business strategy. Explain that Splits Network automates the heavy lifting — AI generates candidate-job fit analysis, stage transitions trigger notifications, and structured feedback channels exist within the platform. Being a great communicator doesn't have to mean more work. Tone: values-driven but practical.
+
+**Company:**
+Write a LinkedIn post (under 200 words) for hiring managers about how feedback loops improve future candidate quality. When candidates know what's expected and where they fell short, they self-select better next time. Position Employment Networks' transparent pipeline with AI-powered feedback as a long-term talent pipeline investment, not just a courtesy. Tone: strategic, data-informed.
+
+---
+
+## Day 6 — Collaboration Over Competition
+
+**Candidate:**
+Write a social media post (under 150 words) for job seekers about what a split-fee recruiter marketplace means for them — instead of one recruiter working your job search, you get two specialized recruiters collaborating: one who knows you, one who knows the company. More coverage, better matches. Introduce Splits Network. Tone: explanatory, exciting.
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) for recruiters about the shift from competing for placements to collaborating on them. Frame split-fee recruiting as a force multiplier — source where you're strong, partner where you're not, and close more deals. Splits Network handles the matching, contracts, billing, and coordination. You just recruit. Tone: entrepreneurial, opportunity-focused.
+
+**Company:**
+Write a LinkedIn post (under 200 words) for employers about how collaborative recruiting networks deliver better candidates faster than single-recruiter models. Two specialized recruiters working a role — one focused on sourcing, one on the client relationship — compress time-to-hire and expand the talent pool. Introduce Employment Networks as the platform that makes this possible. Tone: results-oriented, modern.
+
+---
+
+## Day 7 — Time-to-Hire
+
+**Candidate:**
+Write a social media post (under 150 words) for job seekers about why the best opportunities disappear within 10 days — and why a platform that moves fast matters. Position Applicant Network's AI matching, instant chat with recruiters, and integrated video calls as the speed advantage that gets you in the door before the role closes. Tone: urgent but not pressuring.
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) for recruiters about the 44-day average time-to-hire killing their placements. The best candidates leave the market in 7-10 days. Position Splits Network's speed stack: AI surfaces matches before candidates even apply, same-day video interviews via LiveKit, real-time chat, automated stage progression, and split-fee collaboration that doubles your capacity per role. Tone: direct, metric-driven.
+
+**Company:**
+Write a LinkedIn post (under 200 words) for employers about the financial cost of slow hiring — vacant positions cost approximately $500/day in lost productivity. Position Employment Networks' compressed hiring timeline as a direct P&L impact: AI matching, integrated video interviews, automated workflows, and collaborative recruiting networks that fill roles in days, not months. Tone: CFO-speaks, ROI-focused.
+
+---
+
+## Day 8 — Recruiter Burnout
+
+**Candidate:**
+Write a social media post (under 150 words) for job seekers about why a burned-out recruiter means a bad experience for them. When 27% of talent acquisition teams report burnout, candidates feel it — slow responses, dropped follow-ups, impersonal interactions. Position Applicant Network as a platform where automation handles the admin so recruiters can focus on the human side of recruiting. Tone: connecting the dots, empathetic.
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) for recruiters about burnout in talent acquisition — 27% of TA teams are reporting it. Frame the root cause as administrative overhead, not the actual recruiting work. Position Splits Network as the antidote: automation rules handle repetitive tasks, calendar sync eliminates scheduling ping-pong, split-fee collaboration means you share the workload, and the platform handles contracts, billing, and coordination. Recruit more, administrate less. Tone: "we see you," supportive but solution-oriented.
+
+**Company:**
+Write a LinkedIn post (under 200 words) for employers about how recruiter burnout leads to bad hires — burned-out teams cut corners, rush decisions, and miss red flags. Bad hires cost up to 3x annual salary. Position Employment Networks as a platform that keeps your recruiting partners sharp by eliminating the administrative burden that causes burnout in the first place. Tone: risk-management framing.
+
+---
+
+## Day 9 — Data Fragmentation
+
+**Candidate:**
+Write a social media post (under 150 words) for job seekers about the frustration of having their information scattered across 15 different job platforms, each with different profiles and formats. Position Applicant Network as one unified profile — upload once, match everywhere, track everything in one dashboard. Tone: relatable, simplifying.
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) for recruiters about how fragmented data across disconnected systems (ATS, CRM, email, spreadsheets, calendars) is silently killing productivity. Position Splits Network as a unified ecosystem — ATS, matching, billing, communications, analytics, calendar sync, and video calls in one platform, with 200+ API endpoints behind a single gateway. One login, one source of truth. Tone: operational efficiency, "stop duct-taping tools together."
+
+**Company:**
+Write a LinkedIn post (under 200 words) for employers about how disconnected recruiting tools create blind spots — you can't optimize what you can't measure across systems. Position Employment Networks' integrated analytics and unified data model as the visibility layer hiring leaders need to make informed decisions. Tone: governance and visibility.
+
+---
+
+## Day 10 — AI Matching
+
+**Candidate:**
+Write a social media post (under 200 words) for job seekers about how AI matching is changing job search. Instead of scrolling through hundreds of irrelevant listings, Applicant Network's matching engine analyzes your skills, experience, and career goals and proactively surfaces roles that fit — rated as strong, good, or fair matches. The AI understands context, not just keywords. Tone: future-forward, exciting.
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) for recruiters about the three-layer matching engine in Splits Network. Explain the approach: rule-based scoring checks hard requirements, skills scoring analyzes competencies, and AI-powered semantic embeddings understand context and meaning. It inverts the funnel from "screen 500 applications" to "review 10 strong matches." Position this as the shift from reactive to proactive recruiting. Tone: technical credibility, differentiation.
+
+**Company:**
+Write a LinkedIn post (under 200 words) for employers about how AI-human hybrid recruiting outperforms either approach alone. AI handles the scale (matching thousands of candidates to requirements in seconds) while human recruiters handle judgment (culture fit, motivation, potential). Position Employment Networks as the platform that gets this balance right. Tone: thought leadership, innovation.
+
+---
+
+## Day 11 — Trust in Partnerships
+
+**Candidate:**
+Write a social media post (under 150 words) for job seekers about how to tell if a recruiter is legit. Introduce Applicant Network's recruiter reputation system — badges earned for real placements, XP scores, leaderboard rankings, and visible track records. You can browse the firm directory and see who's actually closing deals, not just who has the slickest pitch. Tone: protective, street-smart.
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) for recruiters about the trust problem in split-fee partnerships — sharing candidate data with someone you've never worked with is risky. Position Splits Network's trust infrastructure: escrow-backed payments (funds held until placement confirms), visible reputation scores via gamification, platform-mediated data sharing (no emailing spreadsheets), and auditable trails for every interaction. Trust isn't just a feeling — it's a system. Tone: serious, building confidence.
+
+**Company:**
+Write a LinkedIn post (under 200 words) for employers about how to evaluate recruiting partners in a platform marketplace. Frame the challenge of choosing between agencies. Position Employment Networks' transparent reputation system — recruiter performance data, placement history, responsiveness metrics, and gamification credentials — as the due diligence layer that traditional agency relationships lack. Tone: procurement-minded, analytical.
+
+---
+
+## Day 12 — Smart Resume
+
+**Candidate:**
+Write a social media post (under 200 words) for job seekers introducing Applicant Network's Smart Resume feature. Upload your existing resume, and AI extracts your experience, skills, education, certifications, and projects into a rich structured profile. Then the platform tailors your resume for each application automatically. Stop rewriting your resume for every job. Tone: practical, "why didn't this exist before?"
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) for recruiters about how AI-parsed candidate profiles change the screening game. Instead of skimming PDFs, Splits Network gives you structured, searchable candidate data extracted by AI — skills, experience, education, projects all normalized and ready for matching. Find the needle in the haystack without reading 500 haystacks. Tone: efficiency-focused, time-saving.
+
+**Company:**
+Write a LinkedIn post (under 150 words) for employers about the quality of candidate data they're receiving. Most resumes are unstructured text documents that get keyword-scanned. Employment Networks' AI-powered profile extraction means hiring teams get rich, structured candidate data that enables better comparison and faster decisions. Tone: quality-of-hire focused.
+
+---
+
+## Day 13 — Video Interviews
+
+**Candidate:**
+Write a social media post (under 150 words) for job seekers about how Applicant Network eliminates the back-and-forth of scheduling interviews. Book video calls directly through the platform, join from your browser — no downloads, no confusion. Calendar sync keeps everything in one place. Tone: convenient, modern.
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) for recruiters about how integrated video calling compresses time-to-hire. Splits Network has built-in video interviews via LiveKit — schedule, call, record, and capture notes without switching tools. Pair this with calendar sync (Google + Microsoft) and you can go from AI match to video screen in the same day. Tone: speed-advantage, competitive.
+
+**Company:**
+Write a LinkedIn post (under 150 words) for employers about how the best candidates are interviewing with 3-5 companies simultaneously. The companies that move fastest win. Position Employment Networks' integrated video calling and automated scheduling as the speed layer that puts your offer first. Tone: competitive urgency.
+
+---
+
+## Day 14 — Gamification & Engagement
+
+**Candidate:**
+Write a social media post (under 200 words) for job seekers about Applicant Network's gamification system. Earn badges for completing your profile, engaging with opportunities, and building your professional brand. Climb the leaderboard with XP from meaningful actions. Build streaks with consistent activity. It's not a game — it's a track record that recruiters can see. Tone: fun, motivating, "level up your job search."
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) for recruiters about how Splits Network's gamification system creates a visible track record. Badges earned from real placements, XP from consistent activity, leaderboard rankings that new partners can verify before agreeing to a split. Gamification isn't about fun — it's about building trust at scale through provable performance. Tone: credibility-building, reputation-focused.
+
+**Company:**
+Write a LinkedIn post (under 150 words) for employers about how to identify high-performing recruiters in a marketplace. Employment Networks' gamification system (badges, XP, leaderboards, streak data) gives you a data-driven view of recruiter quality before you ever engage them. It's like a credit score for recruiting performance. Tone: evaluative, risk-reducing.
+
+---
+
+## Day 15 — The Split-Fee Model Explained
+
+**Candidate:**
+Write a social media post (under 200 words) for job seekers explaining what split-fee recruiting means and why it benefits them. Two recruiters collaborate — one specializes in finding talent like you, one has the relationship with the hiring company. You get double the advocacy, broader reach, and specialized attention. The financial arrangement between the recruiters is handled by the platform — transparent and guaranteed. Tone: educational, "here's how this helps you."
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) for recruiters making the business case for split-fee deals. Half a placement fee is infinitely more than no fee on a role you couldn't fill alone. Splits Network handles the discovery (firm directory), the trust (escrow-backed payments), the contracts (platform-mediated), and the admin (automated billing and payouts via Stripe). You just recruit. Tone: entrepreneurial, math-driven.
+
+**Company:**
+Write a LinkedIn post (under 200 words) for employers about the advantages of collaborative recruiting for hard-to-fill roles. Two specialized recruiters working your role means broader talent access and faster fills. The split-fee model aligns recruiter incentives with your outcomes — they only get paid when you get a hire. Employment Networks makes this collaboration structured and transparent. Tone: procurement advantage, results-focused.
+
+---
+
+## Day 16 — Fraud Prevention
+
+**Candidate:**
+Write a social media post (under 150 words) for job seekers about how Applicant Network protects the integrity of the job marketplace. AI-powered fraud detection flags suspicious postings and fake applications, so the opportunities you see are real and the competition is honest. Your time is valuable — we don't waste it. Tone: protective, trustworthy.
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) for recruiters about the growing problem of bot-driven application spam flooding job postings with low-quality applications. Position Splits Network's fraud detection system — duplicate application detection, rapid submission analysis, and behavioral scoring that flags bots who can't accumulate real badges, XP, or streak data. Clean pipeline, real candidates. Tone: problem-solving, no-nonsense.
+
+**Company:**
+Write a LinkedIn post (under 150 words) for employers about the cost of processing fraudulent applications — wasted recruiter time screening bots and fake candidates. Position Employment Networks' automated fraud detection as a quality filter that ensures your recruiting partners spend time on real candidates, not spam. Tone: efficiency, waste-reduction.
+
+---
+
+## Day 17 — Real-Time Communication
+
+**Candidate:**
+Write a social media post (under 150 words) for job seekers about the power of instant communication with recruiters. No more waiting days for email replies. Applicant Network has built-in messaging — chat directly with your recruiter, get real-time updates on your applications, and ask questions when they matter, not three days later. Tone: modern, conversational.
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) for recruiters about how response time correlates with placement rates. When a candidate is hot, you need to move now — not after three rounds of email tag. Splits Network's real-time chat, in-app notifications, and integrated email keep you connected to candidates and partners without switching between tools. Tone: competitive edge, speed.
+
+**Company:**
+Write a LinkedIn post (under 150 words) for employers about how communication speed in recruiting directly impacts quality of hire. The best candidates make decisions fast. Employment Networks' real-time communication infrastructure — chat, video, email, notifications — ensures the candidates you want don't slip away while someone's drafting an email. Tone: urgency, talent competition.
+
+---
+
+## Day 18 — Hiring Manager Alignment
+
+**Candidate:**
+Write a social media post (under 150 words) for job seekers about a hidden reason hiring takes so long — recruiters and hiring managers aren't on the same page. This misalignment adds 24% to timelines while you wait. Applicant Network connects you to a platform where everyone shares the same pipeline view, the same data, and the same definitions. Less confusion, faster offers. Tone: insider knowledge, empowering.
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) for recruiters about the 24% timeline increase caused by hiring manager misalignment. Frame the problem: different definitions of "qualified," unclear feedback, shifting requirements. Position Splits Network's shared pipeline — both parties see the same stages, AI reviews, notes, and candidate data in real time. Structured stages enforce common language. No more "what did you think of that candidate?" emails. Tone: operational, fixing a known problem.
+
+**Company:**
+Write a LinkedIn post (under 200 words) for hiring managers about their role in slow hiring. When recruiter and hiring manager priorities aren't aligned, timelines expand 24% and top candidates walk. Employment Networks provides shared visibility — structured pipelines, real-time notifications on application events, and transparent candidate data so everyone's working from the same playbook. Tone: self-reflective, action-oriented.
+
+---
+
+## Day 19 — Platform Economics
+
+**Candidate:**
+Write a social media post (under 150 words) for job seekers about why Applicant Network is free for candidates. The platform is funded by recruiter subscriptions and placement fees — not by selling your data or charging you to apply. You get AI matching, smart resumes, video interviews, direct messaging, and a gamified career profile at zero cost. Tone: transparent, no-catch.
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) for recruiters about the ROI of a platform-mediated split-fee model versus traditional networking. Calculate: the time spent finding partners, negotiating terms, managing paperwork, chasing invoices — versus one platform that handles discovery, contracts, escrow, billing, and payouts. Time saved is placements earned. Tone: business case, numbers-driven.
+
+**Company:**
+Write a LinkedIn post (under 200 words) for employers about how platform economics change the recruiting cost equation. Traditional agency models charge 20-30% with no collaboration. Split-fee marketplaces create competition and collaboration simultaneously — broader talent access, faster fills, and recruiter accountability through transparent performance data. Tone: procurement innovation.
+
+---
+
+## Day 20 — Diversity & Inclusion
+
+**Candidate:**
+Write a social media post (under 200 words) for job seekers about how AI matching levels the playing field. Traditional ATS systems favor candidates who know the "right keywords" — often correlating with specific backgrounds and education. Applicant Network's semantic matching engine evaluates skills and experience in context, not by vocabulary. Your capabilities matter more than your phrasing. Tone: inclusive, empowering, no virtue-signaling.
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) for recruiters about the stat that 44% of talent teams say diversity hiring is their greatest challenge. Frame keyword-based ATS as part of the problem — it favors candidates who match a narrow template. Position Splits Network's AI matching (semantic embeddings, skills-based scoring) as a tool that expands candidate pools by understanding meaning and potential, not just exact terminology. Tone: practical inclusion, not performative.
+
+**Company:**
+Write a LinkedIn post (under 200 words) for employers about how outdated recruiting technology undermines diversity goals. When ATS keyword filters screen out 70% of resumes, they disproportionately affect non-traditional candidates. Employment Networks' AI matching evaluates skills and context, not keyword density — helping you find diverse talent your current tools are filtering out. Tone: strategic, impact-focused.
+
+---
+
+## Day 21 — Automation That Helps
+
+**Candidate:**
+Write a social media post (under 150 words) for job seekers about how good automation makes the job search more human, not less. Applicant Network automates the tedious parts — resume parsing, status notifications, match alerts — so your conversations with recruiters are about you, not paperwork. Tone: warm, reassuring.
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) for recruiters about the right kind of recruiting automation. Not bots replacing recruiters — automation that eliminates the 60% of your day spent on admin. Splits Network's rule engine automates stage transitions, follow-up reminders, expiration warnings, and notification dispatch. The platform administrates, you recruit. Tone: "automation done right," practical.
+
+**Company:**
+Write a LinkedIn post (under 150 words) for employers about the difference between automation that replaces judgment and automation that enhances it. Employment Networks automates the administrative overhead of recruiting — scheduling, notifications, billing, stage tracking — while keeping human judgment at every decision point. AI matches, humans hire. Tone: balanced, thoughtful.
+
+---
+
+## Day 22 — The Platform Advantage
+
+**Candidate:**
+Write a social media post (under 200 words) for job seekers about what makes a recruiting platform different from a job board. Job boards are bulletin boards — you post and pray. Applicant Network is an active marketplace where AI matches you to roles, recruiters compete to represent you, you track every application in real time, and you build a reputation that follows you. Tone: "this isn't your parents' job board," energetic.
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) for recruiters about why platform-based recruiting is replacing the old model. Compare: cold-calling candidates vs. AI-surfaced matches, handshake deals vs. escrow-backed splits, scattered spreadsheets vs. a unified pipeline, networking events vs. a searchable firm directory. Splits Network isn't a tool — it's the new way split-fee recruiting works. Tone: industry-shifting, bold.
+
+**Company:**
+Write a LinkedIn post (under 200 words) for employers about the strategic shift from vendor relationships to platform ecosystems in recruiting. Instead of managing 5 agency contracts, tap into a marketplace of specialized, reputation-verified recruiters who collaborate on your roles. Employment Networks provides the transparency, accountability, and speed that traditional agency models can't. Tone: strategic evolution.
+
+---
+
+## Day 23 — Calendar & Scheduling
+
+**Candidate:**
+Write a social media post (under 150 words) for job seekers about the scheduling nightmare of interviewing with multiple companies. Applicant Network syncs with your Google or Microsoft calendar, lets you book video calls directly through the platform, and sends you ICS files so nothing falls through the cracks. One place to manage your entire interview schedule. Tone: organized, stress-reducing.
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) for recruiters about how much time they waste on scheduling logistics. Back-and-forth emails, timezone confusion, calendar conflicts. Splits Network integrates Google Calendar and Microsoft Outlook directly — see availability, book video interviews, and auto-sync events. Combined with built-in video calling, you can schedule and conduct interviews without leaving the platform. Tone: time-saving, practical.
+
+**Company:**
+Write a LinkedIn post (under 150 words) for employers about how scheduling friction extends time-to-hire by days or weeks per candidate. Employment Networks' calendar integration and built-in video calling mean your recruiting partners can move from match to screen to interview without the email relay race. Faster process, better candidate experience. Tone: process improvement.
+
+---
+
+## Day 24 — Escrow & Payment Security
+
+**Candidate:**
+Write a social media post (under 150 words) for job seekers about why platform-guaranteed payments matter to them. When recruiters get paid reliably through Splits Network's escrow system, they're more invested in your success. No payment disputes, no dropped placements, no recruiter suddenly going silent because the fee fell through. Financial certainty creates better service. Tone: connecting the dots, trust.
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) for recruiters about the number one risk in split-fee deals — not getting paid. Position Splits Network's escrow-backed payment system: funds are held via Stripe the moment a placement is confirmed, released when the guarantee period completes, and automated invoicing handles the rest. No chasing payments. No broken agreements. No trust required — just structure. Tone: financial security, peace of mind.
+
+**Company:**
+Write a LinkedIn post (under 150 words) for employers about how structured payment systems through Employment Networks eliminate fee disputes between recruiting partners. When payment is guaranteed and automated, recruiters focus on finding great candidates instead of worrying about getting paid. Better recruiter focus means better candidates for you. Tone: alignment of incentives.
+
+---
+
+## Day 25 — Analytics & Visibility
+
+**Candidate:**
+Write a social media post (under 150 words) for job seekers about having a real dashboard for your job search. Applicant Network gives you activity heatmaps, application status charts, pipeline visualization, job search momentum tracking, and profile completion metrics. Treat your job search like a professional campaign, not a shot in the dark. Tone: empowering, professional.
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) for recruiters about data-driven recruiting. Splits Network's analytics service provides recruiter response metrics, marketplace health monitoring, placement trends, and hourly/daily/monthly rollups. Know exactly how your pipeline is performing, where bottlenecks exist, and which partnerships are most productive. Decisions backed by data, not gut feelings. Tone: analytical, performance-oriented.
+
+**Company:**
+Write a LinkedIn post (under 200 words) for employers about the visibility gap in recruiting. Most hiring managers can't answer basic questions: What's our average time-to-fill? Where do candidates drop off? Which recruiters perform best? Employment Networks' cross-cutting analytics provide these answers in real time — so you can optimize your hiring process like you optimize every other business function. Tone: business intelligence.
+
+---
+
+## Day 26 — ChatGPT & AI Agents
+
+**Candidate:**
+Write a social media post (under 200 words) for job seekers about the future of job searching — doing it through AI. Applicant Network has a ChatGPT plugin and MCP server that lets AI assistants search jobs, analyze your resume against roles, and even submit applications on your behalf. Ask ChatGPT to find you roles that match your skills and it can search the Applicant Network marketplace directly. Tone: cutting-edge, exciting, "the future is here."
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) for recruiters about how AI agent integrations expand their reach. Splits Network's MCP server and ChatGPT plugin mean candidates can discover and apply to your jobs through AI assistants — a new distribution channel that reaches candidates where they already are. Position this as first-mover advantage in AI-native recruiting. Tone: innovative, forward-looking.
+
+**Company:**
+Write a LinkedIn post (under 200 words) for employers about the next wave of talent acquisition technology — AI agents that act on candidates' behalf. Employment Networks is already there with ChatGPT and MCP integrations. Companies that position their roles on AI-accessible platforms will reach the most tech-savvy, in-demand candidates first. Tone: innovation leadership, competitive positioning.
+
+---
+
+## Day 27 — Placement Guarantees
+
+**Candidate:**
+Write a social media post (under 150 words) for job seekers about how placement guarantees protect everyone. When you're placed through Splits Network, there's a guarantee period — if things don't work out, the system is designed to support a good outcome for all parties. It's not just about filling seats — it's about successful placements. Tone: quality-focused, reassuring.
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) for recruiters about how placement guarantees with escrow holds change the risk calculus of split-fee deals. Splits Network holds funds during the guarantee period, releases payment when the placement succeeds, and handles the entire lifecycle. This isn't just billing — it's a quality signal. Clients trust recruiters who back their placements with guarantees. Tone: professional, quality-differentiation.
+
+**Company:**
+Write a LinkedIn post (under 200 words) for employers about demanding more from their recruiting partners. Placement guarantees backed by escrow through Employment Networks mean recruiters have skin in the game for quality, not just speed. If a hire doesn't work out within the guarantee period, there's a structured resolution process. Align incentives with outcomes. Tone: demanding quality, smart procurement.
+
+---
+
+## Day 28 — Industry Transformation
+
+**Candidate:**
+Write a social media post (under 200 words) for job seekers about the fundamental shift happening in recruiting. The old model — spray and pray applications, opaque processes, ghosting as standard — is being replaced by transparent, AI-powered, platform-driven marketplaces. Applicant Network is built for this new world. Position yourself in the system that's replacing the broken one. Tone: big picture, movement, "be early."
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) for recruiters about the structural failure the industry is facing: 80% of seekers feel unprepared, applications per role have doubled, yet 2/3 of recruiters can't find quality talent. This isn't a cycle — it's a broken system. Splits Network is the new infrastructure: AI matching replaces keyword filtering, collaboration replaces competition, transparency replaces trust gaps, platform replaces paperwork. Tone: manifesto, industry leadership.
+
+**Company:**
+Write a LinkedIn post (under 200 words) for employers about the "slowness tax" — 73% of leaders estimate up to 5% annual revenue loss from execution delays caused by unfilled positions. The structural problems in recruiting (fragmented tools, slow processes, misaligned incentives) have a direct P&L impact. Employment Networks is the platform designed to close that gap. Tone: executive briefing, urgency.
+
+---
+
+## Day 29 — Success Stories Framework
+
+**Candidate:**
+Write a social media post (under 200 words) for job seekers painting a picture of what a great job search experience looks like. Walk through the Applicant Network journey: upload resume → AI builds your profile → get matched to curated roles → chat with specialized recruiters → video interview from your couch → track everything on your dashboard → get placed. No ghosting, no keyword games, no black holes. Tone: aspirational, concrete.
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) for recruiters painting a picture of a day on Splits Network. Morning: review 5 AI-matched candidates for an open role. 10 AM: quick video screen with the top match. Noon: propose the candidate to your split-fee partner who has the client relationship. 2 PM: partner presents to the hiring manager. 4 PM: interview scheduled for tomorrow. The platform handled matching, scheduling, communication, and billing. You just recruited. Tone: "a day in the life," tangible.
+
+**Company:**
+Write a LinkedIn post (under 200 words) for hiring managers painting a picture of recruiting through Employment Networks. Post a role → two specialized recruiters collaborate to fill it → AI screens for fit → candidates are pre-qualified → interviews happen within days → hire within weeks, not months → transparent billing, no surprises. Compare this to the current reality of weeks of silence from agencies. Tone: contrast, "this is what's possible."
+
+---
+
+## Day 30 — The Invitation
+
+**Candidate:**
+Write a social media post (under 200 words) for job seekers as a direct invitation to join Applicant Network. Summarize the value: AI-powered matching that understands your skills (not just keywords), real-time application tracking so you're never ghosted, Smart Resume that works for you, direct messaging with recruiters, video interviews, gamified career profile, and it's completely free. Include a call to action to sign up at applicant.network. Tone: warm, direct, compelling.
+
+**Recruiter:**
+Write a LinkedIn post (under 200 words) for recruiters as a direct invitation to join Splits Network. Summarize the value: AI-powered candidate matching, a searchable marketplace of trusted partners, escrow-backed split-fee payments, built-in video calling and chat, automated billing and workflows, gamified reputation building, and analytics to prove your performance. The platform that makes split-fee recruiting actually work. Include a call to action to sign up at splits.network. Tone: direct, opportunity-focused, bold.
+
+**Company:**
+Write a LinkedIn post (under 200 words) for employers as a direct invitation to explore Employment Networks. Summarize the value: access a marketplace of reputation-verified, specialized recruiters who collaborate on your roles. AI-powered matching, transparent pipelines, escrow-backed billing, video interviews, real-time analytics, and a platform built to compress time-to-hire and improve quality of hire. Include a call to action to learn more at employment-networks.com. Tone: professional, results-focused, inviting.
+
+---
+
+## Usage Notes
+
+- **Paste any prompt directly into Claude** to generate a ready-to-post caption
+- **Customize per platform**: Ask Claude to adjust for Twitter/X (shorter), Instagram (add emoji/visual direction), or LinkedIn (as written)
+- **Add visuals**: After generating the post, ask Claude to suggest an image concept or create a graphic prompt for an image generator
+- **Localize stats**: The pain point statistics come from a 2026 global talent acquisition analysis — ask Claude to cite the source or soften to "studies show" as appropriate
+- **Batch generate**: Paste multiple prompts at once and ask Claude to generate all posts in a single response
diff --git a/services/ats-service/src/v3/admin/views/jobs.repository.ts b/services/ats-service/src/v3/admin/views/jobs.repository.ts
index 477ff6517..fc3e5d090 100644
--- a/services/ats-service/src/v3/admin/views/jobs.repository.ts
+++ b/services/ats-service/src/v3/admin/views/jobs.repository.ts
@@ -39,12 +39,37 @@ export class AdminJobsRepository {
}
async findByIdForAdmin(id: string): Promise {
- const { data, error } = await this.supabase
+ const { data: job, error } = await this.supabase
.from('jobs')
.select('*, company:companies(id, name, logo_url, industry)')
.eq('id', id)
.single();
+ if (error) throw error;
+
+ // Fetch application stage counts for pipeline view
+ const { data: apps } = await this.supabase
+ .from('applications')
+ .select('stage')
+ .eq('job_id', id);
+
+ const stageCounts: Record = {};
+ for (const app of apps ?? []) {
+ const stage = app.stage ?? 'unknown';
+ stageCounts[stage] = (stageCounts[stage] || 0) + 1;
+ }
+
+ return { ...job, stage_counts: stageCounts };
+ }
+
+ async updateJob(id: string, updates: Record): Promise {
+ const { data, error } = await this.supabase
+ .from('jobs')
+ .update({ ...updates, updated_at: new Date().toISOString() })
+ .eq('id', id)
+ .select()
+ .single();
+
if (error) throw error;
return data;
}
diff --git a/services/ats-service/src/v3/admin/views/jobs.route.ts b/services/ats-service/src/v3/admin/views/jobs.route.ts
index 29c4b4d06..1531d7568 100644
--- a/services/ats-service/src/v3/admin/views/jobs.route.ts
+++ b/services/ats-service/src/v3/admin/views/jobs.route.ts
@@ -36,6 +36,16 @@ export function registerAdminJobsView(app: FastifyInstance, supabase: SupabaseCl
return reply.send({ data });
});
+ // PATCH /v3/admin/jobs/:id — update job fields
+ app.patch('/v3/admin/jobs/:id', {
+ schema: { params: idParamSchema },
+ }, async (request, reply) => {
+ const { id } = request.params as { id: string };
+ const updates = request.body as Record;
+ const data = await repository.updateJob(id, updates);
+ return reply.send({ data });
+ });
+
// PATCH /v3/admin/jobs/:id/status
app.patch('/v3/admin/jobs/:id/status', {
schema: { params: idParamSchema },
diff --git a/services/ats-service/src/v3/admin/views/lists.repository.ts b/services/ats-service/src/v3/admin/views/lists.repository.ts
index 2945e2993..59d8ea90c 100644
--- a/services/ats-service/src/v3/admin/views/lists.repository.ts
+++ b/services/ats-service/src/v3/admin/views/lists.repository.ts
@@ -19,6 +19,37 @@ function buildPagination(total: number, page: number, limit: number) {
export class AdminListsRepository {
constructor(private supabase: SupabaseClient) {}
+ async getApplicationById(id: string): Promise {
+ const { data: app, error } = await this.supabase
+ .from('applications')
+ .select('*, job:jobs(id, title, status, company:companies(id, name)), candidate:candidates(id, first_name, last_name, email, phone, location, resume_status)')
+ .eq('id', id)
+ .single();
+
+ if (error) throw error;
+
+ // Fetch notes for this application (stage changes, admin notes, etc.)
+ const { data: notes } = await this.supabase
+ .from('application_notes')
+ .select('id, note_type, visibility, body, author_name, created_at')
+ .eq('application_id', id)
+ .order('created_at', { ascending: true });
+
+ return { ...app, notes: notes ?? [] };
+ }
+
+ async updateApplicationStage(id: string, stage: string): Promise {
+ const { data, error } = await this.supabase
+ .from('applications')
+ .update({ stage, updated_at: new Date().toISOString() })
+ .eq('id', id)
+ .select()
+ .single();
+
+ if (error) throw error;
+ return data;
+ }
+
async listApplications(params: AdminListParams) {
const { page, limit, offset } = paginate(params);
const sortBy = params.sort_by || 'created_at';
diff --git a/services/ats-service/src/v3/admin/views/lists.route.ts b/services/ats-service/src/v3/admin/views/lists.route.ts
index de67c1d7e..65a53ef99 100644
--- a/services/ats-service/src/v3/admin/views/lists.route.ts
+++ b/services/ats-service/src/v3/admin/views/lists.route.ts
@@ -10,6 +10,7 @@ import {
adminListQuerySchema,
adminApplicationsQuerySchema,
adminPlacementsQuerySchema,
+ idParamSchema,
AdminListParams,
} from '../types.js';
@@ -25,6 +26,26 @@ export function registerAdminListViews(app: FastifyInstance, supabase: SupabaseC
return reply.send(result);
});
+ // GET /v3/admin/applications/:id — detail with job + candidate + notes
+ app.get('/v3/admin/applications/:id', {
+ schema: { params: idParamSchema },
+ }, async (request, reply) => {
+ const { id } = request.params as { id: string };
+ const data = await repository.getApplicationById(id);
+ return reply.send({ data });
+ });
+
+ // PATCH /v3/admin/applications/:id/stage — admin stage override
+ app.patch('/v3/admin/applications/:id/stage', {
+ schema: { params: idParamSchema },
+ }, async (request, reply) => {
+ const { id } = request.params as { id: string };
+ const { stage } = request.body as { stage: string };
+ if (!stage) return reply.code(400).send({ error: { message: 'stage is required' } });
+ const data = await repository.updateApplicationStage(id, stage);
+ return reply.send({ data });
+ });
+
// GET /v3/admin/candidates — flat list with search
app.get('/v3/admin/candidates', {
schema: { querystring: adminListQuerySchema },
diff --git a/services/identity-service/src/v3/admin/repository.ts b/services/identity-service/src/v3/admin/repository.ts
index f6fbe5909..180af6e7f 100644
--- a/services/identity-service/src/v3/admin/repository.ts
+++ b/services/identity-service/src/v3/admin/repository.ts
@@ -33,16 +33,51 @@ export class AdminRepository {
}
async getUser(id: string): Promise {
- const { data, error } = await this.supabase
+ const { data: user, error } = await this.supabase
.from('users')
.select('*')
.eq('id', id)
.maybeSingle();
if (error) throw error;
+ if (!user) return null;
+
+ // Fetch related data in parallel
+ const [roles, recruiter, candidate] = await Promise.all([
+ this.supabase.from('user_roles')
+ .select('id, role_name, organization_id, company_id, role_entity_type, created_at')
+ .eq('user_id', id).is('deleted_at', null),
+ this.supabase.from('recruiters')
+ .select('id, status, tagline, location, years_experience, marketplace_enabled, stripe_connect_onboarded, created_at')
+ .eq('user_id', id).maybeSingle(),
+ this.supabase.from('candidates')
+ .select('id, first_name, last_name, email, phone, location, resume_status, created_at')
+ .eq('user_id', id).maybeSingle(),
+ ]);
+
+ return {
+ ...user,
+ roles: roles.data ?? [],
+ recruiter: recruiter.data ?? null,
+ candidate: candidate.data ?? null,
+ };
+ }
+
+ async addUserRole(userId: string, roleName: string): Promise {
+ const { data, error } = await this.supabase.from('user_roles')
+ .insert({ user_id: userId, role_name: roleName })
+ .select().single();
+ if (error) throw error;
return data;
}
+ async removeUserRole(roleId: string): Promise {
+ const { error } = await this.supabase.from('user_roles')
+ .update({ deleted_at: new Date().toISOString() })
+ .eq('id', roleId);
+ if (error) throw error;
+ }
+
async listOrganizations(params: AdminListParams): Promise<{ data: any[]; total: number }> {
const page = params.page || 1;
const limit = Math.min(params.limit || 25, 100);
diff --git a/services/identity-service/src/v3/admin/routes.ts b/services/identity-service/src/v3/admin/routes.ts
index b4de57ae2..13085ad39 100644
--- a/services/identity-service/src/v3/admin/routes.ts
+++ b/services/identity-service/src/v3/admin/routes.ts
@@ -14,6 +14,8 @@ import {
adminActivityQuerySchema,
adminPeriodQuerySchema,
idParamSchema,
+ addRoleSchema,
+ roleIdParamSchema,
} from './types.js';
export function registerAdminRoutes(
@@ -80,4 +82,23 @@ export function registerAdminRoutes(
const data = await service.getUser(id);
return reply.send({ data });
});
+
+ // POST /api/v3/admin/users/:id/roles — add role to user
+ app.post('/api/v3/admin/users/:id/roles', {
+ schema: { params: idParamSchema, body: addRoleSchema },
+ }, async (request, reply) => {
+ const { id } = request.params as { id: string };
+ const { role_name } = request.body as { role_name: string };
+ const data = await service.addUserRole(id, role_name);
+ return reply.send({ data });
+ });
+
+ // DELETE /api/v3/admin/users/:id/roles/:roleId — remove role from user
+ app.delete('/api/v3/admin/users/:id/roles/:roleId', {
+ schema: { params: roleIdParamSchema },
+ }, async (request, reply) => {
+ const { roleId } = request.params as { id: string; roleId: string };
+ await service.removeUserRole(roleId);
+ return reply.send({ data: { success: true } });
+ });
}
diff --git a/services/identity-service/src/v3/admin/service.ts b/services/identity-service/src/v3/admin/service.ts
index 27657b7b4..0924b941d 100644
--- a/services/identity-service/src/v3/admin/service.ts
+++ b/services/identity-service/src/v3/admin/service.ts
@@ -31,6 +31,14 @@ export class AdminService {
return user;
}
+ async addUserRole(userId: string, roleName: string) {
+ return this.repository.addUserRole(userId, roleName);
+ }
+
+ async removeUserRole(roleId: string) {
+ return this.repository.removeUserRole(roleId);
+ }
+
async listOrganizations(params: AdminListParams) {
const { data, total } = await this.repository.listOrganizations(params);
const page = params.page || 1;
diff --git a/services/identity-service/src/v3/admin/types.ts b/services/identity-service/src/v3/admin/types.ts
index 0fb74408f..5ae7a1df0 100644
--- a/services/identity-service/src/v3/admin/types.ts
+++ b/services/identity-service/src/v3/admin/types.ts
@@ -40,6 +40,24 @@ export const adminPeriodQuerySchema = {
},
};
+export const addRoleSchema = {
+ type: 'object',
+ required: ['role_name'],
+ properties: {
+ role_name: { type: 'string' },
+ },
+ additionalProperties: false,
+};
+
+export const roleIdParamSchema = {
+ type: 'object',
+ required: ['id', 'roleId'],
+ properties: {
+ id: { type: 'string', format: 'uuid' },
+ roleId: { type: 'string', format: 'uuid' },
+ },
+};
+
export const idParamSchema = {
type: 'object',
required: ['id'],
diff --git a/services/network-service/src/v3/admin/repository.ts b/services/network-service/src/v3/admin/repository.ts
index b3406f93d..5dd849214 100644
--- a/services/network-service/src/v3/admin/repository.ts
+++ b/services/network-service/src/v3/admin/repository.ts
@@ -18,6 +18,31 @@ function buildPagination(total: number, page: number, limit: number) {
export class AdminRepository {
constructor(private supabase: SupabaseClient) {}
+ async getRecruiterById(id: string): Promise {
+ const { data: recruiter, error } = await this.supabase
+ .from('recruiters')
+ .select('*, user:users!recruiters_user_id_fkey(id, name, email, avatar_url, created_at)')
+ .eq('id', id)
+ .single();
+ if (error) throw error;
+
+ // Fetch related data in parallel
+ const [reputation, firmMembership, companies, candidateCount] = await Promise.all([
+ this.supabase.from('recruiter_reputation').select('*').eq('recruiter_id', id).maybeSingle(),
+ this.supabase.from('firm_members').select('*, firm:firms!firm_members_firm_id_fkey(id, name, slug, status)').eq('recruiter_id', id).eq('status', 'active').maybeSingle(),
+ this.supabase.from('recruiter_companies').select('*, company:companies!recruiter_companies_company_id_fkey(id, name, logo_url)').eq('recruiter_id', id).order('created_at', { ascending: false }).limit(20),
+ this.supabase.from('recruiter_candidates').select('id', { count: 'exact', head: true }).eq('recruiter_id', id),
+ ]);
+
+ return {
+ ...recruiter,
+ reputation: reputation.data ?? null,
+ firm_membership: firmMembership.data ?? null,
+ companies: companies.data ?? [],
+ candidate_count: candidateCount.count ?? 0,
+ };
+ }
+
async listRecruiters(params: AdminListParams): Promise<{ data: any[]; pagination: any }> {
const { page, limit, offset } = paginate(params);
let query = this.supabase
@@ -32,6 +57,13 @@ export class AdminRepository {
return { data: data || [], pagination: buildPagination(count || 0, page, limit) };
}
+ async updateRecruiter(id: string, updates: Record): Promise {
+ const { data, error } = await this.supabase.from('recruiters')
+ .update({ ...updates, updated_at: new Date().toISOString() }).eq('id', id).select().single();
+ if (error) throw error;
+ return data;
+ }
+
async updateRecruiterStatus(id: string, status: string): Promise {
const { data, error } = await this.supabase.from('recruiters')
.update({ status, updated_at: new Date().toISOString() }).eq('id', id).select().single();
diff --git a/services/network-service/src/v3/admin/routes.ts b/services/network-service/src/v3/admin/routes.ts
index 4a28b402f..9346cc3bb 100644
--- a/services/network-service/src/v3/admin/routes.ts
+++ b/services/network-service/src/v3/admin/routes.ts
@@ -8,8 +8,8 @@ import { AdminRepository } from './repository.js';
import { AdminService } from './service.js';
import {
AdminListParams,
- adminListQuerySchema, recruiterStatusSchema, firmApprovalSchema,
- idParamSchema, statsQuerySchema,
+ adminListQuerySchema, recruiterStatusSchema, recruiterUpdateSchema,
+ firmApprovalSchema, idParamSchema, statsQuerySchema,
} from './types.js';
const AUTH_ERROR = { error: { code: 'AUTH_REQUIRED', message: 'Authentication required' } };
@@ -54,6 +54,29 @@ export function registerAdminRoutes(
return reply.send({ data: result.data, pagination: result.pagination });
});
+ // GET /api/v3/admin/recruiters/:id
+ app.get('/api/v3/admin/recruiters/:id', {
+ schema: { params: idParamSchema },
+ }, async (request, reply) => {
+ const clerkUserId = getClerkUserId(request);
+ if (!clerkUserId) return reply.status(401).send(AUTH_ERROR);
+ const { id } = request.params as { id: string };
+ const data = await service.getRecruiterById(id, clerkUserId);
+ return reply.send({ data });
+ });
+
+ // PATCH /api/v3/admin/recruiters/:id
+ app.patch('/api/v3/admin/recruiters/:id', {
+ schema: { params: idParamSchema, body: recruiterUpdateSchema },
+ }, async (request, reply) => {
+ const clerkUserId = getClerkUserId(request);
+ if (!clerkUserId) return reply.status(401).send(AUTH_ERROR);
+ const { id } = request.params as { id: string };
+ const updates = request.body as Record;
+ const data = await service.updateRecruiter(id, updates, clerkUserId);
+ return reply.send({ data });
+ });
+
// PATCH /api/v3/admin/recruiters/:id/status
app.patch('/api/v3/admin/recruiters/:id/status', {
schema: { params: idParamSchema, body: recruiterStatusSchema },
diff --git a/services/network-service/src/v3/admin/service.ts b/services/network-service/src/v3/admin/service.ts
index 58577c070..42cb38a5e 100644
--- a/services/network-service/src/v3/admin/service.ts
+++ b/services/network-service/src/v3/admin/service.ts
@@ -18,11 +18,21 @@ export class AdminService {
this.accessResolver = new AccessContextResolver(supabase);
}
+ async getRecruiterById(id: string, clerkUserId: string) {
+ await this.requireAdmin(clerkUserId);
+ return this.repository.getRecruiterById(id);
+ }
+
async listRecruiters(params: AdminListParams, clerkUserId: string) {
await this.requireAdmin(clerkUserId);
return this.repository.listRecruiters(params);
}
+ async updateRecruiter(id: string, updates: Record, clerkUserId: string) {
+ await this.requireAdmin(clerkUserId);
+ return this.repository.updateRecruiter(id, updates);
+ }
+
async updateRecruiterStatus(id: string, status: string, clerkUserId: string) {
await this.requireAdmin(clerkUserId);
return this.repository.updateRecruiterStatus(id, status);
diff --git a/services/network-service/src/v3/admin/types.ts b/services/network-service/src/v3/admin/types.ts
index 263f1f1ac..cb9ab5bb6 100644
--- a/services/network-service/src/v3/admin/types.ts
+++ b/services/network-service/src/v3/admin/types.ts
@@ -46,6 +46,24 @@ export const idParamSchema = {
properties: { id: { type: 'string', format: 'uuid' } },
};
+export const recruiterUpdateSchema = {
+ type: 'object',
+ properties: {
+ bio: { type: 'string' },
+ tagline: { type: 'string' },
+ location: { type: 'string' },
+ phone: { type: 'string' },
+ years_experience: { type: 'integer' },
+ industries: { type: 'array', items: { type: 'string' } },
+ specialties: { type: 'array', items: { type: 'string' } },
+ candidate_recruiter: { type: 'boolean' },
+ company_recruiter: { type: 'boolean' },
+ marketplace_enabled: { type: 'boolean' },
+ marketplace_visibility: { type: 'string', enum: ['public', 'limited', 'hidden'] },
+ },
+ additionalProperties: false,
+};
+
export const statsQuerySchema = {
type: 'object',
properties: {