Marketing
Campaign Marketing initiatives -- email drips, social pushes, content launches, paid ads, webinars, and referral programs.
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' ,
})
Field Type Required Description namestring Yes Campaign name slugstring (unique, indexed) No URL-safe identifier for the campaign descriptionstring No Campaign description and goals typeenum No Channel: Email, Social, Content, Event, Paid, Webinar, or Referral statusenum No Lifecycle stage: Draft, Scheduled, Active, Launched, Paused, Completed, or Cancelled startDatedate No Planned start date endDatedate No Planned end date launchedAtdatetime No Actual launch timestamp budgetnumber No Allocated budget actualCostnumber No Actual spend to date currencystring No Currency code (e.g. USD, EUR) targetLeadsnumber No Target number of leads to generate targetRevenuenumber No Target revenue from this campaign leads<- Lead[] No Leads generated by this campaign actualLeadsnumber No Actual leads generated actualRevenuenumber No Actual revenue attributed roinumber No Return on investment (computed) landingPageUrlstring No Campaign landing page URL utmSourcestring No UTM source parameter utmMediumstring No UTM medium parameter utmCampaignstring No UTM campaign parameter organization-> Organization No Tenant this campaign belongs to owner-> Contact No Campaign owner or manager
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
Verb Event Description createCreatedCreate a new campaign in Draft status updateUpdatedUpdate campaign fields deleteDeletedRemove a campaign launchLaunchedStart the campaign -- sets status to Launched, records launchedAt pausePausedTemporarily halt the campaign completeCompletedMark the campaign as finished
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' },
]),
})
})
create()
(none) ──────────> Draft
│
┌──────────┤
│ │
v v
Scheduled Launched ←── launch()
│
┌──────────┼──────────┐
│ │ │
v │ v
Paused │ Completed
│ │
v v
Launched Cancelled
From Verb To -- createDraftDraftupdateScheduledDraft / ScheduledlaunchLaunchedLaunchedpausePausedPausedlaunchLaunchedLaunched / PausedcompleteCompletedDraft / ScheduledupdateCancelled
Campaign is the bridge between marketing spend and CRM pipeline:
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.
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' )
{
"type" : "Campaign" ,
"filter" : { "status" : "Launched" , "type" : "Email" },
"sort" : { "launchedAt" : "desc" },
"limit" : 10
}
{ "type" : "Campaign" , "id" : "campaign_e5JhLzXc" , "include" : [ "leads" ] }
const campaigns = await $.Campaign. find ({ status: 'Launched' })
await $.Campaign. launch ( 'campaign_e5JhLzXc' )
await $.Campaign. complete ( 'campaign_e5JhLzXc' )
# List launched campaigns
curl https://marketing.headless.ly/~acme/campaigns?status=Launched
# Get a specific campaign
curl https://marketing.headless.ly/~acme/campaigns/campaign_e5JhLzXc
# Create a campaign
curl -X POST https://marketing.headless.ly/~acme/campaigns \
-H 'Content-Type: application/json' \
-d '{"name": "Product Launch Q1", "type": "Email", "status": "Draft", "budget": 10000}'
# Launch a campaign
curl -X POST https://marketing.headless.ly/~acme/campaigns/campaign_e5JhLzXc/launch
# Pause a campaign
curl -X POST https://marketing.headless.ly/~acme/campaigns/campaign_e5JhLzXc/pause
# Complete a campaign
curl -X POST https://marketing.headless.ly/~acme/campaigns/campaign_e5JhLzXc/complete