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

# Domain models

> Core data models and their relationships in EVO Dialed

The EVO Dialed domain model centers on the commercial relationship between brands and creators.

## Model hierarchy

```
Brand (client company)
  └── CampaignContract (formal agreement)
        └── Campaign (content initiative)
              └── CampaignSubmission (placement/content)

User (creator)
  └── BrandCreatorMembership (roster spot)
        └── CreatorSocial (connected account)
              └── CreatorSocialBrand (brand link)
```

## Brand

The client company running creator campaigns.

**File:** `app/models/brand.rb`

```ruby theme={null}
class Brand < ApplicationRecord
  has_many :campaign_contracts
  has_many :campaigns
  has_many :brand_creator_memberships
  has_many :creators, through: :brand_creator_memberships, source: :user
end
```

### Key attributes

| Attribute              | Type   | Description             |
| ---------------------- | ------ | ----------------------- |
| `name`                 | string | Brand display name      |
| `pipeline_status`      | enum   | Current lifecycle stage |
| `assigned_operator_id` | string | EVO team member         |
| `client_hub_slug`      | string | Public hub URL slug     |
| `client_hub_passcode`  | string | Optional 4-digit PIN    |

### Pipeline stages

Brands progress through 8 stages:

| Stage               | Description              |
| ------------------- | ------------------------ |
| `onboarding`        | New brand setup          |
| `active`            | Campaign running         |
| `ending_soon`       | 14 days before end       |
| `up_for_renewal`    | Renewal decision pending |
| `potential_renewal` | Discussions active       |
| `renewed_relaunch`  | Signed for new term      |
| `paused`            | Temporarily stopped      |
| `off_boarded`       | No longer active         |

<Note>
  Pipeline status is written through a single service: `Brands::PipelineStatusChange`. Never update `pipeline_status` directly.
</Note>

## CampaignContract

The commercial agreement between EVO and a brand.

**File:** `app/models/campaign_contract.rb`

```ruby theme={null}
class CampaignContract < ApplicationRecord
  belongs_to :brand
  has_many :campaigns
end
```

### Key attributes

| Attribute                 | Type    | Description                           |
| ------------------------- | ------- | ------------------------------------- |
| `status`                  | enum    | draft/pending/active/paused/completed |
| `term_days`               | integer | Contract length                       |
| `placement_target`        | integer | Required placements                   |
| `platforms`               | array   | ig/tt/fb/yt                           |
| `contracted_roster_slots` | integer | Creator slots                         |

## Campaign

A time-bound content initiative under a contract.

**File:** `app/models/campaign.rb`

```ruby theme={null}
class Campaign < ApplicationRecord
  belongs_to :brand
  belongs_to :campaign_contract, optional: true
  has_many :campaign_submissions
end
```

### Key attributes

| Attribute    | Type   | Description                    |
| ------------ | ------ | ------------------------------ |
| `name`       | string | Campaign name                  |
| `status`     | enum   | pending/active/paused/finished |
| `start_date` | date   | Campaign start                 |
| `end_date`   | date   | Campaign end                   |
| `platforms`  | array  | Target platforms               |

### Status lifecycle

```
pending → active → finished
            ↓
          paused
```

## User (Creator)

Content creators who produce sponsored content.

**File:** `app/models/user.rb`

```ruby theme={null}
class User < ApplicationRecord
  has_many :brand_creator_memberships
  has_many :brands, through: :brand_creator_memberships
  has_many :creator_socials
end
```

### Key attributes

| Attribute      | Type       | Description                                         |
| -------------- | ---------- | --------------------------------------------------- |
| `auth_user_id` | string(64) | Better Auth ID                                      |
| `user_typ`     | enum       | evo/creator                                         |
| `tier`         | enum       | galaxy/moonwalker/rocket/beginner                   |
| `roster_stage` | enum       | onboarding/active/potential\_removal/needs\_removal |
| `badge`        | enum       | verified/preferred/none                             |

<Warning>
  `user_id` in other tables refers to `auth_user_id`, NOT `users.id`. Always join via `users.auth_user_id`.
</Warning>

## BrandCreatorMembership

The assignment of a creator to a brand (called "Roster Spot" in the glossary).

**File:** `app/models/brand_creator_membership.rb`

```ruby theme={null}
class BrandCreatorMembership < ApplicationRecord
  belongs_to :brand
  belongs_to :user, foreign_key: :user_id, primary_key: :auth_user_id
end
```

### Key attributes

| Attribute            | Type     | Description                    |
| -------------------- | -------- | ------------------------------ |
| `brand_id`           | integer  | Brand assignment               |
| `user_id`            | string   | Creator (Better Auth ID)       |
| `status`             | enum     | active/removed/invited         |
| `placements_cadence` | integer  | Posts per day                  |
| `source`             | enum     | manual/application/invite/code |
| `onboarded_at`       | datetime | When fully onboarded           |

### Unique constraint

```ruby theme={null}
# Unique key: [brand_id, user_id]
# A creator can have one membership per brand
```

## CampaignSubmission

A single piece of content (placement).

**File:** `app/models/campaign_submission.rb`

```ruby theme={null}
class CampaignSubmission < ApplicationRecord
  belongs_to :campaign
  belongs_to :creator_social, optional: true
end
```

### Key attributes

| Attribute                | Type     | Description                            |
| ------------------------ | -------- | -------------------------------------- |
| `campaign_id`            | integer  | Parent campaign                        |
| `user_id`                | string   | Creator                                |
| `npc_id`                 | string   | Normalized post content ID (for dedup) |
| `views`                  | bigint   | Current view count                     |
| `likes`                  | integer  | Like count                             |
| `comments`               | integer  | Comment count                          |
| `post_ts`                | datetime | When posted                            |
| `early_viral_alert_sent` | boolean  | One-shot viral notification            |

## CreatorSocial

A connected social media account.

**File:** `app/models/creator_social.rb`

```ruby theme={null}
class CreatorSocial < ApplicationRecord
  belongs_to :user, foreign_key: :user_id, primary_key: :auth_user_id
  has_many :creator_social_brands
end
```

### Key attributes

| Attribute         | Type   | Description                                |
| ----------------- | ------ | ------------------------------------------ |
| `platform`        | enum   | ig/tt/fb/yt                                |
| `handle`          | string | Social handle                              |
| `typ`             | enum   | oauth/scrape                               |
| `sync_status`     | enum   | not\_synced/syncing/connected/sync\_failed |
| `approval_status` | enum   | pending/approved/rejected                  |

### Brand linking

<Warning>
  A social must have `CreatorSocialBrand` links for post attribution. Without these, synced posts have nowhere to be attributed.
</Warning>

```ruby theme={null}
# Both records required for ingestion:
CreatorSocial.create!(user_id: user.auth_user_id, platform: "tt", handle: "example")
CreatorSocialBrand.create!(creator_social: social, brand: brand, user_id: user.auth_user_id)
```

## Chat models

Located in `app/models/chat/`:

| Model              | Purpose                    |
| ------------------ | -------------------------- |
| `Chat::Channel`    | Chat room/channel          |
| `Chat::Message`    | Individual message         |
| `Chat::Membership` | User membership in channel |
| `Chat::Reaction`   | Message reactions          |
| `Chat::Poll`       | Channel polls              |

## Notification models

Located in `app/models/mc/`:

| Model              | Purpose                  |
| ------------------ | ------------------------ |
| `Mc::Notification` | Bell notifications       |
| `Mc::DeviceToken`  | Push notification tokens |
