Files
descrybe/docs/marketing-suite-design.md
greeneclipse 8580c996c3 Initial commit of Descrybe v2 without local scratch artifacts.
Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
2026-08-09 22:47:43 +02:00

22 KiB
Raw Permalink Blame History

Marketing Suite — architecture design (Descrybe v2)

Status: Design only — no implementation in this doc.
Date: 2026-08-04
Repos: f:/laragon/www/_MY/descrybe-v2
Inputs: PRICING-AND-USER-GROWTH.md (sibling), process-and-sell-summary.md, features.md, existing internal/billing, internal/woocommerce, internal/processing, internal/mail, Settings + Nav.


0. North star

Descrybes moat remains feeds → map → process → export/Woo. Marketing add-ons sit on top of a live catalog + Woo connection, not as a second product that competes with “paste ChatGPT into a plugin.”

[ Catalog + Brand kit ]
        │
        ├── Product AI copy (existing pipeline) ── credits / BYOK
        │
        └── Marketing Suite
              ├── Audience (orders + opt-in)
              ├── Campaigns (seasonal / category / product)
              ├── Reviews (sync + social proof in copy)
              └── SEO recommendations (catalog-derived)

Principle: Defaults get a merchant from “Black Friday email for buyers of Category X” in minutes. Advanced (custom prompts, BYOK, segment builders) stays behind optional panels.


1. Context from todays codebase

Area Today (v2) Implication for Marketing
Woo Company-level woocommerce_configs; outbound product push only (internal/woocommerce); worker claims sync jobs Must add inbound order (+ later review) sync; keep credentials encrypted; reuse same store config
Credits credit_balances + AssertCanStartProcessing / ConsumeCredits(..., featureName) Extend with feature names: campaign_copy, seo_recommend, brand_kit_ai; same wallet
Plans plans / company_plans + SKU caps; public Free/Starter/Growth/Business/Enterprise in pricing-data.ts Need feature flags per plan (not only meters) for Marketing
AI Worker Completer (OpenAI or heuristic); no tenant BYOK yet Campaign/SEO/Brand AI share Completer + future BYOK seam
Mail Platform SMTP for invites (internal/mail); noop if disabled Campaign send ≠ platform invite SMTP — tenant-verified provider required
Nav / Settings Nav: PIM core + WooCommerce extra; Settings tabs: profile / company / api-keys / team Add Marketing nav group; Settings → Integrations tab (Woo, Email, Reviews, AI keys)
Jobs DB claim + NOTIFY for processing + Woo Same pattern for order_sync, review_sync, campaign_send, campaign_generate

Pricing doc conflict (resolve explicitly)

PRICING-AND-USER-GROWTH.md currently proposes Free = 50 AI credits / mo.
This suite design requires Free = ZERO AI credits and gates campaigns / SEO AI / brand AI.

ASSUMPTION (recommended): Align product policy as:

Plan Core product AI Marketing AI (campaigns / SEO / brand kit AI)
Free 0 credits (map/export/Woo test only; reverse-trial exception below) Blocked
Starter+ Included pack per plan Included; burns same credit wallet (or BYOK)
Reverse trial (14d Growth) Full Growth credits Full Marketing allowed

Update pricing-data.ts + plan seeds when implementing — Free must not advertise “50 AI credits” if policy is zero.


2. Product modules

2.1 Email campaigns

Jobs: Draft → preview → schedule/send seasonal or catalog-driven emails using Descrybe catalog + Woo audiences.

Campaign types (MVP → later)

Type Trigger / audience seed Copy source
Seasonal template Christmas, Black Friday, New Year, Summer (system templates) Default prompts + brand kit
By category Products in selected category tree node(s) Catalog titles/images + formula fields
By product set Explicit SKUs / product IDs / collection Same
Purchase-based Bought X / not bought Y (needs order sync) Same + segment filters

Prompt model (mirror product formulas)

