Migrate from Klaviyo to Unitpost

A Klaviyo profile importing with its email consent and dated SMS consent, and a Klaviyo flow left to rebuild in Unitpost

Contacts, suppressions, lists, segments and more come over in one import.

What comes with you

To leave Klaviyo, paste a Klaviyo Read-Only private key into Unitpost's Migration Assistant. It imports profiles with their email consent and dated SMS consent, custom properties and location, lists and segments as today's members, bounces and manual suppressions, saved templates, drafts and your 25 most recent campaigns with Klaviyo tags converted, and your sending domain. Profiles with no email are left out. Flows, segment filters, webhooks, forms and analytics stay at Klaviyo.

DataResultWhat happens
ContactsImportsEvery profile with an email address imports with first and last name, custom properties, organization, title, locale and location (IP address excluded). Profiles with only a phone number, or no identifier at all, are left out: a contact here needs an email.
Unsubscribes and consentImportsSubscribed profiles import subscribed, with the date and method Klaviyo recorded kept as contact fields. Unsubscribed profiles import unsubscribed. Profiles that never subscribed (often checkout-only customers, whom Klaviyo can email on implied consent) import unsubscribed from marketing: campaigns skip them, transactional email still reaches them.
Suppressions (bounces and blocks)ImportsHard bounces, invalid addresses, spam complaints and manual suppressions on a profile import as suppressions. Klaviyo shows many spam complaints only as an unsubscribe, so those arrive unsubscribed rather than blocked; to block them for every kind of email, export Klaviyo's suppressed profiles and import that file too.
SMS numbers and consentImportsA profile's phone number becomes the contact's SMS number. An SMS opt-in imports only with the date Klaviyo recorded for it, and an SMS opt-out always carries over. Klaviyo requires express SMS consent, so most opt-ins arrive with their date.
ListsImportsEach list becomes a static segment with its members. Someone who unsubscribed from one list only is left out of that segment.
SegmentsPartly importsActive segments come over with today's members as fixed segments; their filters use Klaviyo's own events and properties, so rebuild the filter here if a segment should keep updating. Inactive segments are listed, and very large segments may come as names only.
TemplatesPartly importsSaved templates, draft campaigns and your 25 most recent sent email campaigns import as code-mode drafts, with exact Klaviyo tags rewritten. Klaviyo may return no body for drag-and-drop templates; those arrive empty and are listed, so export their HTML from Klaviyo.
Sending domainsPartly importsKlaviyo's sending-domain API is beta-only. The domain of your default sender address is recreated here as verifying, with new DKIM keys.
TopicsRebuildKlaviyo has no subscription topics in its API. Create topics here if you want per-category opt-outs.
WebhooksRebuildKlaviyo's API truncates webhook URLs and never returns their secrets, so the preview names your webhooks and their topics for you to add here. Only some Klaviyo plans have webhooks.
API keysRebuildKlaviyo keys stay at Klaviyo. Create Unitpost API keys instead, and delete the Klaviyo key you used for the import.
FlowsStays behindFlows don't move. The preview lists each one with its trigger and status; rebuild them in Unitpost Automations, which are in early access, or send from your code when the event happens.
Send history, analytics and formsStays behindCampaign and flow stats, events, predictive analytics, sign-up forms and older sent campaigns stay at Klaviyo. Unitpost has no hosted forms or store integrations.

Before you start

  • A Klaviyo private API key with Read-Only access: Settings → API keys → Create Private API Key, then Read-Only Key. Owners, Admins and Managers can create one. The six-character public key (site ID) can't read your data.
  • Klaviyo shows a private key once; copy it before closing the dialog.
  • If you text contacts: SMS on Unitpost is on paid plans only, and marketing texts need consent on record per contact.
  • A Unitpost API key for your app with emails:send, plus contacts:write and segments:write if your signup form adds contacts, created in API keys.
  • A second Unitpost key for the migration steps, with templates:write and webhooks:manage, saved as UNITPOST_MIGRATION_KEY. Keep it out of your app and revoke it once the move is done.
  • Access to your DNS host.

