Migrate from Mutiny
Rebuild your personalized experiences on Croct.
Mutiny discontinued its website personalization product on April 8, 2026, and relaunched as an AI tool for generating GTM content. Teams that ran account-based personalization on their website need a different platform.
This migration is different from most because Mutiny was largely no-code: there is no SDK to swap out and no variant entries to port. You’re moving a set of experiences, the audiences behind them, and a workflow.
In this tutorial, you will map Mutiny’s concepts to ours, connect your firmographic provider (if you need one), and rebuild your experiences as experiments you can measure.
How the concepts map
| Mutiny | Croct | Notes |
|---|---|---|
| Segment | Audience | Real-time evaluation |
| Experience | Experience | Same concept |
| Personalization (element edit) | Slot content | The main workflow changes, covered below |
| Firmographic data layer | Integrations | You choose the provider |
| Visitor data | User profile | Built in, real time |
| Goal | GoalCompleted event | Typed event catalog |
| A/B test | Experiment | Bayesian, unsampled |
The one significant difference is how changes reach the page. Mutiny is installed through a tag manager, letting a marketer click any element on a live page and change it. We require a one-time developer integration that connects your components to slots, which makes server-side, flicker-free delivery possible.
After that integration, the day-to-day workflow is the same as Mutiny’s. Audiences, content, preview, approval, and scheduling all happen in the platform, with no per-experience deployment.
In practice, most Mutiny implementations personalized a small number of high-impact areas, such as the hero, logo cloud, testimonials, and CTAs. Those map directly to slots. If your workflow depended on editing arbitrary elements anywhere on the page, plan for that adjustment.
Before you start
You need:
- A Croct account with a workspace and application set up.
- Access to your CMS and your frontend repository.
- Your firmographic provider’s tag still active on the site, if you were using it.
- A list of your old Mutiny experiences, their audiences, and the pages they run on.
Most Mutiny implementations used a fraction of the capability. Before writing any code, list your old experiences, the audience behind each one, and the elements each one changes. Teams routinely find that a dozen configured experiences come down to three that actually make sense right now.
Migrate your experiences
The migration follows the same flow as any other experiment, with your Mutiny inventory as the starting point.
Connect your firmographic provider
We integrate with the mail account intelligence providers. If you were using any of them, you can keep the same strategy. The tool that identifies anonymous companies does not change:
| Provider | Guide |
|---|---|
| 6sense | Integration guide |
| Demandbase | Integration guide |
| ZoomInfo | Integration guide |
The provider resolves the visitor to a company, the firmographic attributes land on the visitor profile, and audiences read them from there.
Firmographic providers take a moment to resolve a visitor, so account-level data is usually not available on the very first pageview of a session. Plan account-based experiences to activate on subsequent pageviews, and use campaign source, referrer, and location for the first impression. This behaved the same way in Mutiny.
Integrate the SDK
Run the CLI in your project:
npx croct@latest initThe CLI detects your framework, installs the SDK, wires the provider, and generates the type definitions. This is additive: nothing changes on your site until you fetch a slot, so you can merge it safely before migrating any experience.
Map personalized elements to slots
Group the elements each Mutiny experience changed into slots, one per area of the page:
| Mutiny experience changed | Slot |
|---|---|
| Headline, subheadline, CTA label | home-hero |
| Customer logos | logo-cloud |
| Testimonials or case studies | social-proof |
| Pricing page CTA | pricing-cta |
Then, for each slot:
Add the slot to your project:
npmnpx croct@latest add slotThe CLI generates the type definitions and downloads the default content for use as an automatic fallback.
Fetch the content where the component renders, passing your CMS content as the fallback:
components/HomeHero.jsx1234567891011121314151617export default async function HomeHero() { const {content} = await fetchContent('home-hero', { fallback: { title: 'Ship faster with confidence', subtitle: 'The platform teams trust to move quickly.', ctaLabel: 'Start free', }, }); return ( <section> <h1>{content.title}</h1> <p>{content.subtitle}</p> <a href="/signup">{content.ctaLabel}</a> </section> );}
The fallback is what visitors see when no experience matches, so it should be your current default copy. Unmatched visitors then see exactly what they see today.
Track conversions
Mutiny goals become goalCompleted events tracked where the conversion happens:
croct.track('goalCompleted', { goalId: 'demo-requested',});To fuel your experience dashboards, also track the leadGenerated and orderPlaced events.
If you use HubSpot forms for demo requests, the HubSpot integration tracks submissions as goals and maps form fields into the visitor profile without custom code.
Implement goals before publishing any experience. Otherwise, early data will show exposures with no conversions.
Rebuild your segments as audiences
Mutiny segments were built from firmographic and behavioral attributes, and Croct is no different. We evaluate audiences on the server against a live profile, so behavioral conditions reflect the current session rather than a value synced earlier.
If a visitor can match more than one audience, set the priority explicitly in the platform. This is a common source of surprises after a migration, when segments that were mutually exclusive in Mutiny overlap once rebuilt.
Relaunch as experiments
Create an experience for each audience, attach the slots it should affect, and write the content for each variant.
Migrating is the cheapest opportunity you will ever have to check whether your personalization strategy was earning its keep. Rather than switching each experience back on and assuming it still works, run it against your default content as an experiment. Some experiences will turn out to have been neutral, and finding that out now costs nothing.
Review and publish
Before you let the experiments run, confirm that everything behaves as expected:
- Preview each variant
Use preview mode to render the page as a member of each audience.
- Confirm that firmographic data is arriving
Check that company attributes appear on visitor profiles before relying on them in an audience.
- Confirm that the content is in the HTML
View the page source, or disable JavaScript, and check that the personalized content is in the initial response.
- Confirm that goals are firing
Complete the conversion yourself and check that the event appears with the correct goal.
What does not carry over
Keep these differences in mind when planning the work:
- We personalize the components connected to slots, not arbitrary elements on a live page, so there is no element-level visual editing.
- We still do not support the creation of one-to-one outbound pages built from CRM records or CSV uploads for named accounts. We personalize for firmographic audiences rather than per-contact pages.
Troubleshooting
| Symptom | Likely cause |
|---|---|
| Company attributes missing from profiles | The provider’s tag is not firing, or the integration is not enabled. Verify that the provider resolves the visitor first. |
| Account-based experience does not trigger on the first pageview | Expected. Firmographic providers resolve after the initial request, so target the first impression on campaign, referrer, or location instead. |
| Wrong variant for a visitor matching two audiences | The audience priority is not set. Define the order explicitly. |
| All visitors see the same content | The route is statically generated. Confirm that the component calls fetchContent and renders dynamically. See Prerendering. |
| Content flashes before updating | The slot is rendering on the client. Move it to a server-side fetch. |
| Exposures recorded, but no conversions | The goal in the experiment does not match the goalId in your event. |
| Fallback content appears in production | Content requests are failing. Check the application credentials and the timeout option. |