---
title: "Migrate from Kit to Unitpost"
description: "Leave Kit in order: import subscribers, tags, bounces, broadcasts and sequence emails with a V4 key, rebuild segments and automations, then move signups."
url: https://www.unitpost.com/migration/kit
section: Migration
updated: 2026-11-07
---
# Migrate from Kit to Unitpost

To leave Kit (formerly ConvertKit), paste a Kit V4 API key into Unitpost's Migration Assistant. It imports subscribers with their state and custom fields, tags as segments, bounces and complaints as suppressions, drafts, your 25 most recent broadcasts and every sequence email as templates with Liquid converted, sending addresses and webhooks. Unconfirmed subscribers are left out. Kit segments arrive as names only, and visual automations, forms, landing pages and commerce stay at Kit.

Contacts, suppressions, tags, templates and more come over in one import.

## What comes with you

| Data | Result | What happens |
| --- | --- | --- |
| Contacts | Imports | Active, cancelled, bounced and complained subscribers import with `first_name` and their custom fields. Kit has no last name field, so a custom field keyed `last_name` (or labelled Last name) becomes the last name. |
| Unsubscribes and consent | Imports | Cancelled, bounced and complained subscribers import unsubscribed. Unconfirmed subscribers (`inactive` in Kit's API) never confirmed double opt-in, so Kit can't email them either, and they are left out. |
| Suppressions (bounces and complaints) | Imports | Bounced and complained subscribers also import as bounce and complaint suppressions. Kit keeps no separate suppression list; the subscriber's state is the record. |
| Tags | Imports | Each tag becomes a static segment with its members. |
| Templates | Partly imports | Draft broadcasts, your 25 most recent sent broadcasts and every sequence email import as code-mode drafts, with snippets inlined and exact Liquid placeholders rewritten. Kit doesn't share its email template layouts, so a broadcast on a Classic template arrives without that template's header and footer. |
| Sending domains | Partly imports | Kit has no domains API. Domains come from your Kit sending addresses and are recreated as verifying with new DKIM keys. |
| Webhooks | Partly imports | Webhook endpoints and legacy webhooks import switched off with a new signing secret. Subscriber events map to `contact.*`, `email.bounced` and `email.complained`; form, sequence, purchase and broadcast events are not mapped. Kit doesn't offer webhooks on every plan. |
| Segments | Rebuild | Kit segments are saved filters, and Kit's API returns only their names, with no members and no filter. The preview lists them; rebuild each one as a dynamic segment. |
| Topics | Rebuild | Kit has no topics or subscription preferences. Create topics here if you want per-category opt-outs. |
| API keys | Rebuild | Kit keys stay at Kit. Create Unitpost API keys instead, and delete the Kit key you used for the import. |
| Send history and analytics | Stays behind | Broadcast stats, subscriber activity and sent broadcasts older than the 25 most recent stay at Kit. |
| Automations | Stays behind | Visual automations and rules aren't readable through Kit's API. Sequences are listed in the preview and their emails come over as templates; rebuild the order and delays in Unitpost Automations, which are in early access, or send from your code. |
| Forms, landing pages and commerce | Stays behind | Forms, landing pages, products, paid newsletters, purchases and recommendations stay at Kit. Unitpost has no hosted forms and doesn't bill readers. |

## Before you start

- A Kit V4 API key: Settings → Developer → Add a new key, under V4 API Keys. V3 keys (`api_key` and `api_secret`) don't work.
- Kit's docs describe no read-only API key. The importer only reads; delete the key in Kit when the move is done.
- If Kit refuses a key you just made, check Billing in Kit: Kit answers the same way for a bad key and for an account whose plan or trial doesn't include API access right now.
- 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](/dashboard/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

### 1. Run the Migration Assistant

Paste the V4 key in [Settings → Migration](/dashboard/settings/migration). The preview lists the unconfirmed subscribers it leaves out, your Kit segments and sequences, and the templates whose Liquid needs review. Suppressions are written before contacts, so bounced and complained addresses land unsubscribed.

### 2. Verify your domain

Kit's API shares sending addresses, not DNS records. Open [Domains](/dashboard/email/domains) and publish the records shown: MX and SPF on `unitpost.<your domain>` and a DKIM TXT under `_domainkey`. They don't replace Kit's records, so both stay authenticated. Keep your single `_dmarc` record.

### 3. Review Liquid and publish

`{{ subscriber.first_name }}` is now `{{first_name}}` and `{{ unsubscribe_url }}` is `{{unsubscribe_url}}`; custom fields map by key, and filters such as `| default:` are dropped. Unitpost fills flat `{{name}}` placeholders only, so `{% if %}` blocks and Kit-only tags such as `{{ address }}` stay as written for you to rewrite. Publish with `PATCH /api/v1/email/templates/{id}`.

### 4. Rebuild segments as filters

Recreate each Kit segment in [Segments](/dashboard/segments) as a dynamic segment on contact fields. Your tags already came over as static segments with their members.

### 5. Rebuild sequences

Each sequence email is a template named after its sequence and position. Rebuild the order and delays in Automations if your workspace has early access; until then, send them from your code on the same schedule.

### 6. Point signups at Unitpost

Kit forms and landing pages keep adding subscribers to Kit. Send signups from your own form with `POST /api/v1/contacts` and add the contact to a segment where you used a tag. Kit's create call updates an existing subscriber; here an existing contact answers `409`, and `PATCH /api/v1/contacts/{id or email}` updates it.

```ts
// Before: Kit
const kit = { "X-Kit-Api-Key": process.env.KIT_API_KEY, "content-type": "application/json" };
await fetch("https://api.kit.com/v4/subscribers", {
  method: "POST",
  headers: kit,
  body: JSON.stringify({ email_address: "user@example.com", first_name: "Ada" }),
});
await fetch("https://api.kit.com/v4/tags/" + TAG_ID + "/subscribers", {
  method: "POST",
  headers: kit,
  body: JSON.stringify({ email_address: "user@example.com" }),
});

// 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. Kit'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" });
```

### 7. Re-key and enable webhooks

Kit signs deliveries with `X-Kit-Signature`. Unitpost signs each body with a `whsec_` secret in `svix-id`, `svix-timestamp` and `svix-signature`. Rotate the secret in [Webhooks](/dashboard/webhooks) to see it, update your handler, then set `status` to `enabled`. Unitpost also sends per-message events Kit doesn't, such as `email.delivered` and `email.opened`.

### 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 broadcast from Unitpost as a campaign to a small segment first, then to everyone, and delete the Kit key.

## Same ideas, new names

| Kit | Unitpost | Note |
| --- | --- | --- |
| Subscriber | Contact |  |
| Tag | Segment (static) |  |
| Segment | Segment (dynamic) | Rebuilt by hand. |
| Custom field | Contact field |  |
| Cancelled subscriber | Unsubscribed contact |  |
| Unconfirmed subscriber | Not imported |  |
| Broadcast | Campaign |  |
| Sequence | Automations | In early access. |
| Visual automation | Automations | In early access. |
| Snippet | No equivalent | Inlined into templates on import. |
| Email template (layout) | Template | Layouts aren't readable; rebuild the design. |
| `{{ subscriber.first_name }}` | `{{first_name}}` |  |

## API calls

| Kit API | Unitpost API | Note |
| --- | --- | --- |
| POST /v4/subscribers | POST /api/v1/contacts | Kit's call upserts; here an existing contact answers 409 and you update it with PATCH. |
| GET /v4/subscribers | GET /api/v1/contacts |  |
| PUT /v4/subscribers/{id} | PATCH /api/v1/contacts/{id or email} |  |
| GET /v4/tags | GET /api/v1/segments |  |
| POST /v4/tags/{tag_id}/subscribers | POST /api/v1/segments/{id}/contacts |  |
| GET /v4/custom_fields | GET /api/v1/contact-fields |  |
| POST /v4/broadcasts | POST /api/v1/email/campaigns |  |
| GET /v4/sequences | Automations | In early access. |
| POST /v4/webhook_endpoints | POST /api/v1/webhooks |  |
| `X-Kit-Api-Key` header | `Authorization: Bearer <api key>` |  |
| No transactional API | POST /api/v1/email | Receipts and resets go to the same contacts as campaigns. |

## Webhook events

| Kit event | Unitpost event | Note |
| --- | --- | --- |
| subscriber.created | contact.created |  |
| subscriber.activated, subscriber.unsubscribed | contact.updated |  |
| subscriber.custom_field_value_updated | contact.updated |  |
| subscriber.tag_added, subscriber.tag_removed | segment.joined, segment.left | The importer maps them to `contact.updated`; the segment events are the closer match. |
| subscriber.bounced | email.bounced |  |
| subscriber.complained | email.complained |  |
| subscriber.subscribed_to_form, subscriber.added_to_sequence, subscriber.sequence_completed | No equivalent | Not mapped by the importer. |
| broadcast.sent | email.sent | Ours fires per recipient. Not mapped by the importer. |
| Legacy: subscriber_activate, subscriber_unsubscribe, subscriber_bounce, subscriber_complain | contact.updated, email.bounced, email.complained | Legacy webhooks map the same way as the current ones. |

## Before you cancel Kit

- Export subscribers as CSV, including unconfirmed ones, which the importer leaves out.
- Note who is in each Kit segment if you'll rebuild it; Kit's API can't list segment members.
- Write down each visual automation and rule, with its trigger and timing.
- Move or close your products and paid newsletters; Unitpost doesn't bill readers.
- Delete Kit's DNS records once no mail goes through Kit. Keep `_dmarc` and Unitpost's records.
- Delete the Kit API key you used for the import.

Facts about Kit checked 2026-11-05.

## FAQ

### Why are my Kit segments empty?

Kit's API returns a segment's name and nothing else: no members and no filter. The importer lists them so you can rebuild each as a dynamic segment. Tags are different: they came over as static segments with their members.

### Kit rejected my key. Why?

Usually it's a V3 key. Kit's V3 `api_key` and `api_secret` don't work on its V4 API, which is the only one that shares segments, templates and webhooks. Create a key under V4 API Keys in Settings → Developer. Kit also refuses API calls when an account's plan or trial doesn't include API access at that moment, so check Billing too.

### Do my sequences come over?

Their emails do, as templates named after the sequence and position. The order, delays and send days don't, because Unitpost has no sequence object. Rebuild them in Automations, which are in early access, or send from your code.

### Can Unitpost sell my products or paid newsletter?

No. Unitpost doesn't bill readers. Keep products and paid subscriptions in Kit or your payment tool, and send to paying readers from a segment.

## Related

- [Migrate from beehiiv to Unitpost](https://www.unitpost.com/migration/beehiiv): Leave beehiiv in order: copy the suppression list by hand, import subscribers, publications, segments and posts, rewrite merge tags, then move signups.
- [Migrate from Klaviyo to Unitpost](https://www.unitpost.com/migration/klaviyo): Leave Klaviyo in order: import profiles, consent, lists, segments and templates with a Read-Only key, rebuild flows, then move your signups and sends.
- [Migrate from Resend to Unitpost](https://www.unitpost.com/migration/resend): Leave Resend in order: copy its suppression list, import contacts, topics and templates, re-verify DNS, swap the SDK and re-key svix webhooks.
