Marketing · Email campaign builder (wizard, A/B test, scheduling)
Four-step wizard — general info, template design, (optional) B-variant design, summary — that composes an email campaign: subject, audience segment, HTML body with personalisation variables, send window, frequency, optional A/B split, and a test send before publishing.
| Area | Marketing |
| Routes | marketing.emailing.new · marketing.emailing.edit |
| Centre modules | marketingMailing |
| Staff levels | root · admin · manager · crmmanager · weboperator · content_creator |
| Models | email-bag · email-campaign · email-campaign-template · segment · test-ab |
| UI label | email-campaign.new / email-campaign.edit |
| Coverage | 5 use cases · 2 happy · 15 failure |
Known issues (2)
- 🟠 Medium — Missing unsubscribe link only warns
- 🟠 Medium — 19 translation keys used by the code do not exist
Missing translations
These keys are used on this screen but do not exist in
translations.json, so users see the raw key:
email-campaign.insert-variable.placeholder
How it works
Step validation is declarative: stepsInfo gives each step a validateAttrs list and the design steps instead validate the HTML path (designHtmlPaths[stepId]), so isValidModel() only checks what the current step owns. Personalisation uses codeParams (centre name, user code, user email, user name) injectable into the subject and body. HTML bodies are scanned for invalid parameters and for the presence of an unsubscribe link, producing non-blocking warnings (email-campaign.warning-invalid-html-params, email-campaign.warning-unsub-link) plus a hard publish-time error (email-campaign.error-invalid-html-params, which names the offending template). Template import reads an uploaded HTML file (readImportedHtml) for A and B separately. userReachedInfo estimates reach and changes its wording when email validation is on (segment.users_reached.info_with_email_validation). Send quota is surfaced as consumedEmailbagWarningMessage from the centre’s email bag. sendTestEmail on the route validates a comma-separated address list itself and throws if it is not an array of e-mails. Frequency supports single sending plus recurring 15min/30min/hourly/daily/weekly.
Use cases
Why someone opens this (5)
- Sending a monthly newsletter to a segment with personalised greeting.
- A/B testing two subject lines on a percentage split before the main send.
- Scheduling a recurring automated email (e.g. a weekly digest).
- Sending a test to colleagues for sign-off before scheduling.
- Re-engaging only those who opened or clicked a previous send (A/B test shipping method).
Path map
flowchart LR F(["Email campaign builder wizard, A/B test, schedu…"]):::feat F --> H1["Single-send campaign scheduled"]:::ok F --> H2["A/B test campaign"]:::ok F -.-> X1["Name or subject blank"]:::bad F -.-> X2["No segment"]:::bad F -.-> X3["Empty template"]:::bad F -.-> X4["Start date in the past"]:::bad F -.-> X5["End before start"]:::bad F -.-> X6["Dates missing"]:::bad F -.-> X7["A/B split invalid"]:::bad F -.-> X8["A/B shipping method missing"]:::bad F -.-> X9["B subject missing"]:::bad F -.-> X10["Invalid HTML parameters at publish"]:::bad F -.-> X11["Missing unsubscribe link"]:::bad F -.-> X12["Email bag exhausted"]:::bad F -.-> X13["Test send with a bad address list"]:::bad F -.-> X14["Duplicate campaign posting twice"]:::bad F -.-> X15["Raw translation key on screen"]:::bad classDef feat fill:#b0432c,stroke:#b0432c,color:#ffffff,font-weight:bold classDef ok fill:#e3f1ea,stroke:#0d6b42,color:#0d4a2e classDef bad fill:#f7e6e2,stroke:#9a2d1c,color:#6e1f13
Happy paths
Single-send campaign scheduled
- Step 1: name, subject, segment and the start/end datetime window are set; frequency is single sending.
- Step 2: an HTML template is imported or composed; personalisation variables are inserted and the unsubscribe link check passes.
- Step 4 summary shows the estimated reach and the remaining email-bag quota.
- A test email is sent and approved.
sendCampaignpublishes it and the campaign moves to scheduled.
A/B test campaign
toggleABTestenables the variant; subject B, a B template and a quote (percentage split) are entered.- At least one shipping method (opened or clicked) is selected.
- The quote validates as an integer between 0.01 and 99.99.
- Step 3 designs template B; the campaign is published with the split.
Failure paths
Name or subject blank
Cause — Required step-1 fields empty.
What happens —
error.email-campaign.name.required/error.email-campaign.subject.required.
No segment
Cause —
segmentbelongsTo empty.What happens —
error.email-campaign.segment.required.
Empty template
Cause —
htmlBodyblank.What happens —
error.email-campaign.htmlBody.required; the design step’s own validation blocks advancing.
Start date in the past
Cause —
startDatetimebefore now.What happens —
error.email-campaign.date.invalid_past.
End before start
Cause —
endDatetimeearlier thanstartDatetime.What happens —
error.email-campaign.endDatetime.post_startDatetime.
Dates missing
Cause — Either datetime blank.
What happens —
error.email-campaign.date.required.
A/B split invalid
Cause — Quote blank, not an integer, or outside 0.01–99.99.
What happens —
error.email-campaign.quote.required,error.email-campaign.quote.integer,error.email-campaign.quote.invalid.
A/B shipping method missing
Cause — Neither
testOpenednortestClickedselected on an A/B campaign.What happens —
error.email-campaign.testOpenedOrClicked.required.
B subject missing
Cause — A/B enabled without
testAb.subject.What happens — Reuses
error.email-campaign.subject.required.
Invalid HTML parameters at publish
Cause — The template contains personalisation tokens the renderer cannot resolve.
What happens —
email-campaign.error-invalid-html-paramsnames the template and blocks publishing; warnings appear earlier in the wizard.
Missing unsubscribe link
Cause — Template has no unsubscribe token.
What happens —
email-campaign.warning-unsub-link— a warning, not a block, so a non-compliant email can still be sent.
Email bag exhausted
Cause — The centre’s send quota is consumed.
What happens —
email-campaign.new.consumed-emailbag-warning-messagewarns on the summary step.
Test send with a bad address list
Cause —
sendTestEmailreceives something that is not an array of e-mails.What happens — Throws
TypeError('The e-mail list is not a array with e-mails.')aftervalidateEmailListfails — a developer-facing error rather than inline field feedback.
Duplicate campaign posting twice
Cause — Double submission of a duplicated campaign.
What happens — Addressed by a dedicated fix (
duplicate-email-campaign-should-not-post, SC-13815) — the duplicate action no longer re-posts.
Raw translation key on screen
Cause — The code references
email-campaign.insert-variable.placeholderbut the key does not exist in translations.json.What happens — ember-intl renders the missing key (or its missing-message placeholder) instead of text — visible to every user of this screen until the key is added.
QA checklist
Tick these off while regression-testing this feature. The boxes are plain Markdown, so progress is visible in git diffs.
- ✅ Single-send campaign scheduled
- ✅ A/B test campaign
- ⛔ Name or subject blank
- ⛔ No segment
- ⛔ Empty template
- ⛔ Start date in the past
- ⛔ End before start
- ⛔ Dates missing
- ⛔ A/B split invalid
- ⛔ A/B shipping method missing
- ⛔ B subject missing
- ⛔ Invalid HTML parameters at publish
- ⛔ Missing unsubscribe link
- ⛔ Email bag exhausted
- ⛔ Test send with a bad address list
- ⛔ Duplicate campaign posting twice
- ⛔ Raw translation key on screen
Implementation
app/controllers/marketing/emailing/new.jsapp/routes/marketing/emailing/new.jsapp/models/email-campaign.jsapp/models/test-ab.jsapp/models/email-bag.jsapp/services/api-email-campaigns.jsapp/enums/email-campaign-frequency-types.jsapp/enums/emailing-campaign-html-params.jsapp/components/email-variable-selector.jsapp/components/nf-code-param-list.js
Translation keys (33)
Key English email-campaign.newCreate an email campaign email-campaign.editEdit an email campaign email-campaign.form.general_infoCampaign details email-campaign.form.desing_templateEmail design email-campaign.form.design_template_bEmail design B email-campaign.form.summaryReview and send email-campaign.single-sendingSingle sending email-campaign.every-15-minutesEvery 15 minutes email-campaign.every-30-minutesEvery 30 minutes email-campaign.hourlyHourly email-campaign.dailyDaily email-campaign.weeklyWeekly email-campaign.params.subject.center_nameAdd the centre name email-campaign.params.subject.user_codeAdd the user code email-campaign.params.subject.user_emailAdd the user email email-campaign.params.subject.user_nameAdd the user name email-campaign.warning-invalid-html-paramsWarning! The current template contains one or more invalid parameters: {params} email-campaign.warning-unsub-linkWarning! The current template doesn’t have an unsubscribe link. Please, add one before publishing the campaign. email-campaign.error-invalid-html-paramsThe template {template} contains invalid parameters. Please fix them before publishing the campaign. email-campaign.new.consumed-emailbag-warning-messageATTENTION! You have consumed {consumedPercentage}% of your emailbag, {daysLeft} days left until contract renewal. segment.users_reached.infoUsers with accepted permissions and valid email segment.users_reached.info_with_email_validationUsers with accepted permissions and verified email error.email-campaign.name.requiredName can’t be blank error.email-campaign.subject.requiredEmail subject can’t be blank error.email-campaign.htmlBody.requiredCampaign template can’t be blank error.email-campaign.segment.requiredSegment field can’t be blank error.email-campaign.date.requiredDate can’t be blank error.email-campaign.date.invalid_pastStart date can’t be in the past error.email-campaign.endDatetime.post_startDatetimeEnd date must be after start date error.email-campaign.quote.requiredGroup percentage required error.email-campaign.quote.integerThe number entered must be an integer error.email-campaign.quote.invalidThe group percentage must be between 0 and 100 error.email-campaign.testOpenedOrClicked.requiredYou must select at least one shipping method
Related
- Same area — Activity list & duplication · Activity editor (loyalty mechanics, rules & targeting) · Activity report & PDF · Activity campaigns (programme grouping) · Email campaign list, duplication & stop · Digital games (lottery) · Fidcard · Present cards (Fideliza gift cards) · Book appointment
- Shares data with — Customer list & filtering · Save current filters as a segment · Segment list, refresh & export · Segment builder with automatic tag actions · Proposal editor (content, coupon, push, AI images) · Push notification editor (scheduling, geofencing, deep links) · Filter & segment builder component
- Behind the same module — Email campaign list, duplication & stop
Generated from doc/feature-inventory.json · id marketing-email-campaign-builder · commit 276788ca6