# Campaign (/entities/marketing/campaign)



Schema [#schema]

```typescript
import { Noun } from 'digital-objects'

export const Campaign = Noun('Campaign', {
  name: 'string!',
  slug: 'string##',
  description: 'string',
  type: 'Email | Social | Content | Event | Paid | Webinar | Referral',
  status: 'Draft | Scheduled | Active | Launched | Paused | Completed | Cancelled',
  startDate: 'date',
  endDate: 'date',
  launchedAt: 'datetime',
  budget: 'number',
  actualCost: 'number',
  currency: 'string',
  targetLeads: 'number',
  targetRevenue: 'number',
  leads: '<- Lead.campaign[]',
  actualLeads: 'number',
  actualRevenue: 'number',
  roi: 'number',
  landingPageUrl: 'string',
  utmSource: 'string',
  utmMedium: 'string',
  utmCampaign: 'string',
  organization: '-> Organization',
  owner: '-> Contact',
  launch: 'Launched',
  pause: 'Paused',
  complete: 'Completed',
})
```

Fields [#fields]

| Field            | Type                     | Required | Description                                                                                        |
| ---------------- | ------------------------ | -------- | -------------------------------------------------------------------------------------------------- |
| `name`           | string                   | Yes      | Campaign name                                                                                      |
| `slug`           | string (unique, indexed) | No       | URL-safe identifier for the campaign                                                               |
| `description`    | string                   | No       | Campaign description and goals                                                                     |
| `type`           | enum                     | No       | Channel: `Email`, `Social`, `Content`, `Event`, `Paid`, `Webinar`, or `Referral`                   |
| `status`         | enum                     | No       | Lifecycle stage: `Draft`, `Scheduled`, `Active`, `Launched`, `Paused`, `Completed`, or `Cancelled` |
| `startDate`      | date                     | No       | Planned start date                                                                                 |
| `endDate`        | date                     | No       | Planned end date                                                                                   |
| `launchedAt`     | datetime                 | No       | Actual launch timestamp                                                                            |
| `budget`         | number                   | No       | Allocated budget                                                                                   |
| `actualCost`     | number                   | No       | Actual spend to date                                                                               |
| `currency`       | string                   | No       | Currency code (e.g. `USD`, `EUR`)                                                                  |
| `targetLeads`    | number                   | No       | Target number of leads to generate                                                                 |
| `targetRevenue`  | number                   | No       | Target revenue from this campaign                                                                  |
| `leads`          | \<- Lead\[]              | No       | Leads generated by this campaign                                                                   |
| `actualLeads`    | number                   | No       | Actual leads generated                                                                             |
| `actualRevenue`  | number                   | No       | Actual revenue attributed                                                                          |
| `roi`            | number                   | No       | Return on investment (computed)                                                                    |
| `landingPageUrl` | string                   | No       | Campaign landing page URL                                                                          |
| `utmSource`      | string                   | No       | UTM source parameter                                                                               |
| `utmMedium`      | string                   | No       | UTM medium parameter                                                                               |
| `utmCampaign`    | string                   | No       | UTM campaign parameter                                                                             |
| `organization`   | -> Organization          | No       | Tenant this campaign belongs to                                                                    |
| `owner`          | -> Contact               | No       | Campaign owner or manager                                                                          |

Relationships [#relationships]

| Field          | Direction | Target           | Description                          |
| -------------- | --------- | ---------------- | ------------------------------------ |
| `leads`        | \<-       | Lead.campaign\[] | Leads attributed to this campaign    |
| `organization` | ->        | Organization     | Tenant this campaign belongs to      |
| `owner`        | ->        | Contact          | Person responsible for this campaign |

Verbs [#verbs]

| Verb       | Event       | Description                                                       |
| ---------- | ----------- | ----------------------------------------------------------------- |
| `create`   | `Created`   | Create a new campaign in Draft status                             |
| `update`   | `Updated`   | Update campaign fields                                            |
| `delete`   | `Deleted`   | Remove a campaign                                                 |
| `launch`   | `Launched`  | Start the campaign -- sets status to Launched, records launchedAt |
| `pause`    | `Paused`    | Temporarily halt the campaign                                     |
| `complete` | `Completed` | Mark the campaign as finished                                     |

Verb Lifecycle [#verb-lifecycle]

```typescript
import { Campaign } from '@headlessly/marketing'

// BEFORE hook -- validate before launch
Campaign.launching(campaign => {
  if (!campaign.landingPageUrl) {
    throw new Error('Landing page URL required before launch')
  }
  if (!campaign.budget || campaign.budget <= 0) {
    throw new Error('Budget must be set before launch')
  }
})

// Execute
await Campaign.launch('campaign_e5JhLzXc')

// AFTER hook -- react to launch
Campaign.launched((campaign, $) => {
  $.Event.create({
    name: 'campaign_launched',
    type: 'track',
    source: 'API',
    properties: JSON.stringify({
      campaignId: campaign.$id,
      type: campaign.type,
      budget: campaign.budget,
    }),
    timestamp: new Date().toISOString(),
  })

  // Create a funnel to track this campaign
  $.Funnel.create({
    name: `${campaign.name} Conversion`,
    steps: JSON.stringify([
      { name: 'Landing Page', event: 'page_view' },
      { name: 'Form Submit', event: 'form_submitted' },
      { name: 'Lead Created', event: 'lead_created' },
    ]),
  })
})
```

Status State Machine [#status-state-machine]

```
         create()
(none) ──────────> Draft
                     │
          ┌──────────┤
          │          │
          v          v
      Scheduled   Launched ←── launch()
                     │
          ┌──────────┼──────────┐
          │          │          │
          v          │          v
       Paused        │      Completed
          │          │
          v          v
       Launched   Cancelled
```

| From                  | Verb       | To          |
| --------------------- | ---------- | ----------- |
| --                    | `create`   | `Draft`     |
| `Draft`               | `update`   | `Scheduled` |
| `Draft` / `Scheduled` | `launch`   | `Launched`  |
| `Launched`            | `pause`    | `Paused`    |
| `Paused`              | `launch`   | `Launched`  |
| `Launched` / `Paused` | `complete` | `Completed` |
| `Draft` / `Scheduled` | `update`   | `Cancelled` |

Cross-Domain Patterns [#cross-domain-patterns]

Campaign is the bridge between marketing spend and CRM pipeline:

```typescript
import { Campaign } from '@headlessly/marketing'

// When a campaign completes, compute ROI
Campaign.completed((campaign, $) => {
  const leads = await $.Lead.find({ campaign: campaign.$id })
  const deals = []
  for (const lead of leads) {
    const leadDeals = await $.Deal.find({ lead: lead.$id, stage: 'Won' })
    deals.push(...leadDeals)
  }

  const revenue = deals.reduce((sum, d) => sum + (d.value || 0), 0)
  const roi = campaign.actualCost > 0
    ? ((revenue - campaign.actualCost) / campaign.actualCost) * 100
    : 0

  await $.Campaign.update(campaign.$id, {
    actualLeads: leads.length,
    actualRevenue: revenue,
    roi,
  })

  $.Metric.create({
    name: `campaign_roi_${campaign.slug}`,
    value: roi,
    type: 'Gauge',
    unit: 'percent',
  })
})
```

* **CRM**: Leads reference their originating campaign via `Lead.campaign`. Campaign owner is a Contact.
* **Analytics**: Campaign launch, pause, and complete events are tracked. ROI computed as a Metric. Funnels model campaign conversion.
* **Billing**: `actualRevenue` ties to subscription and payment data from won deals.
* **Content**: Landing pages and blog posts link to campaigns. UTM parameters track attribution.
* **Forms**: Forms capture leads that are attributed to campaigns.

Query Examples [#query-examples]

SDK [#sdk]

```typescript
import { Campaign } from '@headlessly/marketing'

// Find active campaigns
const active = await Campaign.find({
  status: { $in: ['Launched', 'Active'] },
})

// Get a specific campaign with its leads
const campaign = await Campaign.get('campaign_e5JhLzXc', {
  include: ['leads', 'owner'],
})

// Create a new email campaign
await Campaign.create({
  name: 'Product Launch Q1',
  type: 'Email',
  status: 'Draft',
  budget: 10000,
  currency: 'USD',
  targetLeads: 500,
  utmSource: 'email',
  utmMedium: 'campaign',
  utmCampaign: 'product-launch-q1',
})

// Launch it
await Campaign.launch('campaign_e5JhLzXc')

// Pause temporarily
await Campaign.pause('campaign_e5JhLzXc')

// Complete the campaign
await Campaign.complete('campaign_e5JhLzXc')
```

MCP [#mcp]

```json title="headless.ly/mcp#search"
{
  "type": "Campaign",
  "filter": { "status": "Launched", "type": "Email" },
  "sort": { "launchedAt": "desc" },
  "limit": 10
}
```

```json title="headless.ly/mcp#fetch"
{ "type": "Campaign", "id": "campaign_e5JhLzXc", "include": ["leads"] }
```

```ts title="headless.ly/mcp#do"
const campaigns = await $.Campaign.find({ status: 'Launched' })
await $.Campaign.launch('campaign_e5JhLzXc')
await $.Campaign.complete('campaign_e5JhLzXc')
```

REST [#rest]

```bash
