Skip to main content
RapidDev - Software Development Agency
bolt-ai-integrationsBolt Chat + API Route

How to Integrate Bolt.new with Constant Contact

Constant Contact integrates with Bolt.new through a Next.js API route using OAuth 2.0. Register a developer app to get a client ID and secret, handle the OAuth callback on your deployed site (not in the WebContainer preview), store the access token in .env, and call Constant Contact's v3 REST API to manage contact lists, add subscribers, and create email campaigns.

What you'll learn

  • How to register a Constant Contact developer app and configure OAuth 2.0 with a redirect URI for your deployed site
  • How to handle the OAuth callback and exchange the authorization code for an access token in a Next.js API route
  • How to add subscribers to Constant Contact lists from Bolt forms using the v3 Contacts API
  • How to create and send email campaigns via the Constant Contact Campaign Activities API
  • How to fetch campaign analytics (open rates, click rates, bounces) and display them in a React dashboard
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate18 min read30 minutesMarketingApril 2026RapidDev Engineering Team
TL;DR

Constant Contact integrates with Bolt.new through a Next.js API route using OAuth 2.0. Register a developer app to get a client ID and secret, handle the OAuth callback on your deployed site (not in the WebContainer preview), store the access token in .env, and call Constant Contact's v3 REST API to manage contact lists, add subscribers, and create email campaigns.

Constant Contact Email Marketing Integration for Bolt.new Apps

Constant Contact has served small businesses and nonprofits for over two decades, building a reputation for simplicity and reliability in email marketing. Unlike platforms that cater to developers and growth engineers, Constant Contact's design philosophy centers on ease of use for non-technical users — a trait that extends to its API. The v3 REST API follows standard HTTP conventions with straightforward JSON payloads, making it accessible for developers building custom subscriber capture forms, internal campaign dashboards, or event registration integrations.

For Bolt.new apps, the most common use cases are: capturing subscribers from a website or landing page into a Constant Contact list, building a lightweight email dashboard that shows campaign performance without logging into the Constant Contact interface, and connecting event registration forms to Constant Contact for post-event follow-up sequences. The free trial gives you full API access to test your integration before committing to a paid plan.

The key technical consideration for this integration is OAuth 2.0 authentication. Constant Contact requires a registered developer app with a redirect URI that Constant Contact can call back to after the user authorizes your app. Because Bolt's WebContainer runs in a browser sandbox without a stable public URL, the OAuth callback cannot be completed in the development preview — you must deploy to Netlify or Bolt Cloud first, register that deployed URL as your redirect URI in Constant Contact's developer portal, and complete the OAuth flow there. Once you have an access token stored in your .env file, all subsequent API calls work normally in both development and production.

Integration method

Bolt Chat + API Route

Constant Contact uses OAuth 2.0 for authentication, which requires a publicly accessible redirect URI — this means the OAuth authorization flow must be completed on your deployed site, not in the Bolt WebContainer preview. Once you have an access token, all API calls go through a Next.js API route that keeps the token server-side. The Constant Contact v3 REST API supports managing contacts, lists, campaigns, and analytics via standard HTTPS requests.