The cut-over plan

Klaviyo keeps sending until the last step, so there is no gap in delivery.

  1. Run the Migration Assistant

    Paste the Read-Only key in Settings → Migration. The preview counts what comes over and lists what doesn't: profiles with no email, never-subscribed profiles, your flows, large or inactive segments, and templates whose tags need review. Suppressions are written before contacts, so bounced and blocked addresses land suppressed.

  2. Verify your domain

    Open Domains and publish the records shown: MX and SPF on unitpost.<your domain> and a DKIM TXT under _domainkey. They don't replace Klaviyo's records, so both stay authenticated while you switch. Keep your single _dmarc record.

  3. Review tags and publish

    {{ first_name }} is now {{first_name}}, {{ person|lookup:'Favorite Color' }} becomes the imported field {{favorite_color}}, and {% unsubscribe %} becomes a link to {{unsubscribe_url}}. Filters such as |default:'friend' are dropped. Unitpost fills flat {{name}} placeholders only, so {% if %} and {% for %} blocks, event data and tags such as {% web_view %} or {{ organization.name }} stay as written for you to rewrite. Publish with PATCH /api/v1/email/templates/{id}.

  4. Rebuild segments and flows

    Recreate the segments that should keep updating in Segments as dynamic segments on contact fields. Rebuild each flow in Automations if your workspace has early access; until then, send from your code when the event happens, with POST /api/v1/email.

  5. Point signups and events at Unitpost

    Klaviyo forms keep adding profiles to Klaviyo. Send signups from your own form with POST /api/v1/contacts and add the contact to a segment where you used a list. Klaviyo's profile import upserts; here an existing contact answers 409, and PATCH /api/v1/contacts/{id or email} updates it. Events you sent to Klaviyo to start flows become direct sends.

    signup.ts
    // Before: Klaviyo
    const klaviyo = {
      Authorization: "Klaviyo-API-Key " + process.env.KLAVIYO_PRIVATE_KEY,
      revision: "2026-10-15",
      "content-type": "application/vnd.api+json",
    };
    const res = await fetch("https://a.klaviyo.com/api/profile-import", {
      method: "POST",
      headers: klaviyo,
      body: JSON.stringify({ data: { type: "profile", attributes: { email: "user@example.com", first_name: "Ada" } } }),
    });
    const { data: profile } = await res.json();
    await fetch("https://a.klaviyo.com/api/lists/" + LIST_ID + "/relationships/profiles", {
      method: "POST",
      headers: klaviyo,
      body: JSON.stringify({ data: [{ type: "profile", id: profile.id }] }),
    });
    
    // After: Unitpost
    import { Unitpost } from "unitpost";
    const unitpost = new Unitpost(process.env.UNITPOST_API_KEY);
    const { error } = await unitpost.contacts.create({ email: "user@example.com", first_name: "Ada" });
    // 409 conflict: the contact already exists. Klaviyo's call upserts; update with contacts.update.
    if (error && error.code !== "conflict") throw new Error(error.message);
    await unitpost.segments.addMember("seg_123", { contact: "user@example.com" });
  6. Check SMS consent before texting

    Numbers whose opt-in had no date arrived without consent. Record consent where the contact gave it (POST /api/v1/contacts/{id}/sms-consent) or ask them to confirm with POST /api/v1/contacts/{id}/sms-opt-in-request. Klaviyo SMS opt-outs came over and stay in force.

  7. Add webhooks

    Add each endpoint the preview listed in Webhooks. Klaviyo signs deliveries with Klaviyo-Signature; Unitpost signs each body with a whsec_ secret in svix-id, svix-timestamp and svix-signature, so update your handler.

  8. Test, move traffic, then delete the key

    Send each template with POST /api/v1/email/templates/{id}/test-send and fire POST /api/v1/webhooks/{id}/test at each endpoint. Send your next campaign from Unitpost to a small segment first, then to everyone. Turn each Klaviyo flow off once its replacement runs here, then delete the Klaviyo key.

Name changes

