How do I migrate a legacy public app to projects?
TL;DR — hs app migrate wraps a project around your legacy public app: features become source files, Build #1 deploys as your baseline, and auth credentials, installs, and listings are preserved with zero customer interruption. Before running it: disable the GitHub integration, screenshot your settings page (it must be rebuilt as a UI extension), plan the classic-CRM-card replacement, and — if you have v1/v3 timeline events — be ready to move to the v4 API within 7 days or your calls 401. The token model also changes: one refresh token per account, service-account users on new installs.
What the migration actually preserves
Per HubSpot's public-app migration guide, the operation is designed to be invisible to your customers: original auth credentials, every existing install, your marketplace listing, and supported features (app configuration, webhooks, custom workflow actions) all carry over. Your backend doesn't change. Nobody reinstalls.
What you get in exchange is the projects framework: your app's configuration becomes source files you version, build, and deploy through the CLI — which is also the prerequisite for everything modern (app cards, UI extensions, and the path to 2026.03).
The four things that don't carry — audit these first
1. Timeline events start a 7-day clock. If your app has a v1/v3 timeline event, migrating starts a hard countdown: after 7 days, calls to the old timeline endpoints return 401. This is the one item that breaks a running backend on a timer, so treat the v4 endpoint switch as part of the migration change, deployed the same day — not a follow-up ticket.
2. The GitHub integration must be off before you migrate. If your project auto-builds from GitHub, disable the integration first, migrate, then re-create CI/CD with GitHub Actions. Migrating with it enabled is an unsupported state.
3. Settings pages must be rebuilt. Legacy app settings pages don't convert. HubSpot's own advice: catalog every interface element in production before migrating, then recreate the page as a UI extension afterward. If you skip the cataloging step, you're reverse-engineering your own app from memory.
4. Classic CRM cards aren't supported on projects. They must be refactored to app cards — which has its own deadline and its own guide. For marketplace apps, note the interplay: app cards are auto-hidden after migration for beta testing, and the feature-flags API controls the rollout.
And one architectural change to brief your backend team on: the permission model moves to service accounts. New installs create a service-account user tied to your app, each account holds one refresh token no matter how many users reauthorize, and the OAuth token-metadata endpoints return a serviceAccountId after migration. If your token storage assumes a refresh token per authorizing user, fix that assumption before you migrate.
Running it
CLI version matters: 7.6.0+ for this flow (and 8.4.0+ if you're continuing to 2026.03 — which you should, the functions question is settled there). Then:
hs app migrateYou'll pick the app, confirm the feature list, choose a project name and local path, and assign UIDs to features. UIDs are permanent identity — change one in a later build and the platform treats the feature as new, with all the detachment that implies. Choose deliberately; never edit casually.
Confirmation triggers a single atomic operation: project created, features converted to source files, Build #1 built and deployed (completing the app↔project association), and the source downloaded to your machine. Build #1 is your safety rail — it captures the migrated state as a baseline you can redeploy to roll back later changes. It is not an un-migrate: after this point, project-defined features are no longer editable in the app management UI or the old developer APIs.
Where HS-X fits
The pattern this page keeps repeating — know what won't carry before running the one-way command — is what hs-x migrate inspect mechanizes. Point it at a legacy project tree and it classifies every feature: what converts automatically, what needs human review (settings pages, classic cards, timeline events), and why, with no side effects. HS-X's larger migration model takes the same caution further: generate the modern app as a separate testbed, verify it live against a real portal, and only then touch the app your customers have installed.
hs-x migrate inspect ./my-legacy-app # what carries, what needs review — before anything is one-way
hs-x migrate report ./my-legacy-app # readiness summary + next steps