campaign.prompt_mode = "default" | "custom"
campaign.system_prompt_override?   // only if custom + plan allows
campaign.user_prompt_vars = { season, category_names, product_snippets, brand_voice, cta_url }
  • Default: curated seasonal/category prompts + Brand kit (voice, tone, banned words).
  • Custom: editable system/user prompt (Growth+); stored per campaign, versioned.
  • Preview: generate N sample emails (13) before send; each preview burns credits (or BYOK).

AI path

Path When Billing
Managed Default Debit campaign_copy (+ token packs via existing openai_token_k)
BYOK Growth add-on / Business+ Completer uses tenant key; no credit burn for inference; still need paid plan feature flag

Never send AI-generated body without human confirm on first send of a campaign (checkbox “I reviewed the preview”). Resends of an approved version can skip.

Data model (sketch)

marketing_campaigns
  id, company_id, name, type, status (draft|scheduled|sending|sent|cancelled)
  season_key?, category_ids[], product_ids[]
  audience_query jsonb          -- segment DSL (see §3)
  prompt_mode, prompts jsonb
  subject, preview_text, body_html, body_text
  schedule_at?, sent_at?
  ai_provider (managed|byok), credit_cost_est
  created_by, created_at, updated_at

marketing_campaign_sends
  id, campaign_id, recipient_hash, email_encrypted_or_token
  status (queued|sent|bounced|unsubscribed|failed)
  provider_message_id?, error?, sent_at?

marketing_campaign_events  -- opens/clicks later (P2)

Worker jobs: campaign_generate, campaign_send_batch (chunked, rate-limited).


2.2 Order sync (Woo → audience)

Goal: Build segments like “purchased product/category X in last 90d AND never purchased Y.”

Sync design

  • Extend Woo Integrations config (same woocommerce_configs row or sibling integration_woocommerce_orders):
    • orders_enabled, orders_last_synced_at, orders_cursor (modified_after / page)
  • Worker job woo_order_sync (incremental, bounded pages per tick)
  • REST: GET /wp-json/wc/v3/orders (+ line items); map to local tables
woo_customers
  company_id, woo_customer_id, email_hash, email_enc, name, marketing_opt_in, unsubscribed_at, ...

woo_orders
  company_id, woo_order_id, customer_id, status, ordered_at, currency, total, raw_meta jsonb

woo_order_items
  company_id, order_id, sku, product_id_local?, woo_product_id, quantity, category_ids_snapshot[]

Privacy: store emails encrypted at rest; display masked; use email_hash for uniqueness. Prefer Woo marketing_opt_in / billing consent fields when present.

Audience DSL (JSON, server-validated)

{
  "op": "and",
  "rules": [
    { "type": "purchased_sku", "skus": ["ABC-1"], "since_days": 365 },
    { "type": "not_purchased_sku", "skus": ["ABC-2"], "since_days": 365 },
    { "type": "purchased_category", "category_ids": ["uuid…"], "since_days": 90 },
    { "type": "marketing_opt_in", "equals": true }
  ]
}

Resolver compiles to SQL with hard filters: unsubscribed_at IS NULL, marketing_opt_in = true, bounce suppression list. Always AND these safety filters server-side (client cannot disable).

ASSUMPTION: Guests without account email are skipped unless order billing email present and opted-in.


2.3 Reviews sync

Provider Phase Notes
WooCommerce product reviews (/wp-json/wc/v3/products/reviews) P1 Same store credentials; incremental sync
Placeholder providers (Judge.me, Yotpo, Trustpilot) P2 Interface only: ReviewProvider with ListSince(cursor); UI “Coming soon” + webhook stub
product_reviews
  company_id, provider, external_id, product_sku, rating, title, body, author_hash
  reviewed_at, synced_at, usable_in_marketing bool

Uses: social proof blocks in campaign templates; optional “mention top reviews” in default prompts; SEO module “products with < N reviews.”


2.4 SEO recommendations (from catalog)

Not a full site crawler in P0. Catalog-first checks that reuse processed products + categories + standard fields:

