To integrate Klaviyo with V0 by Vercel, create a Next.js API route that calls the Klaviyo REST API to sync customer profiles and track e-commerce events. V0 generates the subscription forms and checkout UI; you add your Klaviyo private API key to Vercel environment variables and deploy. The result is automated email and SMS marketing flows triggered by real customer actions in your Next.js app.
Sync Customer Data and Trigger Email Flows with Klaviyo from Your V0 App
Klaviyo bridges your Next.js e-commerce app with powerful marketing automation. Each customer action you track — signup, purchase, product view, cart abandonment — feeds Klaviyo's segmentation engine, enabling highly targeted campaigns based on actual behavior rather than demographic guesses. This is why Klaviyo reports 3-5x higher email revenue for e-commerce brands compared to generic email platforms.
V0 generates the frontend touchpoints: email signup widgets, checkout subscription checkboxes, and profile update forms. Your Next.js API routes call the Klaviyo API to create profiles, add customers to lists, and fire events. Klaviyo's automated flows then handle the marketing execution — send the welcome email, trigger the cart abandonment sequence 4 hours later, dispatch the post-purchase review request after delivery.
The integration requires two Klaviyo API keys: a private API key (server-only, full access) for API route calls, and optionally a public API key (Klaviyo site ID) for the browser-side JavaScript snippet that tracks anonymous visitor behavior. Only the private key needs Vercel environment variable storage.
Integration method
Klaviyo connects to V0-generated Next.js apps through server-side API routes that call Klaviyo's REST API to create profiles, subscribe users to lists, and track custom events. The private API key stays in Vercel environment variables. Your frontend forms and checkout flows call your API routes, which forward data to Klaviyo to trigger automated email sequences and SMS campaigns.
Prerequisites
- A V0 account at v0.dev with a Next.js project created
- A Klaviyo account at klaviyo.com (free for up to 500 contacts)
- A Klaviyo Private API Key from Account → Settings → API Keys in the Klaviyo dashboard
- At least one Klaviyo List ID from Lists & Segments in the Klaviyo dashboard
- A Vercel account connected to your V0 project for deployment
Step-by-step guide
Generate the Subscription Form UI with V0
Generate the Subscription Form UI with V0
Open your V0 project and prompt V0 to create the email marketing touchpoints. For a basic subscription form, describe the fields (email, first name), the submit button behavior, success and error states. For a checkout integration, describe where the marketing opt-in checkbox appears in the purchase flow and how it should interact with the order form submission. Klaviyo works best when it receives rich data — not just an email address, but first name, last name, phone number, location, and any custom properties relevant to your business. Ask V0 to include optional fields for the data Klaviyo can use for personalization. A first name field alone enables 'Hi {first_name}' personalization in welcome emails. For event tracking (purchases, product views), the UI action that triggers the event is usually an existing component — a checkout button, a product card view, a cart update. Ask V0 to add an API call to /api/klaviyo/event after the primary action (order creation) completes, so the event fires in the same user session. V0 will generate the useEffect or onSuccess callback that fires the Klaviyo event tracking call.
Create an email marketing signup widget with: a headline, email input (required), first name input (optional), and a 'Join the Newsletter' button. On submit, POST to /api/klaviyo/subscribe with email and firstName. Disable the button while loading. Show a green success banner 'Successfully subscribed!' on success. Show a red error banner with the error message on failure. Style with shadcn/ui components.
Paste this in V0 chat
Pro tip: Include a checkbox for GDPR/CAN-SPAM consent if your app serves EU or US customers. Klaviyo's API accepts a consent property on profile creation that records marketing consent with a timestamp.
Expected result: V0 generates a subscription form component with proper loading, success, and error states that calls /api/klaviyo/subscribe. The API route does not exist yet so submission will fail until step 2.
Create the Klaviyo Subscription API Route
Create the Klaviyo Subscription API Route
Create app/api/klaviyo/route.ts. This route handles POST requests from your subscription form. It calls two Klaviyo API endpoints: first the Profiles API to create or update the customer profile, then the List Subscriptions API to subscribe the profile to a marketing list. The Klaviyo API v2 (2022) uses a resource-based REST structure. To create a profile: POST to https://a.klaviyo.com/api/profiles/ with a JSON body containing a data object with type 'profile' and attributes (email, first_name, last_name, phone_number, and any custom properties). Klaviyo automatically deduplicates profiles by email — if a profile with that email already exists, the API returns the existing profile ID. To add the profile to a list: POST to https://a.klaviyo.com/api/lists/{LIST_ID}/relationships/profiles/ with the profile ID returned from the profile creation call. This triggers Klaviyo's list subscription confirmation flow if double opt-in is enabled for that list. The Authorization header uses your private API key in the format: Klaviyo-API-Key {YOUR_PRIVATE_KEY}. The revision header specifies the API version.
1import { NextRequest, NextResponse } from 'next/server';23const KLAVIYO_API_BASE = 'https://a.klaviyo.com/api';4const KLAVIYO_REVISION = '2024-02-15';56export async function POST(request: NextRequest) {7 const body = await request.json();8 const { email, firstName, lastName, properties } = body;910 if (!email) {11 return NextResponse.json({ error: 'Email is required' }, { status: 400 });12 }1314 const privateKey = process.env.KLAVIYO_PRIVATE_KEY;15 const listId = process.env.KLAVIYO_LIST_ID;1617 if (!privateKey || !listId) {18 return NextResponse.json(19 { error: 'Klaviyo credentials not configured' },20 { status: 500 }21 );22 }2324 const headers = {25 Authorization: `Klaviyo-API-Key ${privateKey}`,26 'Content-Type': 'application/json',27 revision: KLAVIYO_REVISION,28 };2930 try {31 // Step 1: Create or update the profile32 const profileRes = await fetch(`${KLAVIYO_API_BASE}/profiles/`, {33 method: 'POST',34 headers,35 body: JSON.stringify({36 data: {37 type: 'profile',38 attributes: {39 email,40 first_name: firstName || '',41 last_name: lastName || '',42 properties: properties || {},43 },44 },45 }),46 });4748 let profileId: string;49 if (profileRes.status === 409) {50 // Profile already exists — get ID from the conflict response51 const conflictData = await profileRes.json();52 profileId = conflictData.errors[0].meta.duplicate_profile_id;53 } else if (profileRes.ok) {54 const profileData = await profileRes.json();55 profileId = profileData.data.id;56 } else {57 return NextResponse.json(58 { error: 'Failed to create Klaviyo profile' },59 { status: 500 }60 );61 }6263 // Step 2: Subscribe to list64 const subscribeRes = await fetch(65 `${KLAVIYO_API_BASE}/lists/${listId}/relationships/profiles/`,66 {67 method: 'POST',68 headers,69 body: JSON.stringify({70 data: [{ type: 'profile', id: profileId }],71 }),72 }73 );7475 if (!subscribeRes.ok && subscribeRes.status !== 204) {76 return NextResponse.json(77 { error: 'Failed to subscribe to list' },78 { status: 500 }79 );80 }8182 return NextResponse.json({ success: true, profileId });83 } catch (error) {84 console.error('Klaviyo API error:', error);85 return NextResponse.json(86 { error: 'Failed to process subscription' },87 { status: 500 }88 );89 }90}Pro tip: The 409 Conflict response when a profile already exists includes the existing profile ID in errors[0].meta.duplicate_profile_id. Always handle this case to support re-subscription by users who previously unsubscribed.
Expected result: POSTing to /api/klaviyo with an email creates a Klaviyo profile and subscribes it to your list. The new profile appears in the Klaviyo dashboard under Profiles and in your list.
Create the Event Tracking API Route
Create the Event Tracking API Route
Create app/api/klaviyo/events/route.ts for tracking custom events. The Klaviyo Events API records customer behavior — purchases, product views, cart additions, form submissions — that power Klaviyo's automation flows and segmentation. To track an event, POST to https://a.klaviyo.com/api/events/ with a data object containing the event type, metric name (e.g., 'Placed Order', 'Viewed Product', 'Added to Cart'), profile attributes identifying the customer (email is required), and properties describing the event (order total, product name, product ID, etc.). For e-commerce events, Klaviyo has recommended naming conventions: 'Placed Order', 'Ordered Product', 'Viewed Product', 'Added to Cart', 'Checkout Started'. Using these standard names enables Klaviyo's pre-built flow templates to work automatically with your data. The 'Placed Order' event should include a value property (the order total) for revenue attribution — this is how Klaviyo tracks which emails drive actual revenue. Event tracking is fire-and-forget: your API route sends the event to Klaviyo and returns success without waiting for Klaviyo to process the automation trigger. The event appears in the customer's Klaviyo profile history and triggers any matching flows within seconds.
Add event tracking to the checkout success page: when the component mounts with order data, call POST /api/klaviyo/events with event name 'Placed Order', customer email, and order total. Log success silently in the background without blocking the UI. If the tracking call fails, log the error to the console but still show the order confirmation to the user.
Paste this in V0 chat
1import { NextRequest, NextResponse } from 'next/server';23const KLAVIYO_API_BASE = 'https://a.klaviyo.com/api';4const KLAVIYO_REVISION = '2024-02-15';56export async function POST(request: NextRequest) {7 const body = await request.json();8 const { email, eventName, properties, value } = body;910 if (!email || !eventName) {11 return NextResponse.json(12 { error: 'email and eventName are required' },13 { status: 400 }14 );15 }1617 const privateKey = process.env.KLAVIYO_PRIVATE_KEY;18 if (!privateKey) {19 return NextResponse.json(20 { error: 'Klaviyo private key not configured' },21 { status: 500 }22 );23 }2425 try {26 const response = await fetch(`${KLAVIYO_API_BASE}/events/`, {27 method: 'POST',28 headers: {29 Authorization: `Klaviyo-API-Key ${privateKey}`,30 'Content-Type': 'application/json',31 revision: KLAVIYO_REVISION,32 },33 body: JSON.stringify({34 data: {35 type: 'event',36 attributes: {37 metric: {38 data: {39 type: 'metric',40 attributes: { name: eventName },41 },42 },43 profile: {44 data: {45 type: 'profile',46 attributes: { email },47 },48 },49 properties: properties || {},50 value: value || 0,51 time: new Date().toISOString(),52 },53 },54 }),55 });5657 if (!response.ok) {58 const error = await response.json();59 console.error('Klaviyo event error:', error);60 return NextResponse.json(61 { error: 'Failed to track event' },62 { status: 500 }63 );64 }6566 return NextResponse.json({ success: true });67 } catch (error) {68 console.error('Klaviyo events API error:', error);69 return NextResponse.json(70 { error: 'Failed to track event' },71 { status: 500 }72 );73 }74}Pro tip: Use Klaviyo's standard e-commerce event names ('Placed Order', 'Ordered Product', 'Added to Cart', 'Viewed Product') to unlock Klaviyo's pre-built flow templates without custom configuration.
Expected result: POSTing purchase events to /api/klaviyo/events records them in the customer's Klaviyo profile and triggers any matching automated flows configured in Klaviyo.
Add Klaviyo API Key to Vercel Environment Variables
Add Klaviyo API Key to Vercel Environment Variables
Store your Klaviyo credentials in Vercel environment variables. You need two variables: KLAVIYO_PRIVATE_KEY for API authentication and KLAVIYO_LIST_ID for the list to subscribe new signups to. To get your private API key: log in to Klaviyo, go to Account (top-right) → Settings → API Keys, and create a new private API key. Give it a descriptive name like 'V0 App Integration'. Copy the key immediately — Klaviyo only shows the full key once. Private API keys start with 'pk_' in Klaviyo's current API. To get your List ID: go to Lists & Segments in Klaviyo, click on the list you want new signups added to, and copy the list ID from the URL (it is the alphanumeric string after /lists/, e.g., 'AbCdEf'). If you have not created a list yet, create one now — go to Lists & Segments → Create List → name it (e.g., 'Newsletter Subscribers') and note the ID. In Vercel Dashboard → Settings → Environment Variables, add KLAVIYO_PRIVATE_KEY with your private key value and KLAVIYO_LIST_ID with your list ID. Both should be set for All Environments. After adding the variables, redeploy your project. You can also add KLAVIYO_PUBLIC_KEY (your Klaviyo site ID, found on the same API keys page) if you plan to use Klaviyo's browser-side JavaScript tracking snippet.
Pro tip: Create separate Klaviyo lists for different signup sources (e.g., 'Website Newsletter', 'Checkout Opt-in', 'Product Waitlist') using separate KLAVIYO_LIST_ID values or accept the list ID as a parameter in your API route.
Expected result: KLAVIYO_PRIVATE_KEY and KLAVIYO_LIST_ID are set in Vercel. New form submissions create profiles in Klaviyo and appear in your list after redeployment.
Common use cases
Email Signup Form with List Subscription
Add a Klaviyo list subscription form to your landing page or checkout flow. When a user submits their email, your API route creates a Klaviyo profile and subscribes them to a marketing list. Klaviyo then sends the configured welcome email from your flow.
Create an email signup section with a headline 'Get exclusive offers', an email input field, an optional first name field, and a 'Subscribe' button. On submit, POST to /api/klaviyo/subscribe with the email and name. Show a success message 'You're on the list!' after submission. Show an error message if the email is invalid. Use shadcn/ui Input and Button components.
Copy this prompt to try it in V0
E-Commerce Event Tracking
Track customer purchase events from your checkout flow to Klaviyo. When a customer completes a purchase, send an event with order value, product details, and customer info. This data feeds Klaviyo's revenue attribution, post-purchase flows, and product recommendation engines.
After the checkout form submission succeeds and the order is created, call POST /api/klaviyo/event with event name 'Placed Order', customer email, and order details including total value and item names. Show an order confirmation page component that triggers this API call when mounted. Include a loading state while the API calls process.
Copy this prompt to try it in V0
Customer Profile Sync from Registration
When a new user registers on your Next.js app, create a matching Klaviyo profile with their data and add them to the right lists based on their preferences. This keeps your Klaviyo database in sync with your app's user database for accurate segmentation.
After a user registers with name, email, and selects their interests (checkboxes: news, deals, tutorials), POST to /api/klaviyo/profile with their data and selected interest tags. Then POST to /api/klaviyo/subscribe to add them to the appropriate Klaviyo list based on their interests. Show a 'Welcome! Check your email.' confirmation.
Copy this prompt to try it in V0
Troubleshooting
401 Unauthorized — 'Your API key is invalid' from Klaviyo
Cause: The KLAVIYO_PRIVATE_KEY environment variable contains an incorrect key, or the private key was deleted from the Klaviyo dashboard. Note that Klaviyo API v2 keys have a different format from older API keys.
Solution: In Klaviyo → Account → Settings → API Keys, verify your private API key exists and matches the value in Vercel. Klaviyo API v2 private keys start with 'pk_'. If you created the key in the old Klaviyo API, you may need to generate a new v2 API key. Update the Vercel environment variable and redeploy.
Profile is created in Klaviyo but does not appear in the list
Cause: The KLAVIYO_LIST_ID is incorrect, or the list has double opt-in enabled. With double opt-in, Klaviyo sends a confirmation email and the profile only appears in the list after the user clicks the confirmation link.
Solution: Verify the KLAVIYO_LIST_ID matches the ID shown in the list's URL in Klaviyo. In Klaviyo → Lists & Segments → your list → Settings, check whether double opt-in is enabled. For a seamless signup experience, disable double opt-in or communicate to users that a confirmation email is coming.
409 Conflict error when creating a profile that already exists
Cause: The user's email is already in Klaviyo from a previous signup or import. Klaviyo returns 409 when you try to create a profile with an email that already exists.
Solution: This is expected behavior — handle the 409 by extracting the existing profile ID from the error response (errors[0].meta.duplicate_profile_id) and proceeding to add that profile to the list. The code example in step 2 already includes this handling.
1if (profileRes.status === 409) {2 const conflictData = await profileRes.json();3 profileId = conflictData.errors[0].meta.duplicate_profile_id;4}Best practices
- Never use your Klaviyo private API key in client-side code — it grants full account access. Only use it in server-side Next.js API routes with no NEXT_PUBLIC_ prefix.
- Use Klaviyo's standard e-commerce event names (Placed Order, Added to Cart, Viewed Product) to enable pre-built automation flow templates without custom configuration.
- Always handle the 409 Conflict response when creating profiles — use the existing profile ID from the error response to subscribe the existing profile to the list.
- Include revenue value in order-related events using the value property — this enables Klaviyo's revenue attribution to show which email flows generate the most sales.
- Create separate Klaviyo lists for different customer segments (newsletter subscribers, customers, trial users) rather than adding everyone to a single list, for more targeted campaigns.
- Add the revision header to every Klaviyo API request to pin your integration to a specific API version, preventing breaking changes when Klaviyo releases new API revisions.
- Test your flows in Klaviyo by checking the Preview & Test feature before sending to real customers — verify the event data appears correctly in the flow trigger filters.
Alternatives
Mailchimp is a more general-purpose email platform that works well for content newsletters and non-e-commerce use cases, while Klaviyo specializes in e-commerce revenue attribution.
Drip offers similar e-commerce email automation to Klaviyo with a simpler pricing model, making it a good alternative for smaller stores not needing Klaviyo's full feature set.
ConvertKit is better for content creators and course sellers who need subscriber tagging and sequence automation rather than deep e-commerce event tracking.
Frequently asked questions
What is the difference between Klaviyo's public and private API keys?
The private API key (starts with 'pk_') grants full read/write access to your Klaviyo account and must only be used server-side. The public API key (Klaviyo site ID) is safe for browser-side JavaScript and is used with Klaviyo's tracking snippet for anonymous visitor identification. Never put your private key in client-side code or NEXT_PUBLIC_ environment variables.
Does Klaviyo have a free tier?
Yes, Klaviyo's free plan supports up to 500 contacts and 500 emails per month, making it sufficient for testing and small apps. Paid plans start at $20/month for up to 500 contacts with unlimited email sends. Pricing scales with your contact count, not email volume on paid plans.
What is the difference between a Klaviyo list and segment?
A Klaviyo list is a static collection of profiles that you explicitly subscribe people to via the API or forms. A segment is a dynamic filtered view of profiles based on properties and behaviors (e.g., 'all profiles who placed an order in the last 30 days'). Use lists for explicit opt-ins and segments for behavioral targeting in flows and campaigns.
How does Klaviyo's double opt-in work?
When double opt-in is enabled for a list, Klaviyo sends a confirmation email when someone is added to the list. The profile does not officially join the list until they click the confirmation link. This improves list quality and complies with email marketing regulations in some regions. You can enable or disable it per list in Klaviyo → Lists & Segments → your list → Settings.
Can I use Klaviyo with Next.js for both email and SMS?
Yes, Klaviyo supports both email and SMS campaigns and automation. SMS requires collecting phone numbers with explicit consent and enabling SMS in your Klaviyo account. The API integration is the same — include a phone_number attribute when creating profiles and Klaviyo handles both email and SMS delivery through its flow system.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation