Skip to content

CreatorTrack Campaigns app (Mautic + Resend, read-only v1)

URL: https://mkdocs.justinsforge.com/memory/general/reference_creatortrack_campaigns_app/

Cross-brand email marketing cockpit inside CreatorTrack: per-brand subscriber counts, campaign list, open/click/fail rates, per-email reports. Read-only; authoring/sending deep-links to mautic.justinsforge.com. Built 2026-07-06 on branch campaigns (plan: creatortrack-campaigns-app-2026-07-06).

LIVE on prod 2026-07-06 (merge b280b44). Deploy note: the branch predated the finance proxy pattern, so the integrator had to add /api/campaigns/jobs to PUBLIC_PATHS in proxy.ts (cookie-exempt, still bearer-gated) or the middleware 401s the timer POSTs before the route runs (commit b280b44). Timer installed user scope (~/.config/systemd/user/, mirrors finance timers), NOT /etc/systemd/system. Both jobs verified green against live Mautic/Resend on prod (11 emails, 3 segments, 56 resend events).

Architecture (finance-module pattern)

  • PG read copy campaigns.* in lifeos: emails, segments, send_events, job_runs. All workspace-scoped + RLS workspace_isolation, grants to lifeos_app. Migration db/migrations/20260706T145955_campaigns_campaigns_schema.sql (suite repo).
  • Brand map is a TS constant, NOT a table: lib/campaigns/brands.ts (6 brands, Mautic category ids: gusoutdoor 1 / nova 2 / gusbass 3 / justinwieb 4; sipnserve + creatortrack have none). Attribution rule: category id first, from-address domain fallback, name hints last. NEVER scope by Mautic segment visibility (segments are account-wide).
  • Ingest jobs lib/campaigns-jobs.ts + lib/campaigns-jobs/{mautic-sync,resend-poll}.ts. mautic-sync: emails + segments (contact counts via /api/contacts?search=segment:<alias>) + 90d email_stats rows -> send_events (sent/read/failed); prunes rows deleted in Mautic; recipient addresses stored only as sha256 hashes. resend-poll: newest 100 Resend sends -> send_events (delivered/bounced/...). Both idempotent (upsert keys), log to campaigns.job_runs, fail loud via forge_notify.
  • Clients: lib/campaigns/mautic-read.ts (Basic auth vs LAN http://192.168.86.87:8090, needs X-Forwarded-Proto: https header or Mautic 301s), lib/campaigns/resend-read.ts (RESEND_API_KEY via resendEnv()).
  • Route: POST /api/campaigns/jobs/[job], bearer CAMPAIGNS_JOBS_TOKEN (in ~/.forge-secrets/creatortrack-auth.env, timingSafeEqual), jobs: noop, mautic-sync, resend-poll.
  • UI: app id campaigns in lib/apps.ts (route-app, ownerOnly, "New email"). Pages app/(suite)/campaigns/page.tsx (all-brands grid; ?brand=<key> detail: tiles + 30d SVG send chart + emails + segments) and app/(suite)/campaigns/email/[id]/page.tsx (per-email report). CSS components/apps/campaigns/campaigns.css.
  • Read layer: lib/campaigns.ts (rollups, series, email report); RLS scopes reads, no explicit workspace filter.
  • Timers (forge repo): scripts/forge_creatortrack_campaigns_jobs.sh (env override CAMPAIGNS_JOBS_BASE for dev slots) + scripts/systemd/forge-creatortrack-campaigns-sync.{service,timer} (every 30 min). NOT enabled until prod deploy.

Deploy checklist (post-merge)

  1. Merge branch campaigns into suite main (single AI-reconciled merge, WDA doctrine).
  2. Apply migration to live lifeos: cd ~/creatortrack && WORKSPACE_MIGRATIONS_DIR=db/migrations python3 ~/forge/scripts/forge_workspace_migrate.py.
  3. Ensure CAMPAIGNS_JOBS_TOKEN reaches creatortrack-prod's environment (already in creatortrack-auth.env); restart creatortrack-prod.service.
  4. systemctl enable --now forge-creatortrack-campaigns-sync.timer (symlink units from ~/forge/scripts/systemd/).
  5. Teardown dev slot: git -C ~/forge-suite worktree remove ~/forge-suite-wt/campaigns + DROP DATABASE ct_campaigns.

Verified 2026-07-06 (dev slot :3063, DB ct_campaigns)

Both jobs green end-to-end against live Mautic/Resend (11 emails, 3 segments, 18 stat rows, 56 resend events; all 6 brands attribute correctly). Route auth 200/401/404 correct. Dashboard, brand view, and email report render with real data. Trigger script dry-run green.

Related: reference_forge_mail_mautic, reference_creatortrack_finance_module, reference_creatortrack_parallel_dev.

[Claude Code]