# How to Integrate Bolt.new with Campaign Monitor

- Tool: Bolt.new
- Difficulty: Intermediate
- Time required: 20 minutes
- Last updated: April 2026

## TL;DR

Campaign Monitor integrates with Bolt.new through a Next.js API route using HTTP Basic authentication with your API key. Get the key from Account Settings → API keys, store it in .env, and call Campaign Monitor's REST API to manage subscriber lists, add subscribers, create email campaigns, and fetch analytics. Campaign Monitor focuses on branded email design and has a generous free-to-test API with no minimum plan requirement.

## Campaign Monitor Email Marketing Integration for Bolt.new Apps

Campaign Monitor has built its reputation on design quality — the platform is used by agencies and design-conscious brands that prioritize beautiful, on-brand email templates over raw automation complexity. While Mailchimp has more automation features and a better free tier, Campaign Monitor excels at producing polished, responsive email campaigns that match a brand's visual identity precisely. The platform's template builder, which supports both drag-and-drop and custom HTML/CSS imports, gives creative teams more control over email design than most competitors.

For Bolt.new developers, Campaign Monitor integration typically comes in two forms. The first is subscriber capture — adding subscribers from a Bolt-built website or landing page directly to a Campaign Monitor list, triggering welcome sequences and nurture campaigns. The second is building internal tools: a campaign analytics dashboard for teams reviewing email performance, or a subscriber management interface for content teams who manage lists without needing full Campaign Monitor account access.

Campaign Monitor's API uses HTTP Basic auth with the API key as the username — an authentication pattern that requires no OAuth flow, no redirect URIs, and no token expiration management. This makes it one of the simpler email marketing APIs to integrate. The API key gives full access to all clients and campaigns associated with your Campaign Monitor account, so keeping it server-side (never prefixed with NEXT_PUBLIC_) is essential. The API covers all core functionality: subscriber lists, subscriber management (add, update, delete, import), campaigns (create, send, schedule), templates, and detailed analytics reports.

## Before you start

- A Campaign Monitor account (campaignmonitor.com) — a free account allows API testing; paid plans required for sending campaigns to real subscribers
- Your Campaign Monitor API key from Account Settings (top-right avatar) → API Keys — you may need to generate one if none exists
- Your Campaign Monitor Client ID (found in Account Settings → Clients, or in the URL when viewing a client) — required for campaign management endpoints
- A Campaign Monitor subscriber list ID — found in the list settings URL when viewing a list in Campaign Monitor
- A Bolt.new project using Next.js for API routes that keep the API key server-side

## Step-by-step guide

### 1. Get your Campaign Monitor API key and configure authentication

Campaign Monitor uses HTTP Basic authentication with the API key as the username and any non-empty string as the password — the convention is to use 'x' as the password, but Campaign Monitor accepts any value. The authentication is encoded as Base64 of 'API_KEY:x' and sent in the Authorization header.

To get your API key, log into Campaign Monitor and click your account name or avatar in the top-right corner. Select 'Account Settings' from the dropdown. Navigate to the 'API keys' section. If no API key exists, click 'Generate API key.' Copy the key — it is a 40-character hexadecimal string.

In Node.js, construct the auth header as: Buffer.from(`${apiKey}:x`).toString('base64'). The full Authorization header is: Authorization: Basic {base64string}. This pattern is simpler than Insightly's API (which uses an empty password) because Campaign Monitor explicitly accepts 'x' as the placeholder password.

You also need your Campaign Monitor Client ID for endpoints that manage campaigns. Find it in Account Settings → Clients, or look at the URL when you are viewing a client in the Campaign Monitor UI — the numeric-looking ID in the URL path is the client ID. This is different from your API key and specific to the client (brand) you are managing.

For subscriber list management, each list has its own ID. Find it in Campaign Monitor by navigating to a list and looking at the URL, or via the API: GET /clients/{clientId}/lists.json returns all lists with their IDs and names.

Store the API key as CAMPAIGN_MONITOR_API_KEY in .env (server-side, no NEXT_PUBLIC_ prefix). Store the client ID as CAMPAIGN_MONITOR_CLIENT_ID and your primary list ID as CAMPAIGN_MONITOR_LIST_ID. All three are needed for the common operations covered in this guide.