Prerequisites

  • A Constant Contact account with a free trial or paid plan — the free trial provides full API access for testing
  • A registered Constant Contact developer app at developer.constantcontact.com with an OAuth redirect URI pointing to your deployed site (e.g., https://your-app.netlify.app/api/auth/callback/constant-contact)
  • Your Constant Contact app's client ID and client secret from the developer portal
  • A Bolt.new project using Next.js (for API routes that keep the access token server-side)
  • Your deployed site URL (Netlify or Bolt Cloud) to complete the OAuth flow — OAuth callbacks do not work in Bolt's WebContainer preview

Step-by-step guide

1

Register a Constant Contact developer app and configure OAuth

Before writing any code, you need to register a developer application in Constant Contact's developer portal. This creates the OAuth credentials (client ID and secret) your Bolt.new app will use to authenticate with the Constant Contact API. Go to developer.constantcontact.com and sign in with your Constant Contact account. Click 'My Applications' and then 'New Application.' Give your app a name (for example, 'My Bolt Marketing App') and a brief description. The most important field is the Redirect URI — this is where Constant Contact will redirect the user after they authorize your app. Since Bolt's WebContainer runs in a browser sandbox without a stable public URL, you must use your deployed site's URL here: for example, https://your-app.netlify.app/api/auth/callback/constant-contact. You cannot use localhost or the Bolt preview URL as a redirect URI. After creating the app, Constant Contact shows you an API Key (this is your client ID) and a Client Secret. Copy both values. The client ID is safe to use in slightly more visible places, but the client secret must be kept server-side only and stored in .env as CONSTANT_CONTACT_CLIENT_SECRET. Constant Contact's OAuth 2.0 flow works like standard OAuth: your app redirects the user to Constant Contact's authorization URL, the user logs in and grants permission, Constant Contact redirects back to your redirect URI with an authorization code, and your app exchanges the code for an access token. The access token is then used as a Bearer token in all API calls. Access tokens have a long expiration, and Constant Contact also provides a refresh token for obtaining new access tokens without re-authorizing. For Bolt.new integrations where you are the only user (a backend integration for your own account), you can also use the Constant Contact Authorization Code Flow manually: open the authorization URL in a browser, authorize, capture the code from the redirect URL, and exchange it for an access token in a one-time script. This avoids building a full OAuth UI in your app.

Bolt.new Prompt

Add Constant Contact OAuth environment variables to .env. Create the file with CONSTANT_CONTACT_CLIENT_ID=your_client_id, CONSTANT_CONTACT_CLIENT_SECRET=your_client_secret, CONSTANT_CONTACT_ACCESS_TOKEN=your_access_token (after completing OAuth), and CONSTANT_CONTACT_LIST_ID=your_default_list_id. Add comments explaining where to find each value in the Constant Contact developer portal.

Paste this in Bolt.new chat

.env
1# .env
2# Constant Contact developer app credentials
3# Get from developer.constantcontact.com My Applications
4CONSTANT_CONTACT_CLIENT_ID=your_client_id_here
5CONSTANT_CONTACT_CLIENT_SECRET=your_client_secret_here
6
7# Access token obtained after completing OAuth flow
8# Required for all API calls server-side only, never NEXT_PUBLIC_
9CONSTANT_CONTACT_ACCESS_TOKEN=your_access_token_here
10
11# Constant Contact list ID to add new subscribers to
12# Find in Constant Contact Contacts Lists click a list copy ID from URL
13CONSTANT_CONTACT_LIST_ID=your_list_id_here

Pro tip: To get an access token for your own account without building a full OAuth UI, use Constant Contact's OAuth Playground at developer.constantcontact.com/api_guide/oauth2.html. You can authorize and retrieve an access token directly in the browser, then paste it into .env for development and your hosting platform's environment variables for production.

Expected result: You have a registered Constant Contact developer app with client ID, client secret, and an access token in .env. The redirect URI in the developer portal matches your deployed site URL.

2

Build the Constant Contact API service and subscriber route

With credentials in place, scaffold the Constant Contact API service layer. Create a utility module at lib/constant-contact.ts that handles authentication headers and wraps the most commonly used v3 API endpoints. Then create Next.js API routes that the React frontend calls to add subscribers and manage contact lists. The Constant Contact v3 API base URL is https://api.cc.email/v3. Authentication uses a Bearer token in the Authorization header — the access token you obtained via OAuth. The primary endpoints for subscriber management are: GET /contact_lists (retrieve all lists with their IDs and names), POST /contacts (create or update a contact, optionally adding them to lists), GET /contacts (search contacts by email), and GET /contact_lists/{list_id}/contacts (list members of a specific list). The POST /contacts endpoint supports an important feature called 'create_or_update' via the update_source field. Setting update_source to 'Account' with an existing email address will update the contact rather than returning a duplicate error — critical for form-based subscriber capture where the same user might submit multiple times. Paste the Bolt prompt below to generate the API service and subscriber route. After generation, verify that lib/constant-contact.ts reads CONSTANT_CONTACT_ACCESS_TOKEN from process.env and that no token value appears in any client-side file. The subscriber form component should call /api/constant-contact/subscribe rather than calling the Constant Contact API directly — direct client-side calls expose your access token in browser network requests. One important note about the WebContainer development environment: outbound HTTP calls to api.cc.email work fine in Bolt's preview. You can test the subscriber capture form in development and see contacts appear in your Constant Contact account in real time. The OAuth callback flow, however, requires the deployed site.

Bolt.new Prompt

Create a Constant Contact v3 API integration for this Next.js app. (1) Create lib/constant-contact.ts with a ccFetch(endpoint, options) utility that adds Authorization: Bearer {CONSTANT_CONTACT_ACCESS_TOKEN} headers from process.env. Export: getLists() to fetch all contact lists, addSubscriber(data) to POST to /contacts with create_or_update behavior, and getCampaigns(limit) to GET /emails. (2) Create app/api/constant-contact/subscribe/route.ts that handles POST requests with firstName, lastName, email, and optional listId fields. Call addSubscriber from the lib. Return the created contact ID. Add TypeScript types throughout. (3) Create a NewsletterForm React component that calls /api/constant-contact/subscribe and shows success/error states.

Paste this in Bolt.new chat

lib/constant-contact.ts
1// lib/constant-contact.ts
2const CC_BASE = 'https://api.cc.email/v3';
3
4async function ccFetch<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
5 const token = process.env.CONSTANT_CONTACT_ACCESS_TOKEN;
6 if (!token) throw new Error('CONSTANT_CONTACT_ACCESS_TOKEN not configured');
7
8 const res = await fetch(`${CC_BASE}${endpoint}`, {
9 ...options,
10 headers: {
11 Authorization: `Bearer ${token}`,
12 'Content-Type': 'application/json',
13 ...options.headers,
14 },
15 });
16
17 if (!res.ok) {
18 const error = await res.json().catch(() => ({ message: res.statusText }));
19 throw Object.assign(new Error(error.error_message ?? 'Constant Contact API error'), {
20 status: res.status,
21 body: error,
22 });
23 }
24
25 return res.json();
26}
27
28export interface CCContactPayload {
29 email_address: { address: string; permission_to_send: string };
30 first_name?: string;
31 last_name?: string;
32 list_memberships?: string[];
33 update_source?: string;
34}
35
36export async function addSubscriber(
37 email: string,
38 firstName: string,
39 lastName: string,
40 listId?: string
41) {
42 const payload: CCContactPayload = {
43 email_address: { address: email, permission_to_send: 'implicit' },
44 first_name: firstName,
45 last_name: lastName,
46 update_source: 'Account',
47 };
48
49 const targetList = listId ?? process.env.CONSTANT_CONTACT_LIST_ID;
50 if (targetList) {
51 payload.list_memberships = [targetList];
52 }
53
54 return ccFetch('/contacts', { method: 'POST', body: JSON.stringify(payload) });
55}
56
57export async function getLists() {
58 return ccFetch('/contact_lists?include_count=true&status=ACTIVE');
59}
60
61export async function getCampaigns(limit = 10) {
62 return ccFetch(`/emails?limit=${limit}&status=SENT`);
63}