Check Signal Output
Thin title / description Length, uniqueness Rewrite suggestion (AI = paid)
Missing attributes Required attrs empty Fill via process job CTA
Duplicate titles Collision count Merge / differentiate
Weak category paths Depth / orphan Taxonomy tip
Image gaps No primary image Upload / feed map tip
Keyword coverage Brand kit primary keywords vs title Nudge
seo_recommendation_runs
  id, company_id, status, created_at, summary jsonb

seo_recommendations
  id, run_id, product_id?, severity, code, message, suggested_action
  ai_draft?, applied_at?
  • Free: show rule-based list only (no AI drafts).
  • Paid: “Generate rewrite” → seo_recommend credits / BYOK → optional apply to product fields via existing product PATCH.

2.5 Brand kit

Company-scoped creative constraints for product copy + campaigns.

brand_kits
  company_id PK
  voice_summary text          -- short “we sound like…”
  tone_tags text[]            -- e.g. friendly, expert, playful
  banned_phrases text[]
  preferred_phrases text[]
  primary_color, secondary_color
  logo_url / logo_file_id
  fonts jsonb?                -- optional
  locale_default
  updated_at
  • Manual edit: available Starter+ (or Free read-only empty kit?). ASSUMPTION: Free can view empty kit + upgrade CTA; edit + AI-assist = paid.
  • AI assist (“Infer brand from catalog”): paid; feature brand_kit_ai.
  • Injected into processing enhance prompts and campaign defaults (single source of truth).

UI: Settings → Company or Marketing → Brand kit (prefer Marketing → Brand kit with deep-link from Integrations).


3. Plan gating & Free tier

3.1 Feature matrix

Capability Free Starter Growth Business+
SKU / feed / storage meters Per pricing ladder
AI credits 0 Pack Pack Pack / BYOK
Product processing AI ✗ (unless reverse trial)
Marketing nav (read-only teasers) Teaser + upgrade
Campaigns create/send ✓ (caps)
Campaign AI generate Credits Credits/BYOK
Order sync ✗ (or read-only test 1 page?)
Audience “bought X not Y”
Reviews sync (Woo)
SEO rule-based ✓ (limited rows)
SEO AI rewrite
Brand kit edit
Brand kit AI
BYOK Add-on Included

ASSUMPTION: Starter is the smallest plan that unlocks Marketing Suite send + order sync. Free stays acquisition for feed aha, not email ESP.

3.2 Enforcement seams (reuse billing)

Extend internal/billing:

// Pseudocode — design only
AssertFeature(ctx, companyID, "marketing.campaigns")
AssertFeature(ctx, companyID, "marketing.order_sync")
AssertCanSpendCredits(ctx, companyID, feature, est)
  • Plan row gains features jsonb (or plan_entitlements table).
  • API returns 403 + machine code feature_gated / insufficient_credits for UI UpgradeBanner.
  • Reverse trial: temporary Growth entitlements; day 15 → Free (0 credits, Marketing hard-off). Persist draft campaigns read-only.

3.3 Caps (Starter starter-limits)

Meter Starter (example) Growth
Campaigns / mo 5 50
Recipients / send 2_000 25_000
Order sync lookback 180d 730d
SEO AI rewrites / mo 50 1_000

Exact numbers tunable; enforce server-side.


4. Integration style (UX IA)

4.1 Settings → Integrations

Replace scattered /woocommerce as the only integration home. Keep /woocommerce as redirect → Settings Integrations for one release if needed.

Settings tabs: profile | company | team | api-keys | integrations

Integrations cards:

  1. WooCommerce — store URL, keys, test connection, product sync toggle, order sync toggle, reviews sync toggle
  2. Email sending — provider (Resend / SES / SMTP / Postmark), from-domain, verification status, rate limits
  3. AI keys (BYOK) — Growth+; encrypted; never logged
  4. Reviews providers — Woo (active) + placeholders

4.2 Marketing nav

New top-level group (after Export Feeds / before Billing):

