Reference → Newsletters — Reference

Newsletters — Reference

The Newsletters surface is the recurring-broadcast plane for every SGEN site. It owns the subscriber lists, the campaign drafts, the send schedule, and the delivery / open / click reporting for every newsletter the business sends. Anything that goes out as a one-to-many email campaign to a subscriber list — promotional broadcast, weekly digest, transactional announcement, product update — traces back to a record managed here.

This page is a reference for platform engineers and integrators who need to understand the surface before extending it, scripting against it, or scoping a campaign workflow. Customer-facing how-tos live in the customer docs set; this page describes the shape of the surface, not the steps to drive it.


Overview

Newsletters live under the Newsletters module in SG-Admin. The module renders five primary views — the campaigns list, the campaign editor (draft view), the subscriber-list manager, the send-scheduler, and the per-campaign reporting view — and exposes a small set of write operations for create, edit, schedule, send, pause, archive, and per-subscriber action (resubscribe, unsubscribe, hard-bounce mark).

The module is paired by convention: one half renders the views and prepares the data, the other half handles writes and returns AJAX responses. Engineers reading the SG-Admin source will see this split across two controller files plus an async send worker; the reference below describes the combined surface as it appears to a calling integration.

A subscriber on a newsletter list is not automatically a contact. The lists are address-keyed and carry their own opt-in record, separate from the Contacts surface. A subscriber and a contact can be linked when the same email appears in both — the link is opportunistic, set at capture time when a known contact subscribes, but is optional. This split keeps newsletter compliance (opt-in proof, unsubscribe one-click, GDPR audit) isolated from CRM relationship data.

Where it lives in SG-Admin:

  • Sidebar: SG-Admin → Newsletters
  • URL prefix: /sg-admin/newsletters/
  • View templates: application/views/Admin/Newsletters/
The module surface is intentionally broad — a campaign carries the content, the schedule, the audience, the delivery outcome, and the engagement signals. Heavier downstream concerns (multi-channel automation, behavior-triggered drip sequences, A/B testing across many variants) live in adjacent surfaces. This page covers the per-campaign send-and-measure plane plus the subscriber-list management primitives.
┌────────────────────────────────────────────────────────────────────────────┐│ SG-Admin → Newsletters → Campaigns [+ New campaign] │├────────────────────────────────────────────────────────────────────────────┤│ Campaign List Status Sent ││ ──────────────────────── ────────────────── ────────────── ──────────── ││ May Product Update All subscribers sent 2d ago ││ 8,247 sent · 4,102 opened (49.7%) · 312 clicked (3.8%) ││ ││ Q2 Pricing Announcement Customers (paying) sending 64% in flight ││ 5,300 sent · 1,800 opened (no final read yet) ││ ││ Welcome Series — Step 1 New subscribers scheduled Tue 09:00 ││ ││ Weekly Digest #214 Newsletter readers draft — ││ ││ [⋯ Edit] [⋯ Schedule] [⋯ Send test] [⋯ Pause] [⋯ View report] │└────────────────────────────────────────────────────────────────────────────┘

Actions

The Newsletters surface exposes the following operations. Each is described by what it does to the data, not by its internal method name.

List campaigns

Returns the campaign records visible to the current operator, paginated, with name, list, status, sent-count, open-rate, and click-rate columns. Supports column sort, free-text filter, status filter (draft / scheduled / sending / sent / paused / archived), list filter, date-range filter on sent-at, and per-page count. The default view scopes to active and recent campaigns; archived surfaces only via filter.

Create campaign

Opens an empty campaign editor. Required fields: campaign name, from-name, from-address, subject, body (HTML and plain-text), target list. Optional fields cover pre-header text, reply-to address, tag set, schedule (one-shot or recurring), A/B variant configuration, and a test-recipient list. On submit, the surface validates the from-address against the configured sender domains, validates the HTML body for sanitization, and persists the record as a draft.

Edit campaign

Loads an existing draft into the editor. Submit replaces the stored values. Campaigns in scheduled status can be edited and the schedule re-armed; campaigns in sending or sent status are read-only — edits create a new draft duplicate rather than modifying the live record.

Send test

Sends the campaign to a small list of test recipients (configured per operator or per campaign). Useful for a final preview against real inboxes before scheduling the broadcast. Test sends do not affect the campaign's reporting numbers.

Schedule