Same ideas, new names

KlaviyoUnitpostNote
ProfileContact
ListSegment (static)
SegmentSegment (dynamic)Members come over; rebuild the filter.
Custom propertyContact field
Never subscribedUnsubscribed from marketingTransactional email still reaches them.
Suppressed profileSuppression
CampaignCampaign
FlowAutomationsIn early access.
Metric (event)No launched equivalentSend from your code when the event happens.
{{ first_name }}{{first_name}}
{% unsubscribe %}A link to {{unsubscribe_url}}

API calls

Klaviyo APIUnitpost APINote
POST /api/profile-importPOST /api/v1/contactsKlaviyo's call upserts; here an existing contact answers 409 and you update it with PATCH.
GET /api/profilesGET /api/v1/contacts
PATCH /api/profiles/{id}PATCH /api/v1/contacts/{id or email}
GET /api/listsGET /api/v1/segments
POST /api/lists/{id}/relationships/profilesPOST /api/v1/segments/{id}/contacts
POST /api/profile-subscription-bulk-create-jobsPOST /api/v1/contacts/{id}/sms-consentEmail consent is the contact's unsubscribed flag; SMS consent is recorded per contact.
POST /api/profile-suppression-bulk-create-jobsPOST /api/v1/suppressions
POST /api/eventsNo launched equivalentEvents start Klaviyo flows. Send the email from the code that fires the event, with POST /api/v1/email.
POST /api/campaignsPOST /api/v1/email/campaigns
POST /api/webhooksPOST /api/v1/webhooks
Authorization: Klaviyo-API-Key … plus a revision headerAuthorization: Bearer <api key>

Webhook events

Klaviyo eventUnitpost eventNote
event:klaviyo.received_emailemail.delivered
event:klaviyo.opened_emailemail.opened
event:klaviyo.clicked_emailemail.clicked
event:klaviyo.bounced_emailemail.bounced
event:klaviyo.marked_email_as_spamemail.complained
event:klaviyo.dropped_emailemail.suppressed
event:klaviyo.subscribed_to_email_marketing, event:klaviyo.unsubscribed_from_email_marketingcontact.updated
event:klaviyo.sent_smssms.sent
event:klaviyo.received_smssms.received
event:klaviyo.failed_to_deliver_smssms.failed
Every topicAdd the endpoint by handKlaviyo's API truncates webhook URLs, so the importer lists your webhooks instead of copying them.

Before you cancel Klaviyo

  • Export the profiles with no email address if you need them; the importer leaves them out.
  • Export your suppressed profiles (Audience → Profiles → View suppressed profiles) and import them as suppressions, so complaints shown as unsubscribes stay blocked.
  • Write down each flow with its trigger, filters and timing.
  • Note the filters of the segments you'll rebuild; only their members came over.
  • Delete Klaviyo's DNS records once no mail goes through Klaviyo. Keep _dmarc and Unitpost's records.
  • Delete the Klaviyo API key you used for the import.

Facts about Klaviyo checked 2026-11-06. Providers change their APIs and plans; check their docs before you rely on a detail.

Questions about leaving Klaviyo

Why are some profiles missing?

Contacts here need an email address, so Klaviyo profiles with only a phone number, or with no identifier, are left out. The preview counts them. Export them from Klaviyo if you need them.

Why did customers who never subscribed come over unsubscribed?

Klaviyo can email profiles that never subscribed, such as checkout-only customers, on implied consent, and counts them on your bill. The importer brings them over unsubscribed from marketing: transactional email still reaches them, and campaigns go only to people who subscribed.

Do my SMS subscribers come over?

Yes, when the profile has an email address. An SMS opt-in carries over with the date Klaviyo recorded for it, and an opt-out always carries over. Texting from Unitpost needs a paid plan.

Do my flows come over?

No. The preview lists them with their triggers. Rebuild them in Automations, which are in early access, or send from your code when the event happens.

Leaving Klaviyo?

Preview the migration on the free tier

Verify a domain, paste an API key, and see what would come across before anything is written.