Brooke / roles.sales — Campaign & Sequence System Design
Brooke / roles.sales — Campaign & Sequence System Design Date: 2026 04 29 Status: Approved for planning Overview A campaign and sequence system for the B2BEA / RedKey sales motion. Contacts are enrolled in campaigns by the roles.sales agent (Brooke) after Justin gates the initial routing. Each campaign has its own sequence of timed touchpoints (email + Linke...
Date: 2026-04-29 Status: Approved for planning
---
A campaign and sequence system for the B2BEA / RedKey sales motion. Contacts are enrolled in campaigns by the roles.sales agent (Brooke) after Justin gates the initial routing. Each campaign has its own sequence of timed touchpoints (email + LinkedIn). When a contact exits a campaign, the campaign's on_exit map determines the next campaign automatically — Justin only gates the first handoff.
---
``
Tara (roles.bdr) finds prospect → books meeting
→ meeting_booked task dispatched to roles.exec (Justin)
→ Justin reviews handoff, specifies campaign(s): "vendor_membership_close"
→ task dispatched to roles.sales
→ roles.sales enrolls contact in specified campaign(s), step 1
→ Engine fires execute_sequence_step tasks as next_send_at arrives
→ roles.sales drafts each touch → staged_drafts → Justin approves → sends
→ reply received → roles.sales classifies → continue or exit
→ exit: on_exit map routes to next campaign automatically
``
---
| Slug | Type | Client | Purpose |
|---|---|---|---|
| b2bea_world | event | b2bea | Outreach for B2B eCommerce World event |
| b2bea_membership | membership | b2bea | Vendor membership outreach |
| dreamborn | platform | dreamborn | DreamBorn platform outreach |
| Slug | Type | Client | Purpose |
|---|---|---|---|
| b2bea_membership_close | membership | b2bea | Post-meeting close sequence |
| Slug | Type | Client | Purpose |
|---|---|---|---|
| b2bea_membership_cs | onboarding | b2bea | Post-close member onboarding |
``
b2bea_membership (BDR) → meeting → Justin gates → b2bea_membership_close
b2bea_membership_close → converted → b2bea_membership_cs
b2bea_membership_close → no_engagement → null (close the loop)
b2bea_membership_close → rejected → null (close the loop)
b2bea_world (BDR) → registered → null (no sales sequence for event)
dreamborn (BDR) → meeting → Justin gates → no sales campaign yet
``
---
``sql
id uuid primary key
slug text unique not null -- e.g. 'b2bea_membership_close'
name text not null
client_id text not null
type text not null -- event | membership | platform | onboarding
owner_role text not null -- roles.bdr | roles.sales | roles.cs
status text not null default 'active' -- active | paused | archived
on_exit jsonb not null default '{}'
-- shape: { "converted": "<slug|null>", "no_engagement": "<slug|null>", "rejected": "<slug|null>" }
created_at timestamptz not null default now()
``
Each row is one step in a campaign's sequence.
``sql
id uuid primary key
campaign_id uuid not null references campaigns(id)
step_number int not null -- 1-based, per campaign
delay_days int not null -- days from enrollment (step 1 = day 0)
channel text not null -- email | linkedin
content_type text not null
-- follow_up | proof | boss_arming | campaign_visibility
-- personal_visibility | marketing_relief | close | exit
template_instructions text not null -- what to write — not the copy itself
unique (campaign_id, step_number)
``
One row per contact per campaign enrollment. Each enrollment starts fresh at step 1.
``sql
id uuid primary key
contact_id text not null -- Attio contact record ID
campaign_id uuid not null references campaigns(id)
client_id text not null
status text not null default 'active' -- active | converted | rejected | timed_out
current_step int not null default 1
next_send_at timestamptz -- when to fire the next step
enrolled_at timestamptz not null default now()
exited_at timestamptz
exit_reason text
goal text -- awareness | pipeline | credibility | marketing_relief
notes text -- from Justin's exec gate routing decision
-- partial unique: only one ACTIVE enrollment per contact per campaign
-- exited enrollments are preserved as history and do not block re-enrollment
-- create unique index campaign_contacts_active_uniq on campaign_contacts (contact_id, campaign_id) where status = 'active'
``
Individual outbound items queued for execution.
``sql
id uuid primary key
client_id text not null
contact_id text not null
campaign_id uuid not null references campaigns(id)
step_number int not null
channel text not null -- email | linkedin
staged_draft_id uuid -- FK to staged_drafts once drafted
scheduled_at timestamptz not null
sent_at timestamptz
status text not null default 'pending' -- pending | drafted | approved | sent | failed
error text
created_at timestamptz not null default now()
``
---
| Task type | Trigger | What roles.sales does |
|---|---|---|
| enroll_contact | Justin's exec gate approval | Creates campaign_contacts row(s), sets step 1, calculates first next_send_at |
| execute_sequence_step | Engine when next_send_at arrives | Drafts the touch for current step, writes to staged_drafts, advances current_step, sets next next_send_at |
| process_reply | Inbound reply received (deferred — reply detection mechanism TBD) | Classifies intent, updates Attio activity, continues sequence or triggers exit |
| exit_contact | Exit condition met | Sets campaign_contacts.status, follows on_exit map to enroll in next campaign |
---