Sets the send time and arms the campaign for delivery. Required: send-at timestamp (operator-local) or recurring schedule (cron-style expression). Optional: time-zone-aware sending (deliver at recipient's local 9 AM rather than a single global moment). On submit, the surface registers a Cron Jobs row (one-shot or recurring) whose task target is a newsletters send-task slug.

Send now

Bypasses the scheduler and dispatches immediately. The send worker picks up the campaign within the next polling interval (default 30 seconds). Useful for time-sensitive announcements; not a substitute for testing.

Pause / resume

Stops an in-flight send between batches. The worker checks for the pause signal at every batch boundary and exits at the next checkpoint. Partial delivery is captured — recipients already sent remain sent; recipients not yet reached return to the queue. Resume picks up from the queue without re-sending. Useful when the operator realizes the body has a defect mid-send and wants to fix before continuing.

Archive

Marks a sent campaign archived. Archived campaigns disappear from the default list view but remain in the database with their full reporting. Useful for tidying the campaigns list after a quarter of broadcasts.

Add subscriber

Adds an email to a newsletter list. The required input is the email; optional inputs cover the subscriber name, the source (how the subscriber opted in), and any custom-fields configured on the list. The surface enforces double-opt-in if configured for the list — adding writes a pending subscription and triggers a confirmation email; clicking the confirmation link transitions to subscribed.

Bulk import subscribers

Accepts a CSV or paste-block and applies it as subscriber records in a single transaction. Each row is validated independently (email format, list-level duplicates, suppression-list intersection). The surface returns a per-row pass / fail report and applies only the passing rows. Imports do not bypass double-opt-in unless explicitly flagged with a prior_opt_in_proof field per row.

Unsubscribe

Removes a subscriber from a list (or from all lists, depending on the unsubscribe link clicked). The record is retained as unsubscribed for audit (proving the unsubscribe was honored on a given date). Resubscribing through the public form transitions back to subscribed; operator-side resubscribe is gated by a compliance prompt.

Mark hard-bounce

Transitions a subscriber to bounced. Hard-bounces are typically set by the delivery worker on a permanent failure code (mailbox-not-found, domain-rejected). Manual marks are supported for operator-discovered cases. Bounced subscribers are excluded from future sends and surface in the list manager as cleanup candidates.

Per-campaign reporting

Aggregates the delivery, open, click, unsubscribe, and bounce signals into per-campaign rollups and per-subscriber rows. The rollup view shows sent / opened / clicked / unsubscribed / bounced counts plus the open-rate and click-rate percentages. The per-subscriber view shows each recipient's outcome (sent timestamp, opened timestamp, clicks, unsubscribe timestamp if applicable). Reporting is computed from the delivery-event ledger; raw events are retained for a configured window before rollup-only retention takes over.


Data model

The Newsletters surface carries two primary record types — campaigns and subscribers — plus the delivery-event ledger that connects them.

A campaign record carries the following fields.

FieldTypeNotes
idintegerPrimary key. Stable across edits.
namestringInternal display. Not the email subject.
subjectstringEmail subject line.
pre_headerstringPre-header text shown after subject in some clients.
from_namestringDisplay sender name.
from_addressstringSender email. Must match a configured sender domain.
reply_tostringOptional. Defaults to from-address.
body_htmltextHTML body. Sanitized at save.
body_texttextPlain-text alternative body.
list_idintegerTarget subscriber list.
statusenumdraft, scheduled, sending, sent, paused, archived.
schedule_idintegerCron Jobs row identifier when scheduled. Null for one-shot send-now.
send_attimestampWhen the broadcast is set to start.
sent_countintegerFinal recipient count after send completes.
tagsarrayInternal tagging.
created_attimestampSet on create, immutable.
updated_attimestampTouched on any edit.
A subscriber record carries the following fields.
FieldTypeNotes
idintegerPrimary key.
list_idintegerThe list this subscription is against. A subscriber can appear on multiple lists with distinct rows.
emailstringThe subscription email. Unique per list.
namestringOptional display name.
statusenumpending, subscribed, unsubscribed, bounced.
sourcestringCapture origin (form slug, import batch, manual).
custom_fieldsjsonPer-list custom fields.
contact_idintegerOptional FK to the Contacts surface when the email matches a known contact.
subscribed_attimestampSet on transition to subscribed.
unsubscribed_attimestampSet on transition to unsubscribed.
bounced_attimestampSet on transition to bounced.
Delivery-event ledger. Every send writes a row per recipient — sent, opened (when the tracking pixel fires), clicked (per click), unsubscribed (when the unsubscribe link fires), bounced (when the delivery worker reports a permanent failure). The ledger is the source of truth for reporting; rollups are computed from it.

Opt-in proof retention. Subscriber records retain the opt-in event (timestamp, IP, user agent, source page URL when known) for the configured retention window. This is the compliance audit trail — required for GDPR / CAN-SPAM proof of consent.

Suppression list. A site-level suppression list holds emails that should never receive any campaign regardless of list membership (legal holds, hard-bounced across multiple lists, complaint signals from the delivery provider). The send worker checks the suppression list before every batch; suppressed recipients are skipped silently.

CAMPAIGN RECORD├── id integer primary key├── name string internal display├── subject string email subject├── pre_header string client preview text├── from_name string sender display├── from_address string must match configured sender domain├── reply_to string defaults to from-address├── body_html text sanitized at save├── body_text text plain-text alternative├── list_id integer target subscriber list├── status enum draft | scheduled | sending | sent |│ paused | archived├── schedule_id integer Cron Jobs row id (null = send-now)├── send_at timestamp start of broadcast├── sent_count integer final recipient count└── timestamps created_at, updated_atSUBSCRIBER RECORD├── id integer primary key├── list_id integer FK to subscriber list├── email string unique per list├── status enum pending | subscribed |│ unsubscribed | bounced├── source string capture origin├── custom_fields json per-list config├── contact_id integer optional FK to Contacts└── lifecycle ts subscribed_at, unsubscribed_at, bounced_at↓ on every sendDELIVERY EVENT LEDGER ROW(campaign_id, subscriber_id,event_type, timestamp, payload)↓ checked by send workerSUPPRESSION LIST(site-level, blocks all sendsregardless of list membership)

Permissions

Access to the Newsletters surface is gated at two layers.

Layer 1 — admin gate. Every action under SG-Admin passes through the platform's standard admin access check at request entry. An unauthenticated request never reaches the Newsletters surface. (Public unsubscribe and double-opt-in confirmation links enter through a separate public endpoint that does not require admin authentication; those paths are gated by a signed-token check.)

Layer 2 — per-action capability. Within SG-Admin, each Newsletters action checks a capability associated with the calling operator's role. The default role configuration ships with three roles — Administrator, Editor, Viewer — and the capability map is:

CapabilityAdministratorEditorViewer
List campaigns
Create campaign
Edit draft
Send test
Schedule
Send now
Pause in-flight send
Archive
View reporting (own campaigns)
View reporting (all campaigns)
Manage subscriber list
Bulk import subscribers
Operator-side resubscribe
Mark hard-bounce manually
Configure sender domains
Manage suppression list
Custom roles defined under Settings → Roles override the default map. The capability slugs are stable; the role slugs are configurable.

Send-now gate. Send-now is intentionally a stricter capability than schedule. A schedule passes through a Cron Jobs row that can be reviewed and cancelled before fire-time; send-now bypasses the review window. The default Editor role can schedule a broadcast but cannot send-now.

Compliance gate. Operator-side resubscribe (overriding an unsubscribe without the subscriber clicking a re-opt-in link) is admin-only and emits an Activity Log entry tagged newsletters.resubscribe_override for compliance audit. Bulk import without prior_opt_in_proof per row is similarly gated and tagged.

PII gate. Subscriber records carry email + name + custom fields, which is PII. Bulk export of subscriber records emits an Activity Log entry tagged newsletters.pii_movement.

Audit. Every write — campaign create, edit, schedule, send, pause, archive, subscriber add, unsubscribe, bounce-mark, suppression-list change, sender-domain configuration change — emits an Activity Log entry. The log records the acting operator, the target record, and the change shape. The delivery-event ledger is the audit trail for the send itself.

SEND REQUEST│▼┌───────────────────────────┐│ Admin gate │ unauth → reject│ (every /sg-admin/* call) │ (public unsub/confirm path separate)└─────────────┬─────────────┘│ authed▼┌───────────────────────────┐│ Capability check │ role lacks cap → reject│ (per-action) │└─────────────┬─────────────┘│ allowed▼┌───────────────────────────┐│ Send-now gate │ send-now is admin-only;│ │ schedule passes through review window└─────────────┬─────────────┘│ passes▼┌───────────────────────────┐│ Compliance gate │ operator-side resubscribe →│ │ newsletters.resubscribe_override tag│ │ bulk import w/o opt-in proof → tagged└─────────────┬─────────────┘│ resolved▼┌───────────────────────────┐│ PII gate │ bulk export →│ │ newsletters.pii_movement tag└─────────────┬─────────────┘│ tagged▼┌───────────────────────────┐│ Suppression check │ send worker filters out│ │ suppression list per batch└─────────────┬─────────────┘│ filtered▼action executes│▼Activity Log entry +delivery-event ledger rows

Related references

  • Settings — Reference. Owns the sender-domain configuration (SPF / DKIM / DMARC alignment), the suppression list, the double-opt-in toggle per list, the default delivery provider, the opt-in retention window, and Activity Log retention.
  • Contacts — Reference. Subscriber records carry an optional contact_id link when the email matches a known contact. The link is opportunistic — newsletter compliance data stays on the subscriber record, CRM relationship data on the contact.
  • Cron Jobs — Reference. Scheduled campaigns are Cron Jobs rows. Edit the schedule there; edit the campaign content here. A scheduled send fires the campaign's send-task slug.
  • Activity Log — Reference. Source of the newsletters audit trail. Every write emits an entry; the newsletters.resubscribe_override, newsletters.pii_movement tags are bridges for compliance review.
  • Exports — Reference. The newsletter subscriber set is exportable as a structured CSV / JSON / XML. Exports raise the PII tag.
  • Forms — Reference. Public-form signups that target a newsletter list create subscriber records directly. The form's anti-abuse layer plus the list's double-opt-in setting gate the capture.
  • Notifications — Reference. Operator-side campaign-sent and campaign-paused notifications resolve through the notification template surface. Subscriber-side double-opt-in confirmation and unsubscribe-receipt emails are separate from the broadcast itself and live as system templates.
  • Custom Fields — Reference. Per-list custom fields on subscriber records are defined here. Renaming or retiring a field does not rewrite stored values.
For the corresponding customer-facing walkthrough — drafting a campaign, scheduling a recurring digest, importing a subscriber list with prior opt-in proof — see the Newsletters section of the customer docs at /docs/newsletters.
On this page