Migrating From Customer Io
PostHogOfficialPublished by PostHog
- Skill
- Workflow
- Automation and integrations
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a structured process for migrating marketing automations from Customer.io to PostHog. It uses MCP servers to read campaign data, templates, and sender identities from Customer.io and imports them into PostHog as draft workflows. The process emphasizes safety by excluding customer profile data, performing dry-run previews to identify necessary manual adjustments, and ensuring that imported drafts remain disabled until the user reviews and configures them.
What it does
- Lists and reads Customer.io campaigns, templates, and sender identities.
- Imports automations into PostHog as draft workflows.
- Performs dry-run previews to validate imports before final creation.
- Maps Customer.io sender identities and segments to PostHog equivalents.
- Copies email images to the PostHog media library to ensure continuity.
- Generates notes for manual input, unsupported features, or approximated logic.
When to use it
- When a user requests to migrate, move, switch, or copy automations from Customer.io to PostHog.
- When preparing to transition marketing automation infrastructure to PostHog.
- When you need to validate if specific Customer.io campaign settings are compatible with PostHog workflows.
Advantages
- Prevents privacy risks by explicitly excluding customer profiles and event data.
- Provides clear feedback on what requires manual configuration via 'needs_input' notes.
- Ensures email images remain functional by migrating them to the PostHog media library.
- Allows for iterative re-running of imports to refine drafts before final activation.
Usage examples
- “Migrate all my active campaigns from Customer.io to PostHog.”
- The agent will list the active campaigns from the specified Customer.io workspace, perform a dry-run import, and present a report of the drafts created along with any required manual inputs.
- “I need to move my welcome email automation to PostHog.”
- The agent will fetch the specific campaign, preview the import, and highlight any missing templates or segments that the user must configure in the resulting PostHog draft.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/posthog/migrating-from-customer-io/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip migrating-from-customer-io.zip -d .agents/skills
Documentation by PostHog
Migrating from Customer.io
PostHog imports Customer.io automations as draft workflows. Drafts never send. A person reviews each one, fixes what the import could not carry over, and enables it.
You move the data between two MCP servers. Read from Customer.io's MCP (or the cio CLI), and pass what it returns unchanged to PostHog's workflows-import-customerio. PostHog does the translation, so do not rebuild workflows yourself with workflows-create.
Never move people
Pass automations, templates, sender identities and segment names only. Never fetch or pass customer profiles, segment members, deliveries or event data. The people are usually in PostHog already, and copying them is a privacy risk the user did not ask for.
Keep message content out of your context
Email templates are large, often tens of kilobytes of HTML each. When the PostHog project stored a Customer.io App API key (teams that ran the opt-out import have one), PostHog reads the message content from Customer.io itself. So pass the automation graph first, and read templates only if PostHog asks for them.
Steps
Confirm the PostHog project. The drafts go into the PostHog MCP's active project. Read it with
project-get, tell the user its name and id, and ask whether the drafts should go there. If not, switch withswitch-projectbefore you read anything from Customer.io.Find the environment. Ask which Customer.io workspace to migrate if there is more than one. Its id is the
environment_idin every path below.List the automations.
GET /v1/environments/{environment_id}/campaignswith the Customer.io read tool (cio_read_apion the MCP,cio apion the CLI). It returns 20 per page throughpage(0-based): read every page until one returns fewer than 20. Show the user the list with each automation's state, and ask which to move. Running automations are the usual answer; skip archived ones unless asked.Read each automation.
GET /v1/environments/{environment_id}/campaigns/{campaign_id}. Keep the response'scampaignobject and its top-levelactionsarray.campaign.actionsdoes not exist.Read senders and segments.
GET /v1/environments/{environment_id}/identities?type=emailforsender_identities(id,email,name), and the segment list forsegments(id,name,typeonly). PostHog matches each email to the PostHog sender with the same address, and names the segments in its notes.Preview without templates. Call
workflows-import-customeriowithsource: "customerio",dry_run: true,campaigns: [{campaign, actions}],sender_identitiesandsegments. Up to 50 automations per call.Read templates only if needed. Go by each automation's notes, not by
content_fetchedalone:trueonly means PostHog read some content, and one automation can still miss its messages. For every automation whose notes say message content was not part of the import, or that PostHog could not read its messages, read the template of every action that has atemplate_idwithGET /v1/environments/{environment_id}/templates/{template_id}, keep thetemplateobject, and preview again withcampaigns: [{campaign, actions, templates}]. Read templates one at a time: listing every template in the workspace times out on large ones. Tell the user that Import from Customer.io on the Workflows Opt-outs tab stores their App API key, which skips this step next time.Collect snippets and layouts. If a note names a Customer.io snippet, or a template body you read contains
{{ snippets.name }}, read the workspace snippets and pass them assnippets({name: content}). When PostHog read the content itself, it read the snippets too, and a note names any it could not find. If a note names a layout, readGET /v1/environments/{environment_id}/layouts/{layout_id}and pass itsbodyaslayouts({layout_id: body}).Report the preview. For each automation, give its status and its notes, grouped by level (see below). Ask before importing anything with status
failed; itsvalidation_errorssay why the draft would be rejected.Import. Call again with
dry_run: falseand the same arguments. Share each draft's link (/workflows/{workflow_id}) and theneeds_inputnotes again, since the team must handle those before enabling.
When you are unsure of an endpoint's parameters, ask for that one endpoint's schema (cio_schema with resource.method). A whole resource's schema costs tens of thousands of tokens.
Reading the notes
| Level | Meaning | What to tell the user |
|---|---|---|
needs_input | The draft needs a setting only the team can give: an email sender, a trigger cohort, a branch condition | These block enabling. List them per workflow. |
approximated | The step works differently in PostHog, for example a wait capped at 30 days | Worth a look before enabling. |
unsupported | The setting has no PostHog equivalent and was left out, for example an SMS step | Decide whether to rebuild it another way. |
The same notes appear in the PostHog app on the imported workflow, and workflows-import-notes returns them for one workflow.
Senders and categories
An email gets the PostHog email sender with the same address as its Customer.io sender. When no PostHog sender has that address, a needs_input note names it, so the user can add it as a sender. An email's subscription topic, or the automation's topic when the email has none, becomes the PostHog message category the opt-out import made from that topic. When the topics were not imported, a note says so.
Images
The import copies email images hosted by Customer.io into the project's media library, so they keep loading after the Customer.io account closes. That needs the uploaded_media:write scope. If a note says images still load from Customer.io's servers, ask the user to reconnect the PostHog MCP and allow uploading media, then import again.
Segments and cohorts
Customer.io segments do not import. An automation triggered by a segment imports with an empty trigger and a needs_input note. If the user already has a PostHog cohort that matches a segment, pass segment_cohorts: {"<segment id>": <cohort id>} and the import uses it for that segment's triggers and conditions. A cohort trigger needs a realtime cohort. A static segment gets its own note: workflows cannot check a static cohort, so the user rebuilds it as conditions.
Push notifications
Push steps import as unsupported notes, and device tokens never move with the import. If the user sends push from Customer.io, tell them that a device reaches PostHog only when its app registers it with the PostHog SDK, or when they import their exported device tokens into a PostHog push channel. That token import is a separate step the user starts, because tokens are people data.
Re-running
Importing an automation again replaces its draft, so you can re-run after fixing something in Customer.io. Edits the team made to that draft since are lost, so ask before you re-import. A workflow that someone already enabled, or that the user can't edit, is never changed: its result is skipped.
After the import
Do not enable the drafts. Point the user at the needs_input notes, then hand over to the building-workflows skill for testing and enabling, which needs the user's explicit sign-off.
Files
- ATTRIBUTION.mdAdded by AutoGineer871 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md8.0 KiB
ATTRIBUTION.md
Attribution
This folder is an unmodified copy of the Agent Skill "migrating-from-customer-io", distributed by AutoGineer (https://autogineer.com/agent-skills).
Commit: a5d30e775d092e16e198f7b582f1f66dac2cee45
Published by: PostHog (https://github.com/PostHog)
Publisher: PostHog (https://github.com/PostHog)
Licence: MIT License (https://github.com/PostHog/skills/blob/a5d30e775d092e16e198f7b582f1f66dac2cee45/LICENSE)
Licence evidence: repository licence LICENSE
Retrieved: 2026-11-07
The skill's own files are unmodified copies. AutoGineer is not affiliated with or endorsed by the author or the publisher.
Files added by AutoGineer
ATTRIBUTION.md: this file, written by AutoGineer
LICENSE: copied from LICENSE
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs an MCP server, Needs an API key
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- PostHog
- Publisher
- PostHog (Official)
- Source
- PostHog/skills · skills/omnibus/migrating-from-customer-io · commit a5d30e7
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by PostHog or PostHog.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download migrating-from-customer-io.zip