> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrate from Klaviyo to Orbit: CDP profiles, events, segments, and journeys

> Map your Klaviyo integration onto Orbit step by step — private API keys, profiles, lists, segments, events, flows, campaigns, and attribution — with worked curl examples for profile ingest and journey creation.

# Migration from Klaviyo to Orbit

Klaviyo ships an email/SMS marketing product with a customer-profile store: profiles with custom properties, static lists, rule-based segments, an event stream (Placed Order, Viewed Product), flow automations, campaigns, and metric-driven attribution. Orbit ships the same building blocks natively — a CDP contact and event layer, segments, blast/drip/journey campaigns, and conversion-goal attribution — next to the messaging channels themselves.

This guide maps every Klaviyo concept onto its Orbit equivalent and walks the manual, code-level migration. The migration proceeds incrementally — Klaviyo keeps running until you redirect your own integration, and Orbit never sends messages through Klaviyo.

## Concept mapping

| Klaviyo concept | Orbit equivalent | Notes |
| - | - | - |
| Private API key (`pk_…`, read-only scope is enough to import) | Orbit API key (`dv_live_sk_xxxx`) sent as `X-API-Key` | Server-side only — never ship either key to a browser bundle |
| Profile (email, phone, custom properties) | Contact in the Audience hub | Imported as Orbit contacts with email, phone, and custom properties preserved |
| List | Static list | Membership is explicit, exactly as in Klaviyo |
| Segment (rule-based) | CDP segment | The definition is preserved; see [CDP segments](/guides/cdp-segments) |
| Event (Placed Order, Viewed Product, custom metrics) | CDP event stream | Ingest one event per row; see the event debugger for a live tail at [CDP event debugger and DLQ](/guides/cdp-event-debugger-and-dlq) |
| Flow (triggered automation) | Campaign journey (`type: "journey"`) or Flows | Marketing automations become campaign journeys; inbound conversational automations become Flows |
| Campaign (one-off or scheduled send) | Campaign (`type: "blast"` / `type: "drip"`) | Same launch-and-audience model; see [campaigns end-to-end](/guides/campaign-end-to-end) |
| Metric → attributed revenue | Conversion goal + attribution | See [conversion goals and attribution](/guides/conversion-goals-attribution) |
| Email/SMS template | Orbit message template | Imported templates land in the template library with validation on save |
| SMS sender (your sending number/profile) | Sender in the senders hub | Resolve senders through the sender chain; numbers port over like any other carrier — see [port numbers](/guides/port-numbers) |

***

## Prefer the wizard?

The **Settings → Import → Klaviyo** wizard ports your account's configuration — lists, segments, templates, flows, and profiles — with a read-only private API key (`pk_…`). It dry-runs counts and known conflicts before anything is written, runs server-side, and wipes the key when the job finishes. The full walkthrough is the [assisted import wizard](/guides/assisted-import-wizard) guide.

The rest of this page covers the manual, code-level migration for teams who want control over each step.

***

## Step 1: Create your Orbit account

1. Sign up at [orbit.devotel.io/signup](https://orbit.devotel.io/signup)
2. Generate an API key at **Settings → API Keys**
3. Note your key prefix: `dv_live_sk_xxxx`

Authenticate every request with the `X-API-Key` header. Keep both the Klaviyo `pk_…` key and the Orbit key server-side — browser code must never carry either.

***

## Step 2: Export profiles and events from Klaviyo

Read profiles with `GET /api/profiles` and events with `GET /api/events` against Klaviyo's REST API, authenticated with your `pk_…` key. Paginate with the `page[cursor]` links Klaviyo returns, and collect `email`, `phone_number`, and every custom property you rely on per profile.

For events, filter by metric name (Placed Order, Viewed Product, custom metrics) and capture the event timestamp plus the profile it belongs to — you need both to rebuild attribution on Orbit.

***

## Step 3: Ingest profiles and events into the CDP

Create the contact first, then post its events against the contact id. Batch this loop: create-or-update contacts, then walk the exported events and post each one.

**Worked curl — profile ingest (contact upsert):**

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/contacts" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "jane@example.com",
    "phone": "+14155552671",
    "first_name": "Jane",
    "custom_fields": {
      "klaviyo_profile_id": "01HGK…",
      "lifetime_value": 1240.5,
      "preferred_channel": "email"
    }
  }'
# => { "data": { "id": "ct_…" } }
```

**Worked curl — event ingest (CDP event stream):**

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/cdp/events" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "event": "placed_order",
    "contact_id": "ct_…",
    "value_cents": 4999,
    "properties": { "order_id": "ORD-1092", "sku_count": 3 }
  }'
```

Name events in `snake_case` and keep the name byte-for-byte stable — a conversion goal later matches the exact string, so `placed_order` and `Placed Order` are different events. Verify ingest on the [CDP event debugger](/guides/cdp-event-debugger-and-dlq) live tail before you run the full backfill.

