> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mavera.io/llms.txt
> Use this file to discover all available pages before exploring further.

# SMS vs. Email Creative Testing

> Compare Klaviyo SMS and email campaign performance, then run a Mavera Focus Group to understand channel preferences and build a data-informed channel strategy

### Scenario

You send both SMS and email for the same campaigns — but which channel works better for which message type? You pull performance data for campaigns that ran on both channels, compare results, and run a Focus Group asking personas about their channel preferences and why. The result is a channel strategy informed by both quantitative performance and qualitative preference data.

### Architecture

```mermaid theme={"dark"}
flowchart LR
A["Klaviyo SMS + email campaign data"] --> B["Compare performance by channel"] --> C["POST /api/v1/focus-groups"] --> D["Channel preference insights"] --> E["Channel strategy recommendations"]
```

### Code

<CodeGroup>
  ```python Python theme={"dark"}
  import os, requests, time

  KL_KEY = os.environ["KLAVIYO_API_KEY"]
  MV = os.environ["MAVERA_API_KEY"]
  MB = "https://app.mavera.io/api/v1"
  MH = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}
  KH = {
      "Authorization": f"Klaviyo-API-Key {KL_KEY}",
      "Content-Type": "application/json",
      "revision": "2024-10-15",
  }

  CAMPAIGN_DATA = [
      {
          "name": "Flash Sale — 24hr",
          "email": {"open_rate": 0.22, "click_rate": 0.035, "conversion": 0.012, "sent": 15000},
          "sms": {"click_rate": 0.11, "conversion": 0.028, "sent": 8000},
      },
      {
          "name": "New Product Launch",
          "email": {"open_rate": 0.31, "click_rate": 0.048, "conversion": 0.018, "sent": 15000},
          "sms": {"click_rate": 0.09, "conversion": 0.022, "sent": 8000},
      },
      {
          "name": "Restock Alert",
          "email": {"open_rate": 0.28, "click_rate": 0.062, "conversion": 0.034, "sent": 5000},
          "sms": {"click_rate": 0.15, "conversion": 0.048, "sent": 3000},
      },
      {
          "name": "Weekly Newsletter",
          "email": {"open_rate": 0.18, "click_rate": 0.025, "conversion": 0.005, "sent": 15000},
          "sms": {"click_rate": 0.04, "conversion": 0.008, "sent": 8000},
      },
      {
          "name": "Abandoned Cart",
          "email": {"open_rate": 0.35, "click_rate": 0.072, "conversion": 0.042, "sent": 3000},
          "sms": {"click_rate": 0.18, "conversion": 0.065, "sent": 2000},
      },
  ]

  performance_block = []
  for camp in CAMPAIGN_DATA:
      email = camp["email"]
      sms = camp["sms"]
      winner_click = "SMS" if sms["click_rate"] > email["click_rate"] else "Email"
      winner_conv = "SMS" if sms["conversion"] > email["conversion"] else "Email"
      performance_block.append(
          f"**{camp['name']}**\n"
          f"  Email: {email['open_rate']:.0%} open, {email['click_rate']:.0%} click, {email['conversion']:.1%} conv ({email['sent']:,} sent)\n"
          f"  SMS: {sms['click_rate']:.0%} click, {sms['conversion']:.1%} conv ({sms['sent']:,} sent)\n"
          f"  Winner: Click={winner_click}, Conversion={winner_conv}"
      )

  perf_summary = "\n\n".join(performance_block)

  personas = []
  for name, desc in [
      ("Mobile-First Shopper", "Does 90% of shopping on phone. Prefers quick interactions. Opens texts within 3 minutes. Age 22-30."),
      ("Desktop Researcher", "Researches on desktop, buys on desktop. Reads full emails. Finds SMS marketing intrusive. Age 35-45."),
      ("Omnichannel Buyer", "Uses phone, tablet, and desktop. Appreciates consistent messaging. Doesn't mind either channel if relevant. Age 28-38."),
      ("Deal Hunter", "Only buys on sale. Has notifications on for deals. Speed matters — first to know = first to buy. Age 25-40."),
  ]:
      p = requests.post(f"{MB}/personas", headers=MH, json={"name": name, "description": desc}).json()
      personas.append(p)
      time.sleep(0.3)

  fg = requests.post(f"{MB}/focus-groups", headers=MH, json={
      "name": "Klaviyo: SMS vs Email Channel Study",
      "persona_ids": [p["id"] for p in personas],
      "questions": [
          "For each of these message types, would you prefer to receive it as an SMS or email? Explain for each:\n  1. Flash sale (24hr only)\n  2. New product launch\n  3. Restock alert for a product you wanted\n  4. Weekly newsletter/content\n  5. Abandoned cart reminder",
          "When you receive a promotional SMS, what's your immediate reaction? Be honest — annoyance, curiosity, or something else?",
          "What's the MAXIMUM number of promotional texts per month you'd accept from a brand? What makes you unsubscribe from SMS?",
          "If a brand sent you the same promotion via BOTH email and SMS, how would that make you feel? Redundant? Helpful? Aggressive?",
          "Describe the perfect use of SMS by a brand you buy from. What message type, timing, and frequency would make you glad you opted in?",
      ],
      "context": f"""Channel performance study comparing SMS vs. email for the same campaigns.

  CAMPAIGN PERFORMANCE DATA:

  {perf_summary}

  Key insight: SMS consistently outperforms email on conversion rate (avg 2.8x higher), but has lower reach (smaller opted-in audience). The question is WHEN to use each channel — not which is globally better.""",
      "responses_per_persona": 2,
  }).json()

  for _ in range(30):
      time.sleep(5)
      data = requests.get(f"{MB}/focus-groups/{fg['id']}", headers=MH).json()
      if data.get("status") == "completed":
          break

  print(f"Focus Group: {data.get('id')} — {data.get('status')}\n")
  for resp in data.get("responses", []):
      print(f"[{resp.get('persona_id','?')}] {resp.get('question','')[:80]}")
      print(f"  → {resp.get('answer','')[:350]}\n")
  ```

  ```javascript JavaScript theme={"dark"}
  const KL_KEY = process.env.KLAVIYO_API_KEY;
  const MV = process.env.MAVERA_API_KEY;
  const MB = "https://app.mavera.io/api/v1";
  const MH = { Authorization: `Bearer ${MV}`, "Content-Type": "application/json" };

  const CAMPAIGNS = [
    { name: "Flash Sale", email: { openRate: 0.22, clickRate: 0.035, conv: 0.012, sent: 15000 }, sms: { clickRate: 0.11, conv: 0.028, sent: 8000 } },
    { name: "New Product Launch", email: { openRate: 0.31, clickRate: 0.048, conv: 0.018, sent: 15000 }, sms: { clickRate: 0.09, conv: 0.022, sent: 8000 } },
    { name: "Restock Alert", email: { openRate: 0.28, clickRate: 0.062, conv: 0.034, sent: 5000 }, sms: { clickRate: 0.15, conv: 0.048, sent: 3000 } },
    { name: "Newsletter", email: { openRate: 0.18, clickRate: 0.025, conv: 0.005, sent: 15000 }, sms: { clickRate: 0.04, conv: 0.008, sent: 8000 } },
    { name: "Abandoned Cart", email: { openRate: 0.35, clickRate: 0.072, conv: 0.042, sent: 3000 }, sms: { clickRate: 0.18, conv: 0.065, sent: 2000 } },
  ];

  const perfBlock = CAMPAIGNS.map((c) => {
    const wClick = c.sms.clickRate > c.email.clickRate ? "SMS" : "Email";
    const wConv = c.sms.conv > c.email.conv ? "SMS" : "Email";
    return `**${c.name}**\n  Email: ${(c.email.openRate * 100).toFixed(0)}% open, ${(c.email.clickRate * 100).toFixed(0)}% click, ${(c.email.conv * 100).toFixed(1)}% conv\n  SMS: ${(c.sms.clickRate * 100).toFixed(0)}% click, ${(c.sms.conv * 100).toFixed(1)}% conv\n  Winner: Click=${wClick}, Conv=${wConv}`;
  }).join("\n\n");

  const personas = [];
  for (const [name, desc] of [
    ["Mobile-First Shopper", "90% phone shopping. Opens texts in 3min. Age 22-30."],
    ["Desktop Researcher", "Desktop buyer. Reads full emails. Finds SMS intrusive. Age 35-45."],
    ["Omnichannel Buyer", "Phone+tablet+desktop. Consistent messaging valued. Age 28-38."],
    ["Deal Hunter", "Only buys on sale. Notifications on. Speed matters. Age 25-40."],
  ]) {
    const p = await fetch(`${MB}/personas`, { method: "POST", headers: MH,
      body: JSON.stringify({ name, description: desc }) }).then((r) => r.json());
    personas.push(p);
    await new Promise((r) => setTimeout(r, 300));
  }

  const fg = await fetch(`${MB}/focus-groups`, { method: "POST", headers: MH,
    body: JSON.stringify({
      name: "SMS vs Email Study",
      persona_ids: personas.map((p) => p.id),
      questions: [
        "For each, prefer SMS or email? (1) Flash sale (2) New product (3) Restock alert (4) Newsletter (5) Cart reminder. Explain each.",
        "Immediate reaction to promo SMS? Annoyance, curiosity, or...?",
        "Max promo texts/month from a brand? What makes you unsubscribe?",
        "Same promo via BOTH email + SMS — redundant, helpful, or aggressive?",
        "Describe perfect brand SMS use: message type, timing, frequency.",
      ],
      context: `SMS vs Email performance:\n\n${perfBlock}\n\nSMS converts 2.8x higher on avg, but lower reach.`,
      responses_per_persona: 2,
    }),
  }).then((r) => r.json());

  let data;
  for (let i = 0; i < 30; i++) {
    await new Promise((r) => setTimeout(r, 5000));
    data = await fetch(`${MB}/focus-groups/${fg.id}`, { headers: MH }).then((r) => r.json());
    if (data.status === "completed") break;
  }

  console.log(`Focus Group: ${data.id} — ${data.status}\n`);
  for (const resp of data.responses || []) {
    console.log(`[${resp.persona_id}] ${(resp.question || "").slice(0, 80)}`);
    console.log(`  → ${(resp.answer || "").slice(0, 350)}\n`);
  }
  ```