Pro tip: The permission_to_send field is required for all new contacts. Use 'implicit' for contacts who provided their email through a form with a clear opt-in statement. Use 'explicit' only if you have a record of their explicit consent. Using the wrong permission level can affect deliverability.

Expected result: lib/constant-contact.ts provides authenticated access to the Constant Contact v3 API. The /api/constant-contact/subscribe route creates or updates contacts. Submitting the newsletter form in the Bolt preview adds a real subscriber to your Constant Contact list.

3

Handle OAuth 2.0 callback on the deployed site

For applications where users will authenticate with their own Constant Contact accounts (rather than using your own token), you need a full OAuth 2.0 implementation with an authorization callback route. This step is also required if you need to refresh expired access tokens programmatically. The OAuth flow has four steps. First, your app constructs an authorization URL: https://authz.constantcontact.com/oauth2/default/v1/authorize?response_type=code&client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}&scope=contact_data+campaign_data. The scope parameter controls what permissions you're requesting — contact_data gives access to contacts and lists, campaign_data gives access to campaigns and analytics. Second, the user is redirected to this URL and logs into their Constant Contact account. Third, after authorization, Constant Contact redirects back to your redirect URI with a code query parameter (for example: https://your-app.netlify.app/api/auth/callback/constant-contact?code=ABC123). Fourth, your callback route makes a server-side POST request to Constant Contact's token endpoint to exchange the code for access and refresh tokens. This callback must be handled by a real server — it cannot work in Bolt's WebContainer preview because the preview URL is a dynamic StackBlitz-generated URL that changes between sessions and cannot be registered as a stable redirect URI in the developer portal. Deploy to Netlify or Bolt Cloud first, register the production URL as your redirect URI, and test the OAuth flow on the deployed site. For single-user integrations (connecting your own Constant Contact account to your Bolt app), the simpler approach is to complete the OAuth flow once in a browser, capture the access token, store it in your hosting environment variables, and skip building the callback route entirely. The Constant Contact Developer Portal provides an OAuth Playground for exactly this purpose.