***

## Step 4: Rebuild segments as CDP segments

Static lists become static lists directly. Rule-based segments are re-built as CDP segments with the definition preserved — membership re-evaluates from your ingested CDP data rather than a frozen export. Walk each Klaviyo segment's filter (metric counts, property predicates, list membership) and re-express it against the CDP contact and event fields you ingested in Step 3.

The workspace for this is [CDP segments](/guides/cdp-segments). If you want a preview of how much of the export maps cleanly, run it through the [CDP segment migration calculator](/guides/cdp-segment-migration-calculator) first.

***

## Step 5: Convert flows into campaign journeys

Klaviyo flows (welcome series, abandoned cart, win-back) map onto campaign journeys: a trigger enrolls contacts one at a time, and a graph of messages, waits, and branches walks them through. Create the campaign as `type: "journey"` and author the graph on the journey canvas — the builder and the API write the same `variables.journeyDefinition`.

**Worked curl — journey campaign creation:**

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/campaigns" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Welcome series — migrated from Klaviyo flow",
    "type": "journey",
    "audience_type": "all",
    "message_template": "Welcome journey for new signups"
  }'
# => { "data": { "id": "cmp_…" } }
```

Then draw the graph on the canvas (or `PATCH` the campaign's `variables.journeyDefinition`) with a trigger whose event name matches the one you ingested in Step 3 — e.g. enrollment on `placed_order` for a post-purchase flow, or on your signup event for the welcome series.

Inbound conversational automations (chat-style flows over your inbound messages) are Flows, not journeys — the [build your first flow](/guides/build-first-flow) guide covers that canvas. For full journey mechanics (validation gates, simulator, per-node analytics) see [build, simulate, and launch a campaign journey](/guides/campaign-journey-builder).

***

## Step 6: Re-create suppression and opt-out handling

Do not carry suppression as a segment filter — rebuild it as a real suppression surface so every channel honors it:

* Import Klaviyo's suppressed/unsubscribed profiles into [opt-out lists](/guides/opt-out-lists), and route your sending through [message suppression](/guides/message-suppression) so blast, drip, and journey sends all refuse suppressed contacts.
* Map Klaviyo's per-channel unsubscribe flags to the matching Orbit opt-out list per channel instead of one global flag.

Suppressions on Klaviyo stop applying the day you switch sends to Orbit, so treat this as a launch gate, not a cleanup task.

***

## Step 7: Re-map attribution from Klaviyo metrics to conversion goals

Klaviyo attributes revenue to the metric a campaign drove (Placed Order within X days). On Orbit you create a **Custom event** conversion goal named for the event you ingested (e.g. `placed_order`), set the lookback window, and pick the model — first-touch, last-touch, or linear. The goal then joins conversions back to the campaign each contact most recently touched.

The full walk is [conversion goals and attribution](/guides/conversion-goals-attribution); the derived revenue view per campaign is [ROAS and revenue attribution](/guides/campaign-roas-attribution). Re-build every metric you report on before cut-over so the numbers you compare against Klaviyo exist on Orbit day one.

***

## Step 8: Dual-send cut-over and rollback

Run Klaviyo and Orbit in parallel and shift traffic by cohort:

1. **Phase 1 — dry run.** Orbit campaigns send to an internal test segment only; Klaviyo serves all production traffic.
2. **Phase 2 — split.** Route a small production cohort (10%) through Orbit, the rest through Klaviyo; compare delivery, engagement, and attributed conversions against the same metrics on both sides.
3. **Phase 3 — full send.** Move all production traffic to Orbit once the numbers hold.

**Rollback.** If phase 2 or 3 regresses, revert your integration layer back to Klaviyo — the import wizard's dry-run never touched your Klaviyo data, and a wizard-run import can be rolled back from the import history surface. Keep the Klaviyo `pk_…` key alive until parity is confirmed; release it from your secret store after decommission.

***

## Migration checklist

* [ ] Create Orbit account and generate an API key
* [ ] Run the Import → Klaviyo wizard, or export profiles/events manually
* [ ] Ingest profiles, then backfill events into the CDP event stream
* [ ] Rebuild segments as CDP segments
* [ ] Convert flows into journey campaigns (or Flows for inbound)
* [ ] Re-create suppression via opt-out lists + message suppression
* [ ] Re-map metrics to conversion goals
* [ ] Port sending numbers (SMS) or register senders — see [port numbers](/guides/port-numbers)
* [ ] Dual-send 10%, compare, cut over 100%
* [ ] Decommission Klaviyo and release the `pk_…` key

<Tip>
  Need help? Our solutions team offers free migration support for customers moving from Klaviyo. Contact [migrate@devotel.io](mailto:migrate@devotel.io).
</Tip>