Marketing
  ├─ Campaigns
  ├─ Audiences
  ├─ Reviews
  ├─ SEO
  └─ Brand kit

Free users see routes with locked empty-states + upgrade CTAs (not 404).


5. Safety (non-negotiable)

Rule Implementation
No send without verified provider email_provider.status == verified AND DNS/domain checks passed; else Send button disabled + API 412 email_not_verified
Opt-out / unsubscribe Every email: List-Unsubscribe + one-click POST; landing page; set unsubscribed_at; suppress forever until re-opt-in via Woo/consent
Opt-in required Audience resolver always requires marketing consent
Rate limits Per-company send RPM/RPH; provider caps; exponential backoff; circuit breaker on bounce rate > threshold
PII No emails in logs; hash + encrypt; admin support tools mask by default
Double opt-in for imported lists If CSV import ever added (P2): require confirmation — Woo-sourced opted-in only in P0/P1
Abuse Cap Free teasers; paid caps; platform admin kill-switch per company
Legal copy Footer template mandatory (company address, unsubscribe) before schedule

Platform invite SMTP (internal/mail) must not be used for marketing blasts.


6. UX principles

  1. Happy path wizard: Pick template (BF/Christmas) → pick category/products → auto audience suggestion → generate 1 preview → connect email if missing → schedule.
  2. Advanced accordion: custom prompts, raw audience DSL, UTM params, send-time optimization (P2).
  3. Reuse catalog chrome: product pickers, category tree, UpgradeBanner, processing job progress patterns.
  4. Empty states teach the core path: “Connect Woo + sync orders to target buyers of X.”
  5. Credit honesty: show estimated credit cost before Generate; BYOK badge when active.

7. Interactive tutorial (activation)

Extend the pricing docs ≤15 min aha into a guided checklist (dashboard + optional spotlight tour):

Step Route / action Done when
1. Add feed /feeds Feed created with URL/file
2. Map fields /feeds/{id}/mapping Required fields mapped (auto-map OK)
3. Process sample /processing ≥10 products completed (credits: trial/paid)
4. Export or Woo push /export-feeds or Integrations Export generated or Woo test sync OK
5. Brand kit (paid) /marketing/brand-kit Voice saved or skipped with “later”
6. Campaigns (paid) /marketing/campaigns Draft campaign created (send optional)
  • Persist onboarding_progress on company_settings.
  • Free users complete 14; 56 show locked steps with value props.
  • Tutorial deep-links must survive refresh; dismissible; “Replay tour” in help.

Aligns with process-and-sell-summary.md happy path + Marketing as step after export.


8. Technical architecture (v2 packages)

apps/api/internal/
  marketing/           # campaigns, audiences, brand kit, seo services
  integrations/
    email/             # provider interface: Send, VerifyDomain, Webhooks
    reviews/           # ReviewProvider
  billing/             # + AssertFeature, feature costs
  woocommerce/         # + OrdersClient, ReviewsClient, sync workers
apps/api/sql/schema/
  01x_marketing.sql    # tables above
  01y_integrations_email.sql
apps/web/src/routes/
  marketing/campaigns/
  marketing/audiences/
  marketing/reviews/
  marketing/seo/
  marketing/brand-kit/
  settings/            # + integrations tab
  unsubscribe/[token]/  # public

Completer sharing: marketing generation calls the same processing.Completer interface; inject company BYOK client when entitlement + key present.

Idempotency: send batches keyed by (campaign_id, recipient_hash); sync cursors monotonic.


9. API sketch (dashboard + future public)

Method Path Notes
GET/PATCH /api/integrations/woocommerce Existing + order/review flags
POST /api/integrations/woocommerce/sync-orders Enqueue
GET/PATCH /api/integrations/email Provider config
POST /api/integrations/email/verify Start verification
GET/PATCH /api/marketing/brand-kit
GET/POST /api/marketing/campaigns
POST /api/marketing/campaigns/{id}/generate Credits gate
POST /api/marketing/campaigns/{id}/schedule Requires verified email
POST /api/marketing/audiences/preview Count + sample (masked)
POST /api/marketing/seo/runs
POST /api/public/unsubscribe Token auth