Bolt.new Prompt

Create a Next.js OAuth callback route at app/api/auth/callback/constant-contact/route.ts for Constant Contact. The route should: (1) Extract the 'code' query parameter from the request URL, (2) POST to https://authz.constantcontact.com/oauth2/default/v1/token with the code, redirect_uri, client_id, and client_secret using Basic auth (base64 of client_id:client_secret as Authorization header), (3) Exchange for access_token and refresh_token, (4) Store the access_token in a server-side session or return it for manual .env storage, (5) Redirect to /dashboard on success. Also create /api/auth/constant-contact/url/route.ts that returns the OAuth authorization URL so the frontend can redirect users to it.

Paste this in Bolt.new chat

app/api/auth/callback/constant-contact/route.ts
1// app/api/auth/callback/constant-contact/route.ts
2import { NextResponse } from 'next/server';
3
4export async function GET(request: Request) {
5 const { searchParams } = new URL(request.url);
6 const code = searchParams.get('code');
7
8 if (!code) {
9 return NextResponse.json({ error: 'Missing authorization code' }, { status: 400 });
10 }
11
12 const clientId = process.env.CONSTANT_CONTACT_CLIENT_ID!;
13 const clientSecret = process.env.CONSTANT_CONTACT_CLIENT_SECRET!;
14 const redirectUri = `${process.env.NEXT_PUBLIC_APP_URL}/api/auth/callback/constant-contact`;
15
16 // Exchange code for tokens
17 const tokenRes = await fetch(
18 'https://authz.constantcontact.com/oauth2/default/v1/token',
19 {
20 method: 'POST',
21 headers: {
22 Authorization: `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`,
23 'Content-Type': 'application/x-www-form-urlencoded',
24 },
25 body: new URLSearchParams({
26 grant_type: 'authorization_code',
27 code,
28 redirect_uri: redirectUri,
29 }),
30 }
31 );
32
33 if (!tokenRes.ok) {
34 const err = await tokenRes.json();
35 return NextResponse.json({ error: err }, { status: 400 });
36 }
37
38 const tokens = await tokenRes.json();
39 // In production: store tokens in your database or session
40 // For development: log the access token to manually add to .env
41 console.log('Access token:', tokens.access_token);
42
43 return NextResponse.redirect(new URL('/dashboard', request.url));
44}

Pro tip: Constant Contact access tokens have a long but finite lifespan. Use the refresh_token to obtain new access tokens when they expire. The refresh flow sends a POST to the same token endpoint with grant_type=refresh_token and refresh_token={your_refresh_token}.

Expected result: The OAuth callback route exchanges the authorization code for tokens when Constant Contact redirects back after user authorization. The flow works on the deployed site but not in the Bolt WebContainer preview (by design — the preview has no stable callback URL).

4

Create a campaign analytics dashboard

One of the most valuable things you can build with the Constant Contact API is a campaign performance dashboard that surfaces email analytics without requiring access to the Constant Contact platform. This is useful for clients, team members who need read-only access, or integrating email analytics alongside other business metrics in a unified dashboard. The Constant Contact v3 API provides campaign data through several endpoints. GET /emails returns a paginated list of email campaigns with basic metadata: campaign name, subject line, status (SENT, SCHEDULED, DRAFT), sent date, and unique counts for opens and clicks at the summary level. For deeper analytics on a specific campaign, GET /reports/summary_reports?campaign_ids={id} returns detailed metrics including opens, clicks, bounces, unsubscribes, and forwarding data. Build the dashboard in two layers: a campaign list view showing all sent campaigns with top-line metrics, and a detail view that shows the full analytics report for a selected campaign. The list view should be sortable by sent date, open rate, and click rate to help identify top-performing and underperforming campaigns. For displaying analytics data, Recharts is the recommended charting library for Bolt-generated Next.js apps. A bar chart comparing open rates across recent campaigns provides immediate visual context. Color-code open rate bars above 25% as green (industry average) and below as orange or red — this immediately highlights campaigns that need attention. API responses are paginated — Constant Contact returns up to 500 campaigns per page with a cursor-based next link for additional pages. For most small business accounts, the first page covers all recent campaigns. Handle the pagination cursor in the API route so the React component can request additional pages if needed.

