Skip to Content
ProvidersMailchimp

Mailchimp

Category: Email Marketing
Integration type: Tenant OAuth / API key (stored in integrations table)
External SDK: @mailchimp/mailchimp_marketing


Purpose

Mailchimp integration allows the Email Writer agent to push generated email newsletter copy directly into the tenant’s Mailchimp account as a draft campaign. The agent creates the campaign structure; a human always reviews and sends it from within Mailchimp.

The platform also reads Mailchimp audience lists to provide the agent with subscriber count and segment context when writing campaign copy.


Config Structure

Tenant integration (API key or OAuth)

Mailchimp supports both a legacy API key and OAuth. The platform supports both:

interface MailchimpConfig { apiKey: string; // Mailchimp API key (format: key-dc, e.g. abc123-us21) serverPrefix: string; // Data center prefix extracted from API key (e.g. "us21") listId?: string; // Default audience list ID — can be overridden per campaign }

The server prefix is the part after the hyphen in the API key. The API base URL is https://<serverPrefix>.api.mailchimp.com/3.0.


Integration Pattern

Tool layer (packages/tools/src/mailchimp.ts)

import mailchimp from '@mailchimp/mailchimp_marketing'; class MailchimpTool { constructor(private cfg: MailchimpConfig) { mailchimp.setConfig({ apiKey: cfg.apiKey, server: cfg.serverPrefix, }); } async createDraftCampaign(options: { listId: string; // Mailchimp audience list ID subject: string; previewText: string; fromName: string; replyTo: string; htmlContent: string; // Full email HTML body }): Promise<{ campaignId: string; webId: number; editUrl: string }> { // Step 1: Create the campaign shell const campaign = await mailchimp.campaigns.create({ type: 'regular', recipients: { list_id: options.listId, }, settings: { subject_line: options.subject, preview_text: options.previewText, from_name: options.fromName, reply_to: options.replyTo, title: `LM Draft - ${options.subject} - ${new Date().toISOString().slice(0, 10)}`, }, }); // Step 2: Set the content await mailchimp.campaigns.setContent(campaign.id, { html: options.htmlContent, }); return { campaignId: campaign.id, webId: campaign.web_id, editUrl: `https://us${campaign.web_id}.admin.mailchimp.com/campaigns/edit?id=${campaign.web_id}`, }; } async getAudiences(): Promise<{ id: string; name: string; memberCount: number }[]> { const response = await mailchimp.lists.getAllLists({ count: 50 }); return response.lists.map((list: any) => ({ id: list.id, name: list.name, memberCount: list.stats.member_count, })); } async verify(): Promise<void> { const ping = await mailchimp.ping.get(); if (ping.health_status !== 'Everything\'s Chimpy!') { throw new Error('Mailchimp ping failed'); } } }

Email Writer → Mailchimp workflow

Email Writer agent generates HTML newsletter body ▼ (HITL approval) Email Publisher worker ├── Fetch generated HTML from MongoDB ├── Resolve tenant's Mailchimp integration ├── Get default audience list ID from integration metadata ├── Create draft campaign in Mailchimp └── Store campaignId + editUrl in email_campaigns table DM Portal shows "View in Mailchimp" link for final send

Merge Tags

Mailchimp supports *|FNAME|* merge tags for personalization. The Email Writer agent’s system prompt instructs it to use Mailchimp merge tag syntax where appropriate:

Always use Mailchimp merge tags for personalization: - *|FNAME|* for first name - *|EMAIL|* for email address - *|CURRENT_YEAR|* for current year

Test Cases

Unit tests (packages/tools/src/mailchimp.test.ts)

TestApproach
createDraftCampaign() creates campaign then sets contentMock SDK; assert campaigns.create then campaigns.setContent called in order
createDraftCampaign() returns editUrl with correct web IDMock { id: 'abc', web_id: 12345 }; assert URL
getAudiences() maps list data to typed objectsMock lists.getAllLists; assert memberCount from stats.member_count
verify() throws when ping returns unexpected statusMock { health_status: 'Degraded' }; assert throws
Throws on 401 (invalid API key)Mock SDK throws 401; assert propagated

© 2026 Leadmetrics — Internal use only