```
# .env
# Campaign Monitor API key — server-side only, never NEXT_PUBLIC_
# Get from Campaign Monitor: account avatar → Account Settings → API keys
CAMPAIGN_MONITOR_API_KEY=your_40_char_hex_key_here

# Campaign Monitor Client ID — numeric-looking string for the brand/client
# Find in Account Settings → Clients, or in the URL when viewing a client
CAMPAIGN_MONITOR_CLIENT_ID=your_client_id_here

# Subscriber list ID for signup forms
# Find in Campaign Monitor → Lists → click a list → copy ID from URL
CAMPAIGN_MONITOR_LIST_ID=your_list_id_here
```

**Expected result:** The Campaign Monitor API key is stored in .env. You can test it by making a GET request to https://api.createsend.com/api/v3.3/clients/{YOUR_CLIENT_ID}/lists.json with the Basic auth header — it should return your subscriber lists as JSON.

### 2. Build the Campaign Monitor API service and subscriber routes

Create the Campaign Monitor API service module and Next.js API routes for subscriber management. Campaign Monitor's API base URL is https://api.createsend.com/api/v3.3/ for the current version. All endpoints follow RESTful conventions with JSON responses.

The primary subscriber endpoints are: POST /subscribers/{listId}.json (add or re-subscribe a subscriber — specify Resubscribe: true to re-add previously unsubscribed contacts), GET /subscribers/{listId}.json?email={email} (find a specific subscriber by email), PUT /subscribers/{listId}.json (update a subscriber's details), POST /subscribers/{listId}/unsubscribe.json (unsubscribe a contact), and GET /lists/{listId}/active.json (paginated list of active subscribers).

When adding a subscriber, the required fields are EmailAddress and Name. Optional fields include Resubscribe (boolean — whether to re-add unsubscribed contacts), RestartSubscriptionBasedAutoresponders (boolean — whether to trigger welcome automation), and CustomFields (array of {Key, Value} objects for custom subscriber data you have defined in the list).

Campaign Monitor returns HTTP 200 for successful subscriber operations regardless of whether the subscriber was created or updated — there is no 201 Created vs 200 OK distinction. A 400 response indicates validation errors with a descriptive error Code and Message in the response body. Common 400 codes: 1 (invalid email address), 203 (list is at capacity), 210 (email in suppression list).

Build the service module with typed helper functions and verify the subscriber endpoint in Bolt's preview. Outbound calls to api.createsend.com work in the WebContainer since they use standard HTTPS.

```
// lib/campaign-monitor.ts
const CM_BASE = 'https://api.createsend.com/api/v3.3';

function getCMAuth(): string {
  const key = process.env.CAMPAIGN_MONITOR_API_KEY;
  if (!key) throw new Error('CAMPAIGN_MONITOR_API_KEY not configured');
  // Campaign Monitor Basic auth: Base64 of 'api_key:x'
  return Buffer.from(`${key}:x`).toString('base64');
}

async function cmFetch<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
  const res = await fetch(`${CM_BASE}${endpoint}`, {
    ...options,
    headers: {
      Authorization: `Basic ${getCMAuth()}`,
      'Content-Type': 'application/json',
      ...options.headers,
    },
  });

  if (!res.ok) {
    const body = await res.json().catch(() => ({ Message: res.statusText }));
    throw Object.assign(new Error(body.Message ?? 'Campaign Monitor API error'), {
      status: res.status,
      code: body.Code,
      body,
    });
  }

  return res.json();
}

export interface CMCustomField {
  Key: string;
  Value: string;
}

export async function addSubscriber(
  listId: string,
  email: string,
  name: string,
  customFields: CMCustomField[] = [],
  resubscribe = true
) {
  return cmFetch<string>(`/subscribers/${listId}.json`, {
    method: 'POST',
    body: JSON.stringify({
      EmailAddress: email,
      Name: name,
      CustomFields: customFields,
      Resubscribe: resubscribe,
      RestartSubscriptionBasedAutoresponders: resubscribe,
    }),
  });
}

export async function getLists(clientId?: string) {
  const id = clientId ?? process.env.CAMPAIGN_MONITOR_CLIENT_ID;
  if (!id) throw new Error('CAMPAIGN_MONITOR_CLIENT_ID not configured');
  return cmFetch<Array<{ ListID: string; Name: string }>>(`/clients/${id}/lists.json`);
}

export async function getActiveSubscribers(listId: string, page = 1, pageSize = 100) {
  return cmFetch(`/lists/${listId}/active.json?page=${page}&pagesize=${pageSize}`);
}

export async function getCampaigns(clientId?: string) {
  const id = clientId ?? process.env.CAMPAIGN_MONITOR_CLIENT_ID;
  if (!id) throw new Error('CAMPAIGN_MONITOR_CLIENT_ID not configured');
  return cmFetch<Array<{ CampaignID: string; Name: string; Subject: string; SentDate: string }>(
    `/clients/${id}/campaigns.json`
  );
}
```

**Expected result:** lib/campaign-monitor.ts provides authenticated access to Campaign Monitor's API. The /api/campaign-monitor/subscribe route adds subscribers to the configured list. Submitting the signup form in Bolt's preview adds a real subscriber to your Campaign Monitor list.

### 3. Build the campaign analytics dashboard

A campaign analytics dashboard that surfaces Campaign Monitor email performance metrics for teams reviewing results without needing direct Campaign Monitor access. Campaign Monitor provides detailed analytics through its reporting API endpoints.

Fetch campaigns via GET /clients/{clientId}/campaigns.json, which returns sent campaigns with basic metadata: CampaignID, Name, Subject, SentDate, TotalRecipients, and a WebVersionURL. For each campaign, fetch detailed stats via GET /campaigns/{campaignId}/summary.json, which returns comprehensive analytics: Bounces, Clicks, Opens, Recipients, SpamComplaints, TotalOpened, UniqueOpened, Unsubscribes, WebVersionURL, and WorldviewURL.

The most important metrics for the dashboard are: unique open rate (UniqueOpened / Recipients), click-to-open rate (Clicks / UniqueOpened), and unsubscribe rate (Unsubscribes / Recipients). Campaign Monitor returns absolute numbers rather than percentages — calculate rates in your API route or React component.

For the Recharts visualization, a horizontal bar chart comparing open rates across recent campaigns works well — horizontal bars accommodate longer campaign names better than vertical bars. Color-code bars above 25% as green (above average for email), 15-24% as yellow (average), and below 15% as red (below average). These benchmarks vary by industry but provide useful relative context.

For click tracking data, Campaign Monitor also provides link performance through GET /campaigns/{campaignId}/clicks.json, which shows each link clicked and the unique/total click counts. This is useful for identifying which content resonates with subscribers and can be displayed in a detail view for individual campaigns.

```
// app/api/campaign-monitor/campaigns/[id]/summary/route.ts
import { NextResponse } from 'next/server';

export async function GET(
  _request: Request,
  { params }: { params: { id: string } }
) {
  const apiKey = process.env.CAMPAIGN_MONITOR_API_KEY;
  if (!apiKey) {
    return NextResponse.json({ error: 'CAMPAIGN_MONITOR_API_KEY not configured' }, { status: 500 });
  }

  const auth = Buffer.from(`${apiKey}:x`).toString('base64');
  const res = await fetch(
    `https://api.createsend.com/api/v3.3/campaigns/${params.id}/summary.json`,
    { headers: { Authorization: `Basic ${auth}` } }
  );

  if (!res.ok) {
    return NextResponse.json({ error: 'Failed to fetch campaign summary' }, { status: res.status });
  }

  const data = await res.json();
  const recipients = data.Recipients ?? 1; // avoid division by zero

  return NextResponse.json({
    campaignId: params.id,
    recipients: data.Recipients,
    uniqueOpened: data.UniqueOpened,
    totalOpened: data.TotalOpened,
    clicks: data.Clicks,
    bounces: data.Bounces,
    unsubscribes: data.Unsubscribes,
    spamComplaints: data.SpamComplaints,
    // Calculated rates as percentages
    uniqueOpenRate: ((data.UniqueOpened / recipients) * 100).toFixed(1),
    clickRate: ((data.Clicks / recipients) * 100).toFixed(1),
    bounceRate: ((data.Bounces / recipients) * 100).toFixed(1),
    clickToOpenRate: data.UniqueOpened > 0
      ? ((data.Clicks / data.UniqueOpened) * 100).toFixed(1)
      : '0.0',
  });
}
```

**Expected result:** The campaign analytics dashboard lists recent Campaign Monitor campaigns with open and click rates. The horizontal bar chart provides a visual comparison across campaigns with color-coded performance indicators. Clicking a campaign shows the detail panel with all metrics.

### 4. Add subscriber list management and segmentation

Complete the integration with subscriber list management features: viewing active subscribers, searching for specific subscribers, updating custom field data, and managing unsubscribes. This is useful for content teams who manage subscriber lists without needing full Campaign Monitor account access.

Campaign Monitor's subscriber list endpoints return paginated results. The active subscribers endpoint (GET /lists/{listId}/active.json) supports pagination with page and pagesize parameters, returning results with a TotalNumberOfRecords field and a Results array. The maximum page size is 1000 subscribers per request.

Subscriber records include EmailAddress, Name, Date (subscription date), State (Active, Unsubscribed, Bounced, Deleted), and CustomFields (an array of {Key, Value} objects). Custom fields you define in the Campaign Monitor list settings appear in every subscriber record.

For unsubscribing a contact: POST /subscribers/{listId}/unsubscribe.json with { 'EmailAddress': email }. For updating subscriber details: PUT /subscribers/{listId}.json with the same payload format as add subscriber (the API uses the email to find the existing record and update it).

Building a subscriber search UI requires fetching by email — Campaign Monitor does not have a global search endpoint, so you search within a specific list using GET /subscribers/{listId}.json?email={email}. For multi-list search, make parallel requests to each list and aggregate results.

```
// app/api/campaign-monitor/subscribers/route.ts
import { NextResponse } from 'next/server';
import { getActiveSubscribers } from '@/lib/campaign-monitor';

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const listId = searchParams.get('listId') ?? process.env.CAMPAIGN_MONITOR_LIST_ID;
  const email = searchParams.get('email');
  const page = parseInt(searchParams.get('page') ?? '1');

  if (!listId) {
    return NextResponse.json({ error: 'listId required' }, { status: 400 });
  }

  const apiKey = process.env.CAMPAIGN_MONITOR_API_KEY!;
  const auth = Buffer.from(`${apiKey}:x`).toString('base64');

  try {
    if (email) {
      // Search for specific subscriber by email
      const res = await fetch(
        `https://api.createsend.com/api/v3.3/subscribers/${listId}.json?email=${encodeURIComponent(email)}`,
        { headers: { Authorization: `Basic ${auth}` } }
      );
      if (!res.ok) return NextResponse.json({ subscriber: null });
      return NextResponse.json({ subscriber: await res.json() });
    }

    // Get paginated active subscribers
    const data = await getActiveSubscribers(listId, page);
    return NextResponse.json(data);
  } catch (error) {
    const message = error instanceof Error ? error.message : 'Unknown error';
    return NextResponse.json({ error: message }, { status: 500 });
  }
}
```

**Expected result:** The subscriber management interface shows a paginated list of active subscribers with search capability. The unsubscribe action removes contacts from the list and the UI updates optimistically. List health metrics show the breakdown of active, bounced, and unsubscribed contacts.

## Best practices

- Store the Campaign Monitor API key in server-side .env with no NEXT_PUBLIC_ prefix — it provides full access to all subscriber lists and campaign management across your account
- Use Resubscribe: true on subscriber addition to re-add unsubscribed contacts who opt in again — without this, re-adding an unsubscribed email silently fails and the contact remains unsubscribed
- Always use PascalCase for Campaign Monitor API field names (EmailAddress, Name, CustomFields) — lowercase alternatives may be accepted in some cases but PascalCase is the documented standard and more reliable
- Fetch campaign summaries for the campaigns list view lazily or cache them — fetching detailed stats for every campaign on page load creates N+1 API requests for accounts with many campaigns
- Use Campaign Monitor's CustomFields feature to capture additional subscriber data (source, lead magnet, UTM parameters) by adding them to the list definition in Campaign Monitor before sending them via API
- Implement the Resubscribe and RestartSubscriptionBasedAutoresponders flags consistently — setting them to true ensures returning subscribers receive welcome emails and re-enter automation sequences
- Test your subscriber signup flow with real email addresses on the deployed Bolt app — the Bolt WebContainer preview works for outbound API calls to Campaign Monitor, but email delivery testing requires real subscriber data

## Use cases

### Subscriber Capture from Bolt Landing Page

A newsletter or lead magnet signup form on a Bolt-built landing page that adds new subscribers to a Campaign Monitor list when submitted. The subscriber receives an immediate branded welcome email configured in Campaign Monitor, while the marketing team sees the new subscriber in their Campaign Monitor dashboard.

Prompt example:

```
Add a newsletter signup form to this landing page that subscribes users to Campaign Monitor. Create a form component with name and email fields. On submit, POST to /api/campaign-monitor/subscribe which calls https://api.createsend.com/api/v3.3/subscribers/{CAMPAIGN_MONITOR_LIST_ID}.json using Basic auth with CAMPAIGN_MONITOR_API_KEY. Set the subscriber's Name and EmailAddress fields. Use Resubscribe: true to re-add unsubscribed contacts. Show a success message on submission. Handle duplicate emails gracefully (Campaign Monitor returns 200 for existing subscribers when Resubscribe is true). Use TypeScript and Tailwind CSS.
```

### Campaign Analytics Dashboard

An internal campaign performance dashboard showing Campaign Monitor email analytics — opens, clicks, bounces, and unsubscribes for recent campaigns. Design leads and account managers can review email performance in a custom interface alongside other project metrics without needing individual Campaign Monitor logins.

Prompt example:

```
Build a Campaign Monitor campaign analytics dashboard. Create /api/campaign-monitor/campaigns that GETs https://api.createsend.com/api/v3.3/clients/{CAMPAIGN_MONITOR_CLIENT_ID}/campaigns.json using CAMPAIGN_MONITOR_API_KEY Basic auth. Create /api/campaign-monitor/campaigns/[id]/summary that GETs /campaigns/{campaignId}/summary.json for open rate, click rate, bounces, unsubscribes. In React, display a list of recent campaigns with name, sent date, recipient count, open rate, and click rate. Add a Recharts BarChart comparing open vs click rates for the last 10 campaigns. Clicking a campaign shows a detail panel with the full summary stats.
```

### Subscriber List Management Interface

A subscriber management tool that allows content team members to view, search, and segment subscribers across Campaign Monitor lists without needing full account access. Team members can check if an email is subscribed, remove specific subscribers, and view list growth metrics — all from a custom Bolt interface.

Prompt example:

```
Create a Campaign Monitor subscriber management interface. Build /api/campaign-monitor/subscribers that accepts a GET with optional email query param and searches for the subscriber across all lists. Build /api/campaign-monitor/subscribers/[listId] that returns active subscribers for that list with pagination. Create a React interface with a search input to look up subscribers by email — call GET /subscribers/{listId}.json?email={email} for each list using CAMPAIGN_MONITOR_API_KEY. Show subscriber status, date subscribed, and custom fields. Add an 'Unsubscribe' button that POSTs to the unsubscribe endpoint. Display list growth chart using Recharts.
```

## Troubleshooting

### Campaign Monitor API returns 401 Unauthorized on all requests

Cause: The Basic auth header is incorrectly formatted. Campaign Monitor requires Base64 encoding of 'API_KEY:x' (key followed by colon and the letter x). Using just the key, or encoding 'API_KEY:' with an empty password, may also cause auth failures.

Solution: Verify the auth construction: Buffer.from(`${apiKey}:x`).toString('base64'). The full Authorization header is 'Basic ' followed by the Base64 string. Confirm the API key in .env is exactly 40 characters and has no leading or trailing whitespace. Test by pasting your Base64-encoded credentials into a Base64 decoder to verify the 'key:x' format.

```
// Correct Campaign Monitor Basic auth
const apiKey = process.env.CAMPAIGN_MONITOR_API_KEY;
// CORRECT: encode 'apikey:x' (x is placeholder password)
const auth = Buffer.from(`${apiKey}:x`).toString('base64');
const headers = { Authorization: `Basic ${auth}` };
```

### POST to /subscribers returns 400 with Code 1 (Invalid email address)

Cause: The email address in the EmailAddress field failed Campaign Monitor's email validation. This can happen with test email addresses, email addresses with unusual formatting, or if the field name is incorrectly cased.

Solution: Ensure the JSON payload uses PascalCase field names: { EmailAddress: email, Name: name }. Lowercase 'emailAddress' or 'email' will be silently ignored and Campaign Monitor will report a missing required field. Validate the email format client-side before submission to catch obvious formatting errors.

```
// Correct Campaign Monitor subscriber payload (PascalCase fields)
const payload = {
  EmailAddress: email,      // NOT: emailAddress or email
  Name: name,               // NOT: name (lowercase works here but PascalCase is safer)
  Resubscribe: true,
  RestartSubscriptionBasedAutoresponders: true,
  CustomFields: [],
};
```

### Campaign Monitor subscriber count shows in dashboard but analytics returns empty campaigns array

Cause: The CAMPAIGN_MONITOR_CLIENT_ID is incorrect or belongs to a different account than the list ID. Campaign Monitor's campaign endpoints are scoped to clients (brands), not accounts.

Solution: Verify your Client ID by calling GET /clients/{clientId}/details.json with your API key — if it returns client details, the ID is correct. Find your Client ID in Campaign Monitor's URL when viewing a client, or use GET /clients.json (requires account-level API key) to list all clients. The campaign analytics endpoints require the Client ID, not the list ID.

## Frequently asked questions

### Does Campaign Monitor work with Bolt.new?

Yes. Campaign Monitor's REST API uses standard HTTPS requests that work in Bolt's WebContainer via a Next.js API route. Campaign Monitor uses HTTP Basic auth with an API key — no OAuth flow or redirect URI is required. You can test the complete integration (subscribing contacts, fetching campaigns) directly in the Bolt preview without deploying first.

### How do I connect Bolt.new to Campaign Monitor?

Get your API key from Campaign Monitor Account Settings → API keys. Store it in .env as CAMPAIGN_MONITOR_API_KEY. Create a Next.js API route that constructs Basic auth by encoding 'API_KEY:x' as Base64 and adds it as an Authorization header to requests to https://api.createsend.com/api/v3.3. React components call your API routes — never Campaign Monitor's API directly — to keep the key server-side.

### What is the Campaign Monitor API key format and where do I find it?

The Campaign Monitor API key is a 40-character hexadecimal string. Find it in Campaign Monitor by clicking your account name or avatar in the top-right corner, selecting 'Account Settings,' and navigating to the 'API keys' section. If no key exists, click 'Generate API key.' The key provides full API access to all subscriber lists and campaigns in your account.

### How do I add subscribers to Campaign Monitor from a Bolt.new form?

Create a Next.js API route that POSTs to https://api.createsend.com/api/v3.3/subscribers/{listId}.json with Basic auth. The request body must use PascalCase fields: { EmailAddress: email, Name: name, Resubscribe: true }. The Resubscribe flag ensures previously unsubscribed contacts can be re-added if they opt in again. React form components call your API route — never Campaign Monitor directly.

### Can I use Campaign Monitor in Bolt's WebContainer preview without deploying?

Yes. Campaign Monitor uses API key authentication with no OAuth redirect URI requirement, so the full integration works in Bolt's WebContainer preview. Outbound HTTPS calls to api.createsend.com work normally. You can test subscriber addition, campaign fetching, and analytics retrieval in development before deploying. Deploy when you are ready to share with users or need a stable public URL.

### How do I fetch campaign analytics from Campaign Monitor in Bolt.new?

Use two API calls: GET /clients/{clientId}/campaigns.json to list campaigns, and GET /campaigns/{campaignId}/summary.json for detailed stats per campaign. The summary returns absolute numbers (UniqueOpened, Clicks, Bounces) — calculate rates by dividing by Recipients. Fetch summaries lazily (only when a campaign is selected) rather than for all campaigns at once to avoid N+1 API calls.

---

Source: https://www.rapidevelopers.com/bolt-ai-integrations/campaign-monitor
© RapidDev — https://www.rapidevelopers.com/bolt-ai-integrations/campaign-monitor