Bolt.new Prompt

Build a Constant Contact campaign analytics dashboard. (1) Create app/api/constant-contact/campaigns/route.ts that GETs https://api.cc.email/v3/emails?status=SENT&limit=25 with CONSTANT_CONTACT_ACCESS_TOKEN. Return campaign id, name, subject, sent_at date, and summary stats. (2) Create app/api/constant-contact/campaigns/[id]/stats/route.ts that GETs https://api.cc.email/v3/reports/summary_reports?campaign_ids={id}. (3) Build a React dashboard page at app/dashboard/campaigns/page.tsx with a campaign list table showing name, date, opens, and clicks. Add a Recharts BarChart comparing open_rate vs click_rate across the 10 most recent campaigns. Clicking a campaign row expands it to show detailed stats: bounces, unsubscribes, forwards. Use Tailwind CSS.

Paste this in Bolt.new chat

app/api/constant-contact/campaigns/route.ts
1// app/api/constant-contact/campaigns/route.ts
2import { NextResponse } from 'next/server';
3
4export async function GET(request: Request) {
5 const token = process.env.CONSTANT_CONTACT_ACCESS_TOKEN;
6 if (!token) {
7 return NextResponse.json({ error: 'Constant Contact not configured' }, { status: 500 });
8 }
9
10 const { searchParams } = new URL(request.url);
11 const limit = searchParams.get('limit') ?? '25';
12
13 const res = await fetch(
14 `https://api.cc.email/v3/emails?status=SENT&limit=${limit}`,
15 { headers: { Authorization: `Bearer ${token}` } }
16 );
17
18 if (!res.ok) {
19 return NextResponse.json({ error: 'Failed to fetch campaigns' }, { status: res.status });
20 }
21
22 const data = await res.json();
23
24 const campaigns = (data.campaigns ?? []).map((c: Record<string, unknown>) => ({
25 id: c.campaign_id,
26 name: c.name,
27 subject: (c.campaign_activities as Array<{ subject?: string }>)?.[0]?.subject ?? '',
28 status: c.current_status,
29 sentAt: c.updated_at,
30 }));
31
32 return NextResponse.json({ campaigns, next: data._links?.next?.href ?? null });
33}

Pro tip: Constant Contact's campaign list endpoint returns a 'campaigns' array with basic metadata. For detailed open/click rates, you need to make a separate call to the /reports/summary_reports endpoint with the specific campaign_id. Avoid fetching detailed stats for all campaigns on the list page to stay within rate limits.

Expected result: The campaign dashboard displays all sent Constant Contact campaigns with a bar chart comparing open and click rates. Clicking a campaign shows detailed analytics including bounces, unsubscribes, and forward counts — all without logging into Constant Contact.

Common use cases

Subscriber Capture from Landing Page

A signup form on a Bolt.new marketing site that adds new subscribers to a specific Constant Contact list. Visitors enter their name and email, the form submits to a Next.js API route, and the route calls Constant Contact's POST /contacts endpoint to create the contact and add them to the designated list. Confirmation email sequences configured in Constant Contact fire automatically.

Bolt.new Prompt

Add a newsletter signup form to this landing page that subscribes users to a Constant Contact list. Create a form component with first name, last name, and email fields. On submit, call a Next.js API route at /api/constant-contact/subscribe that POSTs to https://api.cc.email/v3/contacts with CONSTANT_CONTACT_ACCESS_TOKEN from process.env. The contact should be added to the list ID stored in CONSTANT_CONTACT_LIST_ID. Map form fields to: first_name, last_name, email_address. Use the create_or_update action to handle duplicate emails. Show a success confirmation and error states. Use TypeScript and Tailwind CSS.

Copy this prompt to try it in Bolt.new

Email Campaign Analytics Dashboard

An internal dashboard showing Constant Contact campaign performance metrics — open rates, click-through rates, bounce counts, and unsubscribes for recent campaigns. Marketing team members can view campaign results in a custom interface without logging into the Constant Contact platform, integrated alongside other business metrics in a single dashboard.

