System diagram
┌─────────────────────────────────────┐
│ Load Balancer │
│ (Railway/Vercel) │
└──────────────┬──────────────────────┘
│
┌────────────────────────────────┼────────────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ iOS App │ │ Admin Frontend │ │ Client Hub │
│ (Swift) │ │ (Next.js) │ │ (Public pages) │
│ │ │ │ │ │
│ • Creator dash │ │ • Mission Ctrl │ │ • Brand reports │
│ • Brand hubs │ │ • Pipeline │ │ • Creator roster│
│ • Chat │ │ • Roster │ │ • Requests │
│ • Strategy │ │ • Analytics │ │ │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
│ HTTPS/WebSocket │ │
└──────────────────────────────┼──────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Rails Backend API │
│ dialedapi.evomarketing.co │
│ │
│ • REST API endpoints │
│ • ActionCable WebSockets │
│ • SolidQueue background jobs │
│ • Push notification dispatch │
└──────────────┬──────────────────────┘
│
┌────────────────────────────────┼────────────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ MySQL │ │ Redis │ │ External APIs │
│ │ │ │ │ │
│ • All app data │ │ • Action Cable │ │ • TikTok OAuth │
│ • Better Auth │ │ • SolidQueue │ │ • Instagram │
│ • Chat history │ │ • Rate limits │ │ • YouTube │
│ • Metrics │ │ • Caching │ │ • ScrapeCreators│
└─────────────────┘ └─────────────────┘ │ • Slack │
│ • Zendesk │
│ • APNS │
└─────────────────┘
Application stack
Backend (Rails)
| Component | Technology | Purpose |
|---|---|---|
| Framework | Ruby on Rails 8 | API and business logic |
| Database | MySQL 8 | Primary data store |
| Cache/Queue | Redis | ActionCable, SolidQueue, caching |
| Auth | Better Auth | JWT-based authentication |
| Jobs | SolidQueue | Background job processing |
| Push | APNS | iOS push notifications |
| Hosting | Railway | Container hosting |
Frontend (Next.js)
| Component | Technology | Purpose |
|---|---|---|
| Framework | Next.js 14 | React-based admin UI |
| Styling | Tailwind CSS | Utility-first CSS |
| State | React Query | Server state management |
| UI | shadcn/ui | Component library |
| Hosting | Vercel | Edge deployment |
iOS (Swift)
| Component | Technology | Purpose |
|---|---|---|
| Language | Swift 5 | Native iOS development |
| UI | SwiftUI | Declarative UI framework |
| Networking | URLSession | API communication |
| Push | APNs | Push notification handling |
| Project | XcodeGen | Project file generation |
Domain model
The core domain model centers on brands and creators:┌─────────────────────────────────────────────────────────────────────────┐
│ Brand │
│ (Client company running creator campaigns) │
│ • pipeline_status (8 stages from onboarding → off_boarded) │
│ • assigned_operator_id (EVO team member) │
└─────────────────────────────────────────────────────────────────────────┘
│
│ has_many
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ Campaign │
│ (Time-bound content initiative) │
│ • status: pending/active/paused/finished │
│ • start_date, end_date │
│ • placement targets │
└─────────────────────────────────────────────────────────────────────────┘
│
│ has_many (through brand_creator_memberships)
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ Creator (User) │
│ (Content creator enrolled in brand deals) │
│ • user_typ: "creator" │
│ • tier: Galaxy/Moonwalker/Rocket/Beginner │
│ • roster_stage: active/graduated/removed │
└─────────────────────────────────────────────────────────────────────────┘
│
│ has_many
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ CreatorSocial │
│ (Connected social media account) │
│ • platform: tt/ig/yt/fb │
│ • handle, typ: oauth/scrape │
│ • sync_status, approval_status │
└─────────────────────────────────────────────────────────────────────────┘
Key relationships
| Relationship | Description |
|---|---|
| Brand → Campaigns | A brand has many campaigns over time |
| Brand → BrandCreatorMemberships → Creators | Creators are assigned to brands, not campaigns |
| Campaign → CampaignSubmissions (Placements) | Content posted for a campaign |
| Creator → CreatorSocials | Connected TikTok, Instagram, YouTube accounts |
| Brand → BrandDeal | Public job board listing for applications |
| Brand → Contracts → Creators | Formal agreements with placement targets |
API structure
The backend exposes three API namespaces:Internal API (/api/internal/*)
For the admin frontend (EVO team):
- Full CRUD on all resources
- Mission Control, Pipeline, Roster
- Analytics and reporting
- Requires admin JWT
Creator API (/api/creator/*)
For the iOS app (creators):
- Dashboard, brand hubs, chat
- Content submission
- Strategy viewing
- Requires creator JWT
Public API (/api/public/*)
For unauthenticated access:
- Client Hub (with optional passcode)
- Public brand reports
- Contract signing
- Invite acceptance
Authentication flow
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Client │────▶│ Better Auth │────▶│ Backend │
│ (iOS/Web) │ │ (Auth) │ │ (Rails) │
└─────────────┘ └─────────────┘ └─────────────┘
│ │ │
│ 1. Login request │ │
│───────────────────▶│ │
│ │ 2. Verify creds │
│ │──────────────────▶│
│ │ 3. User data │
│ │◀──────────────────│
│ 4. JWT token │ │
│◀───────────────────│ │
│ │ │
│ 5. API request with JWT │
│───────────────────────────────────────▶│
│ 6. Response │
│◀───────────────────────────────────────│
- Magic link email login
- Password authentication
- Session management
- JWT token issuance
- Validates JWT tokens
- Looks up user by
auth_user_id - Enforces authorization
Real-time features
ActionCable provides WebSocket connections for:| Channel | Purpose |
|---|---|
NotificationsChannel | Bell notification updates |
ChatChannel | Real-time chat messages |
ChatUserChannel | User presence, typing indicators |
VoiceChannel | LiveKit voice room coordination |
Background jobs
SolidQueue processes background work:| Job Category | Examples |
|---|---|
| Scraping | SyncJob, CreatorSocialSyncJob |
| Notifications | NotificationDispatchJob, WelcomeDmJob |
| Metrics | DailyMetricsJob, PlacementRolloverJob |
| Automation | PipelineAutoAdvanceJob, InactivityNudgeJob |
| Reports | WeeklyReportJob, SlackDailyReportJob |
Deployment architecture
┌─────────────────────────────────────────────────────────────────────┐
│ Railway │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ evo-dialed- │ │ evo-dialed- │ │ metrics- │ │
│ │ backend │ │ ai-worker │ │ collector │ │
│ │ (Web + Queue) │ │ (AI processing) │ │ (Scraping) │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ MySQL │ │ Redis │ │
│ │ (Database) │ │ (Cache/Queue) │ │
│ └─────────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ Vercel │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ evo-dialed-frontend (Next.js) │ │
│ │ dialed.evomarketing.co │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
Security considerations
- All traffic over HTTPS
- JWT tokens with expiration
- Rate limiting on sensitive endpoints
- Client Hub optional passcode protection
- Better Auth handles credential storage
- Environment variables for secrets