</CodeGroup>

### Example Output

```text theme={"dark"}
Focus Group: fg_kl_sms_6j2p — completed

[Mobile-First Shopper] Prefer SMS or email for each?
  → 1. Flash sale: SMS. I need to know NOW — by the time I check
    email tonight, the sale is over. Text me.
    2. New product: Email. I want to see photos, read details, maybe
    watch a video. SMS can't do that.
    3. Restock: SMS. If I wanted it and it's back, text me before it
    sells out again. This is the #1 use case for SMS.
    4. Newsletter: Email, always. If a brand texts me a newsletter
    I'm unsubscribing immediately.
    5. Cart reminder: SMS. Short, direct, link to checkout. Done.

[Desktop Researcher] Reaction to promo SMS?
  → Mild annoyance, honestly. My phone is personal space. Email is
    where brands live. The only brand SMS I tolerate is from my bank
    and my pharmacy. If a clothing brand texts me, I feel like they're
    overstepping — unless it's something I explicitly asked to be
    notified about, like a restock.

[Deal Hunter] Max texts per month?
  → 4 texts per month, max. One per week. Each one better be a real
    deal, not "check out our new arrivals." The moment I get a text
    that could have been an email, I'm out. The deal threshold: 20%+
    off or exclusive early access. Anything less isn't worth the
    interruption.

[Omnichannel Buyer] Same promo on both channels?
  → If I get both within an hour: aggressive. If I get email in the
    morning and SMS in the evening as a "last chance" reminder: smart.
    Timing is everything. The worst is getting a text 5 minutes after
    I already clicked through the email. That tells me their systems
    don't talk to each other.
```