Bolt.new Prompt

Build a Constant Contact campaign analytics dashboard. Create a Next.js API route at /api/constant-contact/campaigns that fetches recent email campaigns from https://api.cc.email/v3/emails using CONSTANT_CONTACT_ACCESS_TOKEN. Also create /api/constant-contact/campaigns/[id]/stats that fetches campaign activity stats for a specific campaign ID. In React, display a list of recent campaigns with their name, sent date, and a summary of opens and clicks. On clicking a campaign, show a detail panel with open_rate, click_rate, bounces, and unsubscribes. Use recharts for a bar chart comparing open vs click rates across campaigns. Use Tailwind CSS for layout.

Copy this prompt to try it in Bolt.new

Event Registration Sync to Constant Contact

A custom event registration form in Bolt that captures attendee details and automatically adds them to a Constant Contact list tagged with the event name. After the event, the marketing team can send follow-up emails directly from Constant Contact to all attendees using that list.

Bolt.new Prompt

Create an event registration form component that collects attendee name, email, company, and job title. On submission, call /api/constant-contact/contacts which should POST to Constant Contact's v3 contacts API (https://api.cc.email/v3/contacts) with CONSTANT_CONTACT_ACCESS_TOKEN. Add the contact to list CONSTANT_CONTACT_EVENT_LIST_ID and include a custom field 'event_name' with value 'Spring Conference 2026'. Handle the create_or_update action to avoid duplicates. Return a confirmation with the contact's ID. Show a loading state during submission and a thank-you message on success.

Copy this prompt to try it in Bolt.new

Troubleshooting

OAuth redirect returns 'invalid_redirect_uri' error from Constant Contact

Cause: The redirect URI in your authorization request does not exactly match the redirect URI registered in your Constant Contact developer app. Even a trailing slash difference or http vs https mismatch will cause this error.

Solution: Go to developer.constantcontact.com → My Applications → your app → edit the Redirect URI. Ensure it exactly matches the URL in your code, including protocol (https), domain, path, and no trailing slash. The redirect URI must also be a deployed URL — Bolt's WebContainer preview URL and localhost do not work as Constant Contact redirect URIs.

POST to /contacts returns 400 Bad Request with 'email_address is required'

Cause: The email_address field in the Constant Contact v3 API is an object with 'address' and 'permission_to_send' sub-fields, not a plain string. Sending email as a top-level string field causes a validation error.

Solution: Ensure the contact payload wraps the email in the correct object structure. The email_address field must be an object: { "address": "user@example.com", "permission_to_send": "implicit" }.

typescript
1// Correct contact payload structure for Constant Contact v3
2const payload = {
3 email_address: {
4 address: email, // email as nested 'address' field
5 permission_to_send: 'implicit', // required: 'implicit' or 'explicit'
6 },
7 first_name: firstName,
8 last_name: lastName,
9 list_memberships: [listId],
10 update_source: 'Account', // prevents duplicate errors
11};

Constant Contact API returns 401 Unauthorized after the integration worked previously

Cause: Constant Contact access tokens expire. When the token expires, all API calls return 401 until you refresh the token using the refresh token obtained during the original OAuth flow.

Solution: Use the refresh token to obtain a new access token. POST to https://authz.constantcontact.com/oauth2/default/v1/token with grant_type=refresh_token. Update CONSTANT_CONTACT_ACCESS_TOKEN in your .env and redeploy. For production apps, implement automatic token refresh logic that detects 401 responses and refreshes before retrying.

typescript
1// Refresh an expired Constant Contact access token
2async function refreshAccessToken(refreshToken: string) {
3 const clientId = process.env.CONSTANT_CONTACT_CLIENT_ID!;
4 const clientSecret = process.env.CONSTANT_CONTACT_CLIENT_SECRET!;
5
6 const res = await fetch('https://authz.constantcontact.com/oauth2/default/v1/token', {
7 method: 'POST',
8 headers: {
9 Authorization: `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`,
10 'Content-Type': 'application/x-www-form-urlencoded',
11 },
12 body: new URLSearchParams({ grant_type: 'refresh_token', refresh_token: refreshToken }),
13 });
14 return res.json(); // contains new access_token and refresh_token
15}

Newsletter form submits successfully in the Bolt preview but contacts do not appear in Constant Contact