Public API v1 exposure of Marketing = Business+ / later; dashboard session auth first.


10. Risks & open decisions

Topic Risk Recommendation
Free AI credits vs pricing doc Conflicting promises Ship Free = 0 AI; update pricing doc + pricing-data.ts
Becoming an ESP Deliverability / spam liability Verified domain only; start with low caps; consider “export to Klaviyo” P2
Order PII GDPR Encrypt, retention policy, delete-with-company, DPA
Woo permission scopes Keys may lack orders/reviews TestConnection checks endpoints; clear UI errors
Custom prompts abuse Prompt injection / brand damage Length limits; strip tools; require preview ack

Open questions (non-blocking for P0 scaffolding):

  1. First ESP: Resend vs SES vs tenant SMTP only?
  2. Starter recipient cap exact number?
  3. Free SEO rule-based: show all issues or cap at 20?

11. Sibling-agent checklist

P0 — foundation (do first; unblocks everything)

  • P0.1 Align Free plan: 0 AI credits; Marketing feature flags on plans; update pricing-data.ts + seeds; reverse-trial keeps Growth marketing.
  • P0.2 billing.AssertFeature + credit feature names (campaign_copy, seo_recommend, brand_kit_ai); wire UpgradeBanner codes.
  • P0.3 Settings → Integrations tab; move Woo config UX there (redirect old /woocommerce).
  • P0.4 Marketing nav shell + locked empty states for Free.
  • P0.5 Brand kit schema + CRUD (manual fields only).
  • P0.6 Email provider integration model + verified-only send gate (even if provider is stubbed behind interface).
  • P0.7 Campaign draft CRUD + default seasonal templates + generate-preview job (no mass send yet).
  • P0.8 Unsubscribe token endpoint + suppression list.
  • P0.9 Woo order sync (incremental) + woo_orders / items / customers tables.
  • P0.10 Audience preview: purchased X / not purchased Y + opt-in hard filters.
  • P0.11 Onboarding tutorial steps 14 wired to real completion signals; 56 gated.
  • P0.12 Safety tests: cannot send if unverified; cannot include unsubscribed; Free cannot generate campaign AI.

P1 — sellable Marketing MVP

  • P1.1 Campaign schedule/send batches via verified provider + rate limits + bounce basic handling.
  • P1.2 Custom vs default prompts UI; credit estimate + BYOK seam (if BYOK platform work ready).
  • P1.3 Woo product reviews sync + Reviews Marketing page.
  • P1.4 SEO recommendation runs (rule-based + paid AI rewrite).
  • P1.5 Brand kit injection into product enhance + campaign defaults.
  • P1.6 Cap enforcement (campaigns/mo, recipients/send).
  • P1.7 Tutorial steps 56 + “first Black Friday campaign” recipe.
  • P1.8 Ops: kill-switch, send metrics on billing overview.

P2 — expand

  • P2.1 Placeholder review providers (Judge.me / Yotpo) + webhooks.
  • P2.2 Open/click tracking; A/B subjects.
  • P2.3 Export audience / campaign to Klaviyo/Mailchimp.
  • P2.4 Site crawl SEO (beyond catalog).
  • P2.5 Public API for campaigns.
  • P2.6 Multi-language campaign variants from catalog locales.
  • P2.7 AI “infer brand kit from best products.”

12. Verification expectations (when built)

  • Unit: audience SQL compiler, feature gate matrix, unsubscribe token.
  • Integration: Woo orders pagination mocked; send blocked without verify.
  • UI: Free user sees locks; Starter can preview generate with credits.
  • No claim-done without AssertFeature coverage on every mutating Marketing route.

13. Rollback

  • Feature flags MARKETING_ENABLED (platform) + per-plan entitlements.
  • Schema additive only; drop via goose Down if needed.
  • Disabling flag hides nav and 404s APIs without deleting drafts.