Marketing Campaign Authoring

Summary

Campaign authoring is the editable side of marketing: campaigns, flows, templates, triggers, suppression choices, variants, and actions. It defines what could run. Runtime state defines what actually ran. Keeping those apart lets operators improve future campaigns without rewriting past execution.

Reader Question

How does the platform let an operator author a campaign while preserving versioned inputs and avoiding accidental mutation of active or historical runs?

Surface Or Workflow

The workflow begins in campaign creation or edit screens. Operators choose campaign basics, audience bindings, suppression policy, templates, flow steps, variants, triggers, and actions. Template Studio and Flow Builder are authoring tools, not runtime workers.

Lifecycle

Authoring starts with draft configuration. Services validate campaign shape, template identity scope, flow graph, trigger bindings, suppression policy, and action definitions. When saved, definitions become durable authoring state. Future runs snapshot the relevant pieces into run-owned runtime rows.

Active-run edits require explicit preview/apply paths where they exist. Normal authoring changes should affect future runs only.

Child Threads

  • marketing-authoring-state-model: campaign, template, flow, trigger, and action definition state.
  • marketing-trigger-system: reusable trigger definitions and campaign bindings.
  • marketing-action-plan-dispatch: authored actions, run snapshots, and transition dispatch.
  • marketing-rendering-and-assets: templates and prompt inputs that become rendered assets.

Implementation Boundaries

Authoring UI lives under app/web/app/marketing/campaigns, app/web/app/marketing/flows, app/web/app/marketing/templates, app/web/app/marketing/triggers, and app/web/app/marketing/suppressions. API routes under app/api/marketing adapt those workflows. Authoring services and typed contracts live under services/marketing/authoring and libs/marketing/authoring.

Tradeoffs

Version-aware authoring is heavier than editing a JSON campaign blob, but it is what makes runtime explainable. A rendered asset or action execution can point back to the versioned input it used.

Visual

The current visual is a graph neighborhood. The intended visual is an authoring state diagram from draft campaign inputs to validated definitions to future run snapshots.

Authoring As Editable Intent

Campaign authoring is where marketing intent remains deliberately editable. An operator is still deciding what the campaign means: which audience it will use, which flow steps exist, which template variants are eligible, which triggers should react to events, which actions may be dispatched, and which suppression policy applies. That state is not runtime truth yet. It is a set of versioned definitions that future runs may snapshot.

This distinction is the core architectural point. A campaign draft can change because an operator improves copy, adds a variant, adjusts a trigger, or fixes an audience binding. A historical run cannot change in the same way. Once a run has rendered an asset, sent a message, or dispatched an action, the system needs to explain what version of the authoring inputs produced that outcome. Without that boundary, attribution and repair become unreliable because there is no stable link between what was intended and what actually happened.

The authoring UI should therefore optimize for clarity and preview rather than hidden execution. Campaign creation, Flow Builder, Template Studio, trigger configuration, and suppression selection should make relationships visible. They should help an operator answer whether the campaign is complete enough to run, not perform the run directly. API routes adapt those authoring decisions into service calls. Services validate and persist definitions. Jobs later execute against run-owned snapshots.

What Gets Authored

Several different objects participate in campaign authoring. Campaign metadata defines the broad purpose, lifecycle, and bindings. Audience references connect the campaign to a population source but do not themselves materialize members. Templates define renderable content with versioned inputs and channel-specific constraints. Flow steps define sequencing. Triggers define what event or state transition should advance work. Actions define side effects that may be planned or dispatched during runtime.

These objects are related, but they should not collapse into one blob. A template can be tested independently. A trigger can be reused or reasoned about separately from a campaign. Suppression policy can be previewed before a run. An action definition can be validated for dependency and idempotency requirements. Keeping those pieces separate gives the console enough structure to guide the author and gives runtime enough structure to snapshot what matters.

The tests and authoring contracts protect exactly this shape. They should catch invalid definitions, unsafe generated SQL, missing template context, incorrect trigger bindings, and campaign states that would become ambiguous at runtime. The goal is not to make authoring slow. The goal is to let operators move quickly while preventing invalid campaign shape from becoming production runtime work.

Snapshot Boundary

The snapshot boundary is where authoring stops being a mutable plan and starts becoming executable state. A run should not continuously reinterpret current campaign definitions. It should carry enough copied or versioned context to explain the work it performs. That applies to audience membership, variant assignment, templates, flow steps, triggers, and action plans.

This boundary enables repair. If a rendered asset fails validation, the system can show the template and inputs that produced it. If a trigger dispatches an unexpected action, the run can point to the trigger and action definition that were active for that run. If an operator changes future campaign copy, the historical run remains explainable. Runtime state becomes a durable record, not a moving interpretation of the latest authoring screen.

Active-run editing is therefore a special case. If the system supports it, it needs explicit preview and apply semantics. It should not be accidental. A normal authoring save should affect future runs, while runtime repairs should flow through review and run-owned state.

Reader Heuristics

A useful way to inspect campaign authoring code is to ask whether a function is changing intent or executing work. Intent belongs under authoring services and UI authoring surfaces. Execution belongs under runtime jobs and runtime services. A page component that starts dispatching provider actions is crossing the boundary. A job that rewrites campaign definitions to satisfy a run is also crossing the boundary. The platform should instead move intent through validated definitions into snapshots and then into runtime work.

This also explains why authoring is linked to audience building but not owned by it. Campaign authoring can bind an audience; audience services decide how that audience previews and materializes. The authoring surface should show the operator enough information to make a choice, but the membership lifecycle belongs to the audience-building thread and runtime materialization.

Authoring Failure Modes

Authoring failures should be caught before runtime when possible. A campaign may be missing a required audience binding, a template may require context the run cannot provide, a trigger may reference an event that is not emitted, or an action may lack the dependency information needed for idempotent dispatch. Those are authoring problems. They should appear as validation messages, preview failures, or blocked campaign states rather than as halfway-completed runtime work.

Some failures are subtler. A campaign can be syntactically valid but hard to interpret because a flow is too complex, a template variant has unclear purpose, or a suppression choice does not match the intended audience. Those cases are why the console should show structure and review hints, not just raw forms. Authoring is part product design and part state design: it must guide operators toward definitions that future runs can explain.

The authoring thread should therefore be read together with review and quality posts. The best runtime repair is often preventing ambiguous intent from entering the system in the first place.

Working From The Console Backward

The strongest way to understand campaign authoring is to start with the operator screen and work backward. The console should make it obvious what the campaign is for, who it targets, what will be sent, what conditions cause it to run, which suppressions apply, and what evidence will be available after a run. Every backend primitive exists to make those operator questions precise.

That framing prevents the authoring model from becoming a pile of internal objects. A trigger is not interesting because it is a record; it is interesting because it tells the operator when intent becomes eligible for execution. A template is not just content; it is a structured promise about the fields, variants, approvals, and render context a run will need. An action is not merely a side effect; it is a dispatchable step that should be idempotent, diagnosable, and attributable.

The deeper implementation links should therefore support the console story. If a reader follows a link into templates, triggers, actions, or runtime snapshots, they should learn how that primitive protects authoring intent. They should not have to reconstruct the product from table names.

Source Evidence

  • app/web/app/marketing/campaigns
  • app/web/app/marketing/flows
  • app/web/app/marketing/templates
  • app/web/app/marketing/triggers
  • app/web/app/marketing/suppressions
  • app/api/marketing/campaigns
  • app/api/marketing/templates
  • app/api/marketing/triggers
  • app/api/marketing/suppressions
  • services/marketing/authoring
  • libs/marketing/authoring
  • tests/marketing