Cause: The API call is likely failing silently. Either the CONSTANT_CONTACT_ACCESS_TOKEN is missing or incorrect, or the list ID in CONSTANT_CONTACT_LIST_ID does not match any existing list in your account.

Solution: Open browser DevTools → Network tab and inspect the response from /api/constant-contact/subscribe. If it returns an error body, read the error_message field. Verify CONSTANT_CONTACT_ACCESS_TOKEN is set and starts with a valid token string. Find your list ID in Constant Contact → Contacts → Lists, click a list name, and copy the numeric ID from the URL.

Best practices

  • Always use the update_source: 'Account' parameter when POSTing to /contacts to enable upsert behavior — this prevents duplicate contacts when the same email submits your form multiple times
  • Store the Constant Contact access token in server-side .env only — it grants full access to your contact database and campaign management, so it must never appear in client-side JavaScript or be prefixed with NEXT_PUBLIC_
  • Complete the OAuth flow on your deployed site, not in Bolt's WebContainer preview — the preview uses a dynamic URL that cannot be registered as a stable OAuth redirect URI
  • Request only the OAuth scopes you need (contact_data for subscriber management, campaign_data for analytics) — avoid requesting broad permissions that could be misused
  • Implement access token refresh logic for production apps so the integration does not silently break when the token expires — store both access_token and refresh_token in your environment configuration
  • Use Constant Contact's batch contact import endpoints for bulk subscriber operations rather than making individual POST /contacts calls to stay within rate limits
  • Test your subscription form with real email addresses on the deployed site before launching — ad blockers and browser extensions in the development environment can sometimes interfere with form validation and network requests

Alternatives

Frequently asked questions

Does Constant Contact work with Bolt.new?

Yes. Constant Contact's v3 REST API uses standard HTTPS requests that work in Bolt's WebContainer via a Next.js API route. The main constraint is OAuth 2.0 authentication — the OAuth callback must be handled on your deployed site (Netlify or Bolt Cloud), not in the WebContainer preview, because OAuth requires a stable redirect URI. Once you have an access token, all subscriber management and campaign API calls work in both development and production.

How do I connect Bolt.new to Constant Contact?

Register a developer app at developer.constantcontact.com to get a client ID and client secret. Deploy your Bolt app to Netlify or Bolt Cloud, register the deployed URL as the OAuth redirect URI, complete the OAuth authorization flow to get an access token, and store it in .env as CONSTANT_CONTACT_ACCESS_TOKEN. Then create Next.js API routes that call the Constant Contact v3 API with the token in the Authorization header.

Can I test the Constant Contact integration in Bolt's development preview?

Partially. Outbound API calls to api.cc.email work in Bolt's WebContainer preview — you can test subscriber creation and campaign fetching in development. However, the OAuth 2.0 authorization flow (which requires a callback redirect from Constant Contact to your app) cannot be completed in the preview because the WebContainer uses dynamic URLs that cannot be registered as stable redirect URIs. Deploy to Netlify first to complete the OAuth setup.

Do I need a paid Constant Contact plan to use the API?

Constant Contact does not offer a permanent free tier — only a free trial for new accounts. The trial provides full API access and is sufficient for building and testing your Bolt.new integration. After the trial, you need a paid plan (Email or Email Plus) to continue using the API in production. All paid plans include full API access with the same v3 endpoints.

How do I add subscribers to a Constant Contact list from a Bolt form?

Create a Next.js API route that receives the form data and POSTs to https://api.cc.email/v3/contacts with an Authorization: Bearer {token} header. The request body must include email_address as an object with 'address' and 'permission_to_send' fields, plus a list_memberships array containing your list ID. Add update_source: 'Account' to enable upsert behavior and avoid duplicate contact errors.

How do I deploy a Bolt.new Constant Contact app to Netlify?

Connect your Bolt project to Netlify through Settings → Applications. In Netlify's site settings under Environment Variables, add CONSTANT_CONTACT_CLIENT_ID, CONSTANT_CONTACT_CLIENT_SECRET, CONSTANT_CONTACT_ACCESS_TOKEN, and CONSTANT_CONTACT_LIST_ID. Register your Netlify URL as the redirect URI in your Constant Contact developer app. Trigger a redeploy after adding environment variables. Complete the OAuth flow on the live Netlify URL to obtain your access token.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.