Skip to Content
Content ToolkitMailchimp Integration

Mailchimp Integration

[To Build] · provider-mailchimp (new) · ConnectedChannel type addition

Connects a tenant’s Mailchimp account via OAuth and enables approved email newsletters to be pushed directly to Mailchimp as draft campaigns, ready to send to the tenant’s selected audience.

Related: Email Writer · Channels · Repurposing Pipeline · Content Toolkit Overview


Overview

FunctionPush approved email drafts to Mailchimp as campaigns; manage audience selection
TypeProvider + Channel Integration
StatusTo Build
PriorityP2 — Differentiating
Packagepackages/providers/mailchimp/ (new)
Credits0 cr (delivery action; no LLM inference)
PlanPro+

Why This Is Needed

The email-writer agent produces complete email newsletters — subject line, preview text, structured body sections, CTA. But there is no delivery path: the content is reviewed and approved inside Leadmetrics and then must be manually copied into Mailchimp. Connecting directly to Mailchimp closes the loop, making the email workflow end-to-end: generate → review → approve → push to Mailchimp draft → tenant sends.

The existing provider-sendgrid package handles transactional delivery (system emails). Mailchimp targets the marketing campaign use case — mass audience sends — which is a different API and a different workflow.


OAuth Connect Flow

Mailchimp uses OAuth 2.0. The connect flow follows the same pattern as existing channels (Google, LinkedIn, Meta).

Tenant navigates to Settings → Channels → Connect Mailchimp Redirect to Mailchimp OAuth: https://login.mailchimp.com/oauth2/authorize Scopes: N/A — Mailchimp OAuth grants full account access by default Mailchimp redirects back to: /api/pg/v1/mailchimp/callback Exchange code for access token (no expiry on Mailchimp tokens) Fetch datacenter prefix: GET https://login.mailchimp.com/oauth2/metadata Returns: { dc: "us14" } — prefix used in all API calls Create ConnectedChannel record: { tenantId, channelType: "Mailchimp", isConnected: true, tokenInfo: encrypt({ accessToken, dc }), accountName: metadata.login.login_email, } Dashboard Channels page shows Mailchimp as connected

provider-mailchimp Package

Location: packages/providers/mailchimp/

Public API:

interface MailchimpProvider { // Audience management getAudiences(accessToken: string, dc: string): Promise<MailchimpAudience[]>; getAudienceSegments(accessToken: string, dc: string, listId: string): Promise<MailchimpSegment[]>; // Campaign management createDraftCampaign( accessToken: string, dc: string, params: CreateCampaignParams ): Promise<{ campaignId: string; webId: number; archiveUrl: string }>; updateCampaignContent( accessToken: string, dc: string, campaignId: string, html: string ): Promise<void>; } interface CreateCampaignParams { listId: string; // Mailchimp audience ID segmentId?: number; // Optional segment within the audience subjectLine: string; previewText: string; fromName: string; // e.g. "Acme Marketing" replyTo: string; // reply-to email address title: string; // internal campaign name in Mailchimp } interface MailchimpAudience { id: string; name: string; memberCount: number; } interface MailchimpSegment { id: number; name: string; memberCount: number; }

Push to Mailchimp Workflow

When an EmailActivity transitions to client_approved:

EmailActivity.status → "client_approved" Check: does this tenant have a ConnectedChannel of type "Mailchimp"? No → Skip; no error; email remains in "Approved" state in dashboard Yes → Check: does the EmailActivity have a selectedAudienceId? No → Email remains in dashboard; show "Select audience to push" prompt Yes → Enqueue mailchimp-publisher job: { tenantId, emailActivityId, connectedChannelId, audienceId, segmentId? } mailchimp-publisher worker: 1. Decrypt token from ConnectedChannel.tokenInfo 2. Convert EmailActivity.bodyMarkdown to HTML (marked.js) 3. POST /campaigns → create draft campaign with subject + preview text 4. PUT /campaigns/{id}/content → set HTML body 5. Store mailchimpCampaignId + mailchimpCampaignUrl on EmailActivity 6. Emit WebSocket event: email:pushed_to_mailchimp Dashboard email detail: "Pushed to Mailchimp" badge + link to Mailchimp campaign editor

Audience Selector

When a DM or tenant creates an EmailActivity (or a repurposed email activity is created), they can select which Mailchimp audience and optional segment to target:

UI flow:

  1. Email activity creation form includes a “Mailchimp Audience” field (shown only if Mailchimp is connected)
  2. Field fetches audiences via GET /tenant/v1/channels/mailchimp/audiences
  3. Optional: segment picker rendered after audience selection via GET /tenant/v1/channels/mailchimp/audiences/:listId/segments
  4. selectedAudienceId and optional selectedSegmentId stored on the EmailActivity record

DB Changes

Add to the EmailActivity model (or equivalent email content model):

mailchimpCampaignId String? mailchimpCampaignUrl String? mailchimpPushedAt DateTime? selectedAudienceId String? selectedSegmentId Int?

New API Routes

MethodPathDescription
GET/tenant/v1/channels/mailchimp/audiencesList Mailchimp audiences for the connected account
GET/tenant/v1/channels/mailchimp/audiences/:listId/segmentsList segments within a specific audience
POST/tenant/v1/email/:id/push-to-mailchimpManually trigger push (if auto-push did not fire)
GET/api/pg/v1/mailchimp/callbackOAuth callback handler

Key Design Decisions

DecisionChoiceRationale
Push as draft, not sendCampaign is created as a draft in MailchimpLeadmetrics is the content platform; the tenant controls when to actually send from Mailchimp
No scheduled sendMailchimp draft only; tenant schedules from Mailchimp UISimplifies v1; avoids replicating Mailchimp’s send scheduler
Token does not expireMailchimp tokens are non-expiring; no refresh neededSimplifies token management; still encrypted at rest
Markdown → HTML in workerConvert using marked.js in the workerKeeps the email-writer output clean (Markdown); HTML conversion is a delivery concern
One Mailchimp account per tenantOnly one ConnectedChannel of type “Mailchimp” per tenantAgencies managing multiple clients each on their own Mailchimp account will need a separate tenant per client — consistent with the multi-tenant model

Implementation Phases

Phase 1 — OAuth Connect

  1. Create packages/providers/mailchimp/ package
  2. Implement getAudiences(), createDraftCampaign(), updateCampaignContent() methods
  3. Add Mailchimp to ChannelMaster in the database seed
  4. Add /api/pg/v1/mailchimp/callback OAuth callback route
  5. Channels settings page: “Connect Mailchimp” button

Phase 2 — Push Worker

  1. Create apps/api/src/workers/mailchimp-publisher.worker.ts
  2. Add mailchimpCampaignId, mailchimpCampaignUrl, mailchimpPushedAt to email model (migration)
  3. Hook client_approved status transition to auto-enqueue mailchimp-publisher if audience is selected
  4. Emit email:pushed_to_mailchimp WebSocket event
  5. Dashboard email detail: Mailchimp status badge + campaign link

Phase 3 — Audience Selector

  1. Add selectedAudienceId, selectedSegmentId to email model (migration)
  2. Add GET /tenant/v1/channels/mailchimp/audiences and segments routes
  3. Email activity creation form: audience + segment picker (shown when Mailchimp connected)
  4. “Push to Mailchimp” manual action button in email detail page

© 2026 Leadmetrics — Internal use only