Marketing

Campaigns, A/B testing & migration

Run high-volume email campaigns with A/B split testing, dynamic audience segmentation, AI content generation, and 1-click Brevo workspace migration.

Portal endpoints under /api/v1/* are authenticated with a portal session (Authorization: Bearer <jwt>). You can also manage all of these features visually in the portal under Campaigns, Segments and Events & Brevo Import.

A/B split testing for campaigns

Any email campaign can include an ab_test configuration. When the campaign starts sending:

  • SnappyMails takes a deterministic audience snapshot from your list_ids, segment_ids and exclude_segment_ids.
  • The initial test sample (sample_percent, between 10% and 50% of total recipients) is split evenly 50/50 between Variant A (the campaign's default subject, sender name and HTML) and Variant B (the overridden field).
  • Once the test sample finishes dispatching, the campaign enters phase: "WAITING_WINNER" until winner_pick_at (test_hours after sample completion).
  • When the evaluation timer elapses (or when you crown a winner manually), SnappyMails compares Variant A and Variant B by winner_criteria (open_rate or click_rate), records winner_variant and automatically dispatches the winning variant to the remaining 50%โ€“90% of your audience.
POST/api/v1/campaignsBearer JWT
ParameterTypeDescription
ab_test.enabledrequiredbooleanEnables A/B split testing on the campaign.
ab_test.test_typerequiredstringWhat to test: subject (Subject Line), from_name (Sender Name) or content (Email HTML Content).
ab_test.variant_b_subjectstringVariant B subject line (supports template tags).
ab_test.variant_b_from_namestringVariant B sender display name.
ab_test.variant_b_htmlstringVariant B HTML body.
ab_test.sample_percentintegerPercentage of total audience included in the initial A+B test pool (10 to 50, default 20).
ab_test.winner_criteriastringMetric used to pick the winning variant: open_rate (unique opens / delivered), click_rate (unique clicks / delivered), or manual (wait until a team member crowns a winner).
ab_test.test_hoursintegerHours to wait after the test sample finishes sending before automatically crowning the winner (1 to 168).
POST /api/v1/campaigns
{
  "name": "November Black Friday Preview",
  "subject": "Early access: 30% off everything inside",
  "from_name": "Acme Shop",
  "from_email": "news@acme.com",
  "reply_to": "support@acme.com",
  "html_content": "<h1>Early Access Starts Now</h1><p>Hi {{ contact.FIRSTNAME | default:\"there\" }}, shop first.</p><p><a href=\"{{ unsubscribe }}\">Unsubscribe</a></p>",
  "list_ids": [
    "7b1a2c3d-d5e6-4f7a-8b9c-0d1e2f3a4b5c"
  ],
  "segment_ids": [
    "2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d"
  ],
  "exclude_segment_ids": [],
  "ab_test": {
    "enabled": true,
    "test_type": "subject",
    "variant_b_subject": "{{ contact.FIRSTNAME | default:\"Friend\" }}, your private 30% VIP code is ready ๐ŸŽ",
    "sample_percent": 20,
    "winner_criteria": "open_rate",
    "test_hours": 4
  }
}

Live per-variant progress & report

GET/api/v1/campaigns/{id}/progressBearer JWT
GET/api/v1/campaigns/{id}/reportBearer JWT

Both the live progress endpoint and the full campaign report return a variants array with real-time metrics broken down by Variant A and Variant B:

200 OK
{
  "status": "SENDING",
  "total_recipients": 5000,
  "sent_count": 1000,
  "delivered_count": 994,
  "bounce_count": 6,
  "complaint_count": 0,
  "open_count": 312,
  "click_count": 84,
  "delivery_rate": 99.4,
  "speed_per_second": 45.2,
  "elapsed_seconds": 22,
  "ab_test": {
    "enabled": true,
    "test_type": "subject",
    "variant_b_subject": "{{ contact.FIRSTNAME | default:\"Friend\" }}, your private 30% VIP code is ready ๐ŸŽ",
    "sample_percent": 20,
    "winner_criteria": "open_rate",
    "test_hours": 4,
    "phase": "WAITING_WINNER",
    "test_completed_at": "2026-10-28T15:00:22Z",
    "winner_pick_at": "2026-10-28T19:00:22Z"
  },
  "variants": [
    {
      "variant": "A",
      "sent": 500,
      "delivered": 497,
      "unique_opens": 134,
      "unique_clicks": 36,
      "bounces": 3,
      "unsubscribes": 1,
      "open_rate": 0.2696,
      "click_rate": 0.0724
    },
    {
      "variant": "B",
      "sent": 500,
      "delivered": 497,
      "unique_opens": 178,
      "unique_clicks": 48,
      "bounces": 3,
      "unsubscribes": 0,
      "open_rate": 0.3581,
      "click_rate": 0.0966
    }
  ]
}

Manually crown an A/B winner

POST/api/v1/campaigns/{id}/ab-winnerBearer JWT

Selects "A" or "B" as the winner immediately and dispatches the remaining holdout recipients right away.

Request body
{
  "winner": "B"
}

Dynamic audience segments

Segments (/api/v1/segments*) evaluate nested all (AND) / any (OR) rule trees across contact attributes, list memberships and email engagement (opened, clicked, not_opened, never_opened, bounced, unsubscribed). Both email campaigns and WhatsApp broadcast campaigns accept segment_ids and exclude_segment_ids alongside list_ids.

MethodPathDescription
GET / POST/api/v1/segmentsList saved segments with cached counts or create a new segment.
POST/api/v1/segments/previewPreview matching contact count and sample contacts for unsaved rules.
POST/api/v1/segments/audienceCalculate deduplicated net recipient count across list_ids, segment_ids and exclude_segment_ids.

1-Click Brevo migration API

POST/api/v1/integrations/brevo/previewBearer JWT
POST/api/v1/integrations/brevo/importBearer JWT
GET/api/v1/integrations/brevo/runsBearer JWT

Connects to Brevo's v3 API using a Brevo API key (xkeysib-...) to inspect and import subscriber lists, contacts with all custom attributes, active HTML email templates, and blocklisted/unsubscribed addresses into your SnappyMails brand. Pass "api_key": "demo" to test the importer with built-in sample Brevo data.

POST /api/v1/integrations/brevo/import
{
  "api_key": "xkeysib-...",
  "import_lists": true,
  "import_contacts": true,
  "import_templates": true,
  "import_suppressions": true
}
200 OK
{
  "id": "8d7c6b5a-4e3f-2d1c-9b8a-7f6e5d4c3b2a",
  "status": "COMPLETED",
  "lists_imported": 3,
  "contacts_imported": 1420,
  "contacts_updated": 15,
  "suppressions_imported": 42,
  "templates_imported": 8,
  "summary": {
    "demo_mode": false
  },
  "created_at": "2026-10-28T15:20:00Z",
  "completed_at": "2026-10-28T15:20:04Z"
}

SnapAI writing assistant

Generate responsive email templates, subject lines, preheaders, body copy and WhatsApp templates using /api/v1/snapai/*:

MethodPathDescription
POST/api/v1/snapai/email-templateGenerate or revise an email-safe 600px HTML template, subject and preheader (supports SSE streaming).
POST/api/v1/snapai/subject-linesGenerate high-converting subject line and preheader ideas (great for A/B testing).
POST/api/v1/snapai/textWrite, rewrite, shorten, expand, translate or change tone of email copy.
POST/api/v1/snapai/whatsapp-templateGenerate Meta-compliant WhatsApp templates with variables and buttons.

Try it in the portal

Open New Campaign to use SnapAI subject line suggestions directly inside the A/B Split Testing wizard.