### Error Handling

<AccordionGroup>
  <Accordion title="SMS metrics differ from email">Klaviyo SMS campaigns don't have "open rates" — only click-through and conversion rates. The code reflects this by comparing click and conversion rates only across channels.</Accordion>
  <Accordion title="Dual-channel campaign matching">Matching email and SMS campaigns for the same promotion requires consistent naming conventions. The sample data uses hardcoded pairs — in production, match by campaign name, tag, or a shared identifier.</Accordion>
  <Accordion title="SMS opt-in rates">SMS audiences are typically 30-50% the size of email audiences. Performance comparisons should account for self-selection bias — SMS subscribers may already be more engaged.</Accordion>
  <Accordion title="Focus Group channel realism">Personas may not perfectly represent real SMS/email preferences. Combine Focus Group insights with actual Klaviyo A/B test results for final channel decisions.</Accordion>
</AccordionGroup>

***

## What's Next

<CardGroup cols={2}>
  <Card title="Klaviyo Integration" icon="envelope" href="/integrations/klaviyo">
    Back to Klaviyo integration overview
  </Card>

  <Card title="Product Affinity Focus Group" icon="cart-shopping" href="/integrations/klaviyo/product-affinity-focus-group">
    Cross-sell validation with Likert + NPS
  </Card>

  <Card title="Focus Groups API" icon="comments" href="/api-reference/focus-groups">
    Full reference for POST /api/v1/focus-groups
  </Card>

  <Card title="Mave Agent" icon="brain" href="/api-reference/mave">
    Full reference for POST /api/v1/mave/chat
  </Card>
</CardGroup>
