Integrate ConvertKit (now rebranded as Kit) with Bolt.new by embedding ConvertKit forms using their JavaScript SDK or by managing subscribers programmatically via their REST API v3. Form embeds work directly in Bolt's WebContainer preview — just add the script tag. For subscriber management and tag operations, use a Next.js API route with your ConvertKit API key stored in a server-side environment variable. Webhook-based automation triggers require a deployed URL.
Add ConvertKit Email Marketing to Your Bolt.new App
ConvertKit — recently rebranded as Kit — has carved out a strong position as the email platform of choice for individual creators: bloggers, podcasters, YouTubers, course creators, and newsletter writers. Its tag-based subscriber management model is fundamentally different from list-based platforms like Mailchimp. Instead of putting subscribers in separate lists, ConvertKit assigns tags to indicate subscriber attributes (source, interests, purchase history, engagement level), which enables highly flexible segmentation and automation rules without duplicating contacts.
For Bolt.new developers, the integration story is unusually straightforward. ConvertKit's embeddable forms are hosted on ConvertKit's infrastructure and load via a script tag — no CORS issues, no API keys required, and they work perfectly in Bolt's WebContainer preview. This is the fastest integration path: generate an embed code from ConvertKit's form builder, add a script tag to your React component, and subscribers are captured immediately. The ConvertKit form handles validation, double opt-in, and GDPR compliance automatically.
For deeper integrations — custom signup flows, tagging users based on in-app behavior, syncing purchase data to ConvertKit — the REST API v3 provides full subscriber management capabilities. These calls go through a Next.js API route to keep your API key server-side. ConvertKit's API key is effectively a secret that grants full access to your subscriber list, so never expose it in client-side code or NEXT_PUBLIC_ environment variables.
Integration method
ConvertKit integrates with Bolt.new apps through two complementary approaches: embedding ConvertKit-hosted opt-in forms using their JavaScript embed code, and managing subscribers programmatically using the ConvertKit REST API v3 from Next.js API routes. Form embeds work in the WebContainer preview immediately. API operations use your ConvertKit API key stored in server-side environment variables — never in client-side code. Automation webhook triggers require a deployed Netlify URL.
Prerequisites
- A ConvertKit (Kit) account at convertkit.com — the free plan supports up to 1,000 subscribers
- A ConvertKit API key found in Settings → Developer → API Keys — create one with a descriptive name
- At least one ConvertKit form created (Forms → Create New Form) — note the Form ID from the form URL
- A Bolt.new account with a new Next.js project open
- A Netlify account for deployment if you plan to use ConvertKit automation webhooks
Step-by-step guide
Get your ConvertKit API key and form IDs
Get your ConvertKit API key and form IDs
ConvertKit uses two different authentication mechanisms depending on the API operation. Most subscriber management operations use an API key for simple authentication. Some read operations (fetching subscriber counts visible to the public) use a public API key. Write operations — subscribing users, adding tags, fetching subscriber details — require the private API key, which must be kept server-side. To find your API key: in the ConvertKit dashboard, click your account name in the top-right, then go to Settings → Developer. You will see your API Key (labeled 'API Key' or 'API Secret' depending on the version). This is the credential you will use for all API calls from your Next.js API routes. Do not confuse it with the API Secret (an older credential type) or the public-facing API key — for the v3 API, you use api_key as a query parameter or request body field. For form embeds, you need the Form ID. Open the form in ConvertKit's form builder and look at the URL — it will be in the format app.convertkit.com/forms/{formId}/edit. The numeric ID at the end is your Form ID. You will also see it in the embed code that ConvertKit generates: look for `data-uid` attribute value in the script embed. For tag-based operations, create any tags you plan to use first: in ConvertKit, go to Subscribers → Tags → Create a Tag. Each tag has a numeric ID visible in the URL when you click on it. Note these IDs — you will reference them in your API calls. Add your credentials to .env.local. The API key goes in a server-only variable. The Form ID can be public (it is embedded in the script tag visible to users anyway), so NEXT_PUBLIC_CONVERTKIT_FORM_ID is acceptable.
Set up ConvertKit in my Next.js project. Create a .env.local file with: CONVERTKIT_API_KEY=your-api-key-here (server-side only), NEXT_PUBLIC_CONVERTKIT_FORM_ID=your-form-id (public), CONVERTKIT_FORM_ID=your-form-id (server-side copy), CONVERTKIT_CUSTOMER_TAG_ID=your-tag-id. Create a lib/convertkit.ts file with TypeScript interfaces for ConvertKit subscriber data and exported helper functions: subscribeToForm(email, firstName, formId), addTagToSubscriber(email, tagId), and getSubscriberByEmail(email). Use process.env.CONVERTKIT_API_KEY for authentication.
Paste this in Bolt.new chat
1// lib/convertkit.ts2const API_BASE = 'https://api.convertkit.com/v3';34export interface ConvertKitSubscriber {5 id: number;6 first_name: string;7 email_address: string;8 state: 'active' | 'inactive' | 'cancelled' | 'bounced' | 'complained';9 created_at: string;10 fields: Record<string, string>;11}1213export interface SubscribeResult {14 subscription: {15 id: number;16 state: string;17 created_at: string;18 source: string;19 referrer: string | null;20 subscribable_id: number;21 subscribable_type: string;22 subscriber: ConvertKitSubscriber;23 };24}2526export async function subscribeToForm(27 email: string,28 firstName: string,29 formId?: string30): Promise<SubscribeResult> {31 const apiKey = process.env.CONVERTKIT_API_KEY;32 const targetFormId = formId || process.env.CONVERTKIT_FORM_ID;33 if (!apiKey) throw new Error('CONVERTKIT_API_KEY is not set');34 if (!targetFormId) throw new Error('No form ID provided');3536 const response = await fetch(`${API_BASE}/forms/${targetFormId}/subscribe`, {37 method: 'POST',38 headers: { 'Content-Type': 'application/json' },39 body: JSON.stringify({40 api_key: apiKey,41 email,42 first_name: firstName,43 }),44 });4546 const data = await response.json();47 if (!response.ok) throw new Error(data.message || 'ConvertKit subscribe failed');48 return data;49}5051export async function addTagToSubscriber(52 email: string,53 tagId: string54): Promise<void> {55 const apiKey = process.env.CONVERTKIT_API_KEY;56 if (!apiKey) throw new Error('CONVERTKIT_API_KEY is not set');5758 const response = await fetch(`${API_BASE}/tags/${tagId}/subscribe`, {59 method: 'POST',60 headers: { 'Content-Type': 'application/json' },61 body: JSON.stringify({ api_key: apiKey, email }),62 });6364 const data = await response.json();65 if (!response.ok) throw new Error(data.message || 'ConvertKit tag failed');66}6768export async function getSubscriberByEmail(69 email: string70): Promise<ConvertKitSubscriber | null> {71 const apiKey = process.env.CONVERTKIT_API_KEY;72 if (!apiKey) throw new Error('CONVERTKIT_API_KEY is not set');7374 const url = `${API_BASE}/subscribers?api_secret=${apiKey}&email_address=${encodeURIComponent(email)}`;75 const response = await fetch(url);76 const data = await response.json();7778 if (!response.ok) throw new Error(data.message || 'ConvertKit fetch failed');79 return data.subscribers?.[0] || null;80}Pro tip: ConvertKit's API v3 uses api_key in the request body for write operations and api_secret as a query parameter for read operations that return sensitive subscriber data. The naming is confusing — what they call 'API Key' in Settings → Developer is the credential used as api_key in POST requests for subscriptions and tag assignments.
Expected result: A lib/convertkit.ts helper with TypeScript interfaces and functions for subscribing users, adding tags, and looking up subscribers, with the API key configured in .env.local.
Embed a ConvertKit form in your React component
Embed a ConvertKit form in your React component
ConvertKit's JavaScript embed is the fastest way to add email capture to a Bolt.new app. The embed consists of two parts: a `<script>` tag that loads ConvertKit's JavaScript, and a `<div>` element with a `data-uid` attribute that ConvertKit targets to inject the form HTML. In a React application, dynamically loading a third-party script requires using the `useEffect` hook to inject the script tag into the document when the component mounts. You should also clean up the script and the injected form HTML when the component unmounts, to avoid duplicate forms if the component is remounted. ConvertKit's inline embed, popup embed, and slide-in embed are all created through the same form builder. The embed type is determined by the JavaScript snippet ConvertKit generates for you — inline forms render directly in the page, while popup and slide-in forms appear as overlays triggered by scroll depth or time delay. For landing pages and sidebar placement, inline is most common. One major advantage of using ConvertKit's embed: you get ConvertKit's built-in spam protection, double opt-in flow (if enabled on the form), mobile responsiveness, and GDPR consent options without implementing any of this yourself. The form also auto-updates when you edit it in ConvertKit — your code does not need to change. This embed approach works perfectly in Bolt.new's WebContainer preview. The script loads from ConvertKit's CDN and renders the form in the browser — no server-side logic required.
Create a reusable ConvertKit form embed component. Create src/components/ConvertKitForm.tsx that accepts props: formId (string, defaults to NEXT_PUBLIC_CONVERTKIT_FORM_ID env var), className (optional string for wrapper styling), onSuccess (optional callback function). Use useEffect to dynamically load the ConvertKit script tag (src='https://f.convertkit.com/{formId}/index.js') when the component mounts and clean it up on unmount. Render a div with data-uid={formId} for ConvertKit to target. Handle the case where the script is already loaded. Also create a NewsletterSection page section using this component with a heading, subheading about newsletter value, and the embedded form centered on the page.
Paste this in Bolt.new chat
1// src/components/ConvertKitForm.tsx2'use client';3import { useEffect, useRef } from 'react';45interface ConvertKitFormProps {6 formId?: string;7 className?: string;8 onSuccess?: () => void;9}1011export function ConvertKitForm({ formId, className = '' }: ConvertKitFormProps) {12 const targetFormId = formId || process.env.NEXT_PUBLIC_CONVERTKIT_FORM_ID;13 const containerRef = useRef<HTMLDivElement>(null);1415 useEffect(() => {16 if (!targetFormId) {17 console.warn('ConvertKitForm: No form ID provided');18 return;19 }2021 const scriptId = `convertkit-form-${targetFormId}`;2223 // Remove existing form content before re-rendering24 if (containerRef.current) {25 containerRef.current.innerHTML = `<div data-uid="${targetFormId}"></div>`;26 }2728 // Avoid loading the script twice29 if (document.getElementById(scriptId)) {30 // Re-trigger form rendering by removing and re-adding the script31 const existingScript = document.getElementById(scriptId);32 existingScript?.remove();33 }3435 const script = document.createElement('script');36 script.id = scriptId;37 script.src = `https://f.convertkit.com/${targetFormId}/index.js`;38 script.async = true;39 document.body.appendChild(script);4041 return () => {42 const scriptEl = document.getElementById(scriptId);43 scriptEl?.remove();44 };45 }, [targetFormId]);4647 if (!targetFormId) {48 return (49 <div className={`p-4 border border-dashed border-gray-300 rounded text-gray-500 text-sm ${className}`}>50 Add NEXT_PUBLIC_CONVERTKIT_FORM_ID to your .env file51 </div>52 );53 }5455 return (56 <div className={className} ref={containerRef}>57 <div data-uid={targetFormId}></div>58 </div>59 );60}Pro tip: ConvertKit forms inherit some styles from their embed code, but you can override the form's appearance by adding custom CSS in ConvertKit's form builder under Styles → Custom CSS. The form injects an iframe-free HTML structure, so your global Tailwind styles will partially apply. For full control, use the custom API approach in the next step instead.
Expected result: A ConvertKitForm React component that loads and renders a ConvertKit-hosted opt-in form, visible in the Bolt.new WebContainer preview.
Build a custom subscribe API route for branded forms
Build a custom subscribe API route for branded forms
For apps where you want pixel-perfect control over the signup form design — matching your exact color palette, typography, and layout — the ConvertKit embed approach may not be the right choice. Instead, build a completely custom React form that calls your own Next.js API route, which in turn calls ConvertKit's API to subscribe the user. This approach has several benefits: complete visual control, the ability to capture additional custom fields beyond email and first name, instant feedback without page reload, and the ability to perform additional logic at signup time (create a Supabase user record, trigger a Stripe trial, add to a specific ConvertKit sequence based on the user's plan selection, etc.). The ConvertKit API call for form subscriptions is a simple POST to `/v3/forms/{formId}/subscribe` with the api_key, email, and first_name in the request body. ConvertKit returns the subscription object immediately. If double opt-in is enabled on the form, the subscriber gets a confirmation email before their state changes to 'active' — your API gets a response with state: 'inactive' until they confirm. If you want immediate subscription without confirmation email, disable double opt-in on the specific form in ConvertKit. For tagging, make the tag assignment a separate API call after the subscription — this way a failure in tag assignment does not prevent the user from being subscribed. Tags are additive in ConvertKit; assigning an existing tag to a subscriber who already has it is idempotent and safe.
Create a custom ConvertKit subscription API route at app/api/convertkit/subscribe/route.ts using the subscribeToForm and addTagToSubscriber helpers from lib/convertkit.ts. Accept POST with: email (required), firstName (optional), tag (optional, a string like 'landing-page' or 'trial-signup'). Validate email format. If tag is provided, look up the corresponding tag ID from a mapping object using CONVERTKIT_TAG_{TAG_NAME}_ID env vars. Subscribe the user to the form, then add the tag if provided. Return success with the subscriber ID, or a validation/API error. Then create a branded SubscribeForm React component with email input, optional first name input, and submit handling that calls this route with a loading state and success message.
Paste this in Bolt.new chat
1// app/api/convertkit/subscribe/route.ts2import { NextRequest, NextResponse } from 'next/server';3import { subscribeToForm, addTagToSubscriber } from '@/lib/convertkit';45const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;67// Map tag names to ConvertKit tag IDs from environment variables8function getTagId(tagName: string): string | null {9 const envKey = `CONVERTKIT_TAG_${tagName.toUpperCase().replace(/-/g, '_')}_ID`;10 return process.env[envKey] || null;11}1213export async function POST(request: NextRequest) {14 const body = await request.json();15 const { email, firstName = '', tag } = body;1617 if (!email || !emailRegex.test(email)) {18 return NextResponse.json({ error: 'Valid email address is required' }, { status: 400 });19 }2021 try {22 const result = await subscribeToForm(email, firstName);23 const subscriberId = result.subscription?.subscriber?.id;2425 // Add tag if provided26 if (tag && subscriberId) {27 const tagId = getTagId(tag);28 if (tagId) {29 try {30 await addTagToSubscriber(email, tagId);31 } catch (tagError) {32 // Log but don't fail the subscription for a tag error33 console.error('ConvertKit tag error:', tagError);34 }35 }36 }3738 return NextResponse.json({39 success: true,40 subscriberId,41 state: result.subscription?.state,42 requiresConfirmation: result.subscription?.state === 'inactive',43 });44 } catch (error) {45 console.error('ConvertKit subscribe error:', error);46 return NextResponse.json(47 { error: 'Failed to subscribe. Please try again.' },48 { status: 500 }49 );50 }51}Pro tip: If your ConvertKit form has double opt-in enabled, subscribers will be in 'inactive' state until they click the confirmation email. Return a message like 'Check your email to confirm your subscription' when the API response shows requiresConfirmation: true, rather than a generic 'You're subscribed!' message that would be misleading.
Expected result: A custom subscribe API route that accepts email and first name, subscribes to ConvertKit, optionally adds a tag, and returns subscriber status — ready to use with any custom-styled signup form.
Deploy to Netlify and configure ConvertKit webhooks
Deploy to Netlify and configure ConvertKit webhooks
Email capture and subscriber management via the ConvertKit API work in Bolt.new's WebContainer during development — outbound API calls to ConvertKit's servers are fully functional in the preview. You can build and test all of the subscribe and tag functionality before deploying. The one ConvertKit feature that requires a deployed URL is Automations webhooks — ConvertKit can send a POST request to your app when specific subscriber events occur (subscriber confirmed, tag added, unsubscribed, purchase completed). These are useful for triggering in-app actions when email events happen: granting access when a subscriber confirms, revoking access when they unsubscribe, or updating a Supabase record when a tag changes. To deploy to Netlify: click Deploy in Bolt.new, connect your Netlify account, and wait for the build. After the initial deploy completes, navigate to Site Configuration → Environment Variables in the Netlify dashboard and add: CONVERTKIT_API_KEY, CONVERTKIT_FORM_ID, and any tag ID variables you defined (CONVERTKIT_TAG_LANDING_PAGE_ID, etc.). Trigger a redeploy. For ConvertKit webhooks: go to ConvertKit → Settings → Integrations → Zapier/Webhooks, or use ConvertKit's Automations builder to trigger webhooks. Alternatively, set up webhooks via the API using POST to `/v3/automations/hooks`. Enter your Netlify URL in the format `https://your-app.netlify.app/api/convertkit/webhook`. Select the events you want to receive. Important: ConvertKit webhooks cannot reach Bolt.new's WebContainer preview. This is a fundamental WebContainer constraint — the browser-based runtime has no persistent public URL and cannot receive incoming HTTP connections. Always configure and test webhooks using your deployed Netlify domain.
Create a ConvertKit webhook handler at app/api/convertkit/webhook/route.ts. Accept POST requests from ConvertKit automation webhooks. Parse the JSON body which contains event type and subscriber data. Handle these events: subscriber.subscriber_activate (log new confirmed subscriber), subscriber.subscriber_unsubscribe (log unsubscribe), tag_subscribe (log tag assignment with subscriber email and tag name). Validate the webhook by checking for a CONVERTKIT_WEBHOOK_SECRET env var if set. Return 200 for all events. Also add a netlify.toml with Next.js build configuration.
Paste this in Bolt.new chat
1// app/api/convertkit/webhook/route.ts2import { NextRequest, NextResponse } from 'next/server';34interface ConvertKitWebhookEvent {5 type: string;6 initiator: string;7 subscriber: {8 id: number;9 first_name: string;10 email_address: string;11 state: string;12 tags: Array<{ id: number; name: string; created_at: string }>;13 };14 tag?: { id: number; name: string };15}1617export async function POST(request: NextRequest) {18 const body = await request.text();1920 let event: ConvertKitWebhookEvent;21 try {22 event = JSON.parse(body);23 } catch {24 return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 });25 }2627 const { type, subscriber, tag } = event;28 const email = subscriber?.email_address;29 const name = subscriber?.first_name;3031 switch (type) {32 case 'subscriber.subscriber_activate':33 console.log(`ConvertKit: New confirmed subscriber — ${name} <${email}>`);34 // TODO: Create user record in Supabase, send welcome email, etc.35 break;3637 case 'subscriber.subscriber_unsubscribe':38 console.log(`ConvertKit: Unsubscribe — ${email}`);39 // TODO: Update subscription status in your database40 break;4142 case 'subscriber.tag_add':43 console.log(`ConvertKit: Tag '${tag?.name}' added to ${email}`);44 // TODO: Trigger relevant action based on tag (e.g., grant course access)45 break;4647 case 'subscriber.tag_remove':48 console.log(`ConvertKit: Tag '${tag?.name}' removed from ${email}`);49 break;5051 default:52 console.log(`ConvertKit webhook: unhandled event type '${type}'`);53 }5455 // Always return 200 — ConvertKit retries failed webhooks56 return NextResponse.json({ received: true });57}Pro tip: ConvertKit automations can trigger webhook calls for many events, but the most valuable for app integrations are subscriber_activate (confirmed double opt-in) and tag_add. Build your access control logic around tag_add if you use ConvertKit tags to manage feature access or content unlocking.
Expected result: A deployed Netlify app with ConvertKit subscriber capture working in production, webhook handler receiving subscriber events, and all environment variables configured.
Common use cases
Embedded Opt-in Form for Newsletter Signup
Add a ConvertKit opt-in form to your landing page or blog sidebar using ConvertKit's JavaScript embed. The form is styled via ConvertKit's form builder, collects the subscriber's email (and optionally their first name), and handles the confirmation email automatically. Works immediately in the Bolt preview without any API keys.
Add a ConvertKit newsletter signup form to my landing page. Create a NewsletterSignup React component that loads a ConvertKit form embed using a script tag. The component should accept a formId prop (from NEXT_PUBLIC_CONVERTKIT_FORM_ID env var), load the ConvertKit script tag dynamically using useEffect, and render a div with the data-uid attribute for ConvertKit to target. Add a subtle 'Powered by ConvertKit' attribution link below the form. Place the component in the hero section and the footer of the landing page.
Copy this prompt to try it in Bolt.new
In-App Subscriber Capture with Custom Styling
Build a completely custom-styled signup form in React that sends subscriber data to ConvertKit via your own API route. This approach lets your form match your app's design system exactly while ConvertKit handles email delivery and list management. Tag subscribers at signup based on which page or product brought them to the form.
Create a custom email capture feature. Build an API route at app/api/convertkit/subscribe/route.ts that accepts POST with email, firstName, and tag (optional string) fields. Subscribe to ConvertKit form using CONVERTKIT_API_KEY and CONVERTKIT_FORM_ID env vars by calling POST https://api.convertkit.com/v3/forms/{formId}/subscribe. If a tag is provided, also add that tag to the subscriber using the tags endpoint. Create a beautiful custom opt-in form component with email and first name inputs, styled with Tailwind, showing a success animation after submission.
Copy this prompt to try it in Bolt.new
Purchase-Based Subscriber Tagging
When a user completes a purchase in your app, automatically tag them in ConvertKit to trigger an automated email sequence — a welcome series for new customers, an upsell sequence, or a course delivery automation. Tags serve as the entry point for ConvertKit's visual automation workflows.
After a successful Stripe checkout, tag the customer in ConvertKit to trigger a welcome email sequence. In my Stripe webhook handler at app/api/stripe/webhook/route.ts, after processing a checkout.session.completed event, call a new API function tagConvertKitSubscriber(email, tag) that uses CONVERTKIT_API_KEY to: first find or create the subscriber by email, then add the specified tag using POST https://api.convertkit.com/v3/tags/{tagId}/subscribe. Use CONVERTKIT_CUSTOMER_TAG_ID env var for the tag ID. Handle errors gracefully so webhook processing continues even if tagging fails.
Copy this prompt to try it in Bolt.new
Troubleshooting
ConvertKit API returns 401 Unauthorized — 'Authorization Failed: API Key is invalid'
Cause: The API key is incorrect, has been regenerated since it was saved, or is being passed in the wrong request field. ConvertKit v3 API uses api_key as a field in the POST body for write operations, not as a header.
Solution: Double-check your API key in ConvertKit Settings → Developer. Ensure it is stored in CONVERTKIT_API_KEY (not NEXT_PUBLIC_CONVERTKIT_API_KEY) and that your API route reads it with process.env.CONVERTKIT_API_KEY. The key goes in the JSON request body as api_key, not in an Authorization header — ConvertKit's v3 API does not use Bearer token authentication.
1// Correct: API key in request body2body: JSON.stringify({3 api_key: process.env.CONVERTKIT_API_KEY,4 email,5 first_name: firstName,6})78// Wrong: API key as header9headers: { 'Authorization': `Bearer ${apiKey}` }Subscriber is added but stays in 'inactive' state indefinitely
Cause: The ConvertKit form has double opt-in enabled. Subscribers are inactive until they click the confirmation email. If the confirmation email goes to spam or the user does not check their inbox, they remain inactive and will not receive future broadcasts.
Solution: For quick subscription flows (like gating content behind email capture), disable double opt-in on the specific form in ConvertKit's form builder under Incentive → Incentive Email → toggle off. For list health, double opt-in is recommended for cold audiences. Communicate the confirmation step clearly in your UI with a message like 'Check your inbox to confirm your subscription'.
ConvertKit embed form is not appearing in the Bolt.new preview
Cause: The ConvertKit script may not have loaded, the form ID is incorrect, or the script is being blocked by a browser extension (ad blockers often block email capture scripts). The div with data-uid renders but ConvertKit's JavaScript has not executed yet.
Solution: Open browser DevTools → Network tab and filter by the script URL (f.convertkit.com). If it shows as blocked, disable ad blockers in the Bolt preview. Verify the form ID is correct by checking the URL in ConvertKit's form editor. Check the Console tab for any JavaScript errors. If testing with an ad blocker, try opening the preview in an incognito window without extensions.
ConvertKit webhooks are not firing during Bolt.new development
Cause: ConvertKit automations send webhook POST requests to a specified URL. Bolt.new's WebContainer does not have a persistent public URL that can receive incoming HTTP connections — the preview URL changes per session and cannot be registered as a webhook target.
Solution: Deploy your app to Netlify (click Deploy in Bolt.new), then register your Netlify URL in ConvertKit's automation webhook settings. This is a fundamental WebContainer limitation — incoming webhook delivery requires a publicly accessible server, which the browser-based WebContainer cannot provide.
Best practices
- Never store your ConvertKit API key in a NEXT_PUBLIC_ environment variable — it grants full read/write access to your subscriber list and must remain server-side only
- Use tags rather than separate forms for audience segmentation — one subscriber with multiple tags is easier to manage than the same subscriber on multiple lists
- Always send your API route's ConvertKit API key via the request body (api_key field), not as an Authorization header — ConvertKit v3 expects body-based authentication
- Handle the double opt-in state gracefully — show a 'Please confirm your email' message when the API returns state: inactive instead of claiming the user is subscribed
- Add error boundaries around ConvertKit embed scripts — script loading failures should not crash your React component tree
- Test your ConvertKit form embed with an ad blocker enabled, since many users browse with ad blockers that block email capture scripts — have a fallback non-embedded form ready
- Use ConvertKit custom fields to store app-specific data like plan tier, signup source, or trial start date alongside the subscriber — this enriches your automation targeting
- Deploy to Netlify before testing webhook automations — ConvertKit cannot send events to Bolt.new's WebContainer preview URL
Alternatives
Mailchimp uses list-based subscriber management with stronger enterprise analytics — better for businesses running multiple brand campaigns than for individual creators using tag-based automation.
AWeber is a reliable, straightforward email platform focused purely on email delivery without the commerce and creator features ConvertKit provides — simpler but less feature-rich for content businesses.
Drip is ConvertKit's closest competitor in the e-commerce email automation space, with stronger Shopify integration and deeper purchase event tracking for product-based businesses.
GetResponse is a broader all-in-one marketing platform with landing pages, webinars, and funnels built in — better for marketers who want everything in one tool rather than a focused email platform.
Frequently asked questions
Does ConvertKit work with Bolt.new's WebContainer during development?
Yes, for most operations. ConvertKit's JavaScript form embed loads from their CDN and renders directly in the browser — it works in the WebContainer preview immediately. API calls to subscribe users and add tags also work in development since they are outbound HTTP requests. The only feature requiring deployment is ConvertKit automation webhooks, which need a public URL to send events to.
Is ConvertKit still called ConvertKit or has it been rebranded?
ConvertKit rebranded to 'Kit' in late 2024. However, their domain (convertkit.com), API endpoints (api.convertkit.com/v3), and npm package names continue to use the ConvertKit name. Most developers still refer to it as ConvertKit. The dashboard is accessible at app.convertkit.com and the brand name Kit is used on their marketing site.
What is the difference between using a ConvertKit form embed versus the API?
The form embed approach uses ConvertKit's hosted JavaScript to render a styled form — zero code for validation, double opt-in, or spam protection, but limited visual customization. The API approach gives you complete control over the form design and allows additional logic at subscription time (tagging, Supabase record creation, Stripe trial initialization), but you handle validation and UX yourself. Both work in Bolt.new.
How do I trigger a ConvertKit email sequence when a user signs up through my app?
In ConvertKit, create an Automation with trigger 'Subscribes to a form' or 'Tag is added' and add your email sequence as the action. When your API route subscribes a user to a form or adds a tag, ConvertKit automatically enters them into any automation that uses that form subscription or tag as a trigger. No additional code is needed on your end — the subscription or tag assignment is the trigger.
Can I use ConvertKit's free plan for my Bolt.new app?
Yes. ConvertKit's free plan supports up to 1,000 subscribers with unlimited email sends and access to the API. The free plan does not include automation workflows or sequences (these require the Creator plan at $9/month). For simple subscriber capture and broadcast emails, the free plan is sufficient to get started.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation