---
title: "HubSpot Legacy CRM Cards Are Dead: 2026 App Card Migration Guide"
slug: hubspot-legacy-crm-cards-are-dead-2026-app-card-migration-guide
date: 2026-08-03
author: Raaj
categories: [HubSpot]
excerpt: "HubSpot Legacy CRM Cards stop rendering October 31, 2026. This technical guide covers the full migration to UI Extension App Cards using the View Swapping Tool."
tldr: "HubSpot Legacy CRM Cards die October 31, 2026. Rebuild as React-based UI Extensions, deploy via Projects, and use the View Swapping Tool for zero-downtime migration."
canonical: https://clonepartner.com/blog/hubspot-legacy-crm-cards-are-dead-2026-app-card-migration-guide/
---

# HubSpot Legacy CRM Cards Are Dead: 2026 App Card Migration Guide


# HubSpot Legacy CRM Cards Are Dead: 2026 App Card Migration Guide

**HubSpot Legacy CRM Cards stop rendering on October 31, 2026.** After that date, every legacy CRM card built with the CRM Extensions API will silently disappear from every CRM record page — contacts, companies, deals, tickets — across every portal where your app is installed. No fallback. No grace period. If you haven't rebuilt your cards as UI Extension App Cards and swapped the views, your users will see a blank space where your integration used to be.

This is not a soft deprecation. It is a hard architectural cutoff. HubSpot is replacing the old API-callback model with the React-based UI Extensions SDK and the Projects development platform. Developers must rebuild their integrations from the ground up and deploy the View Swapping Tool to update existing customer portals without manual intervention.

This guide covers the full technical migration path: what's changing architecturally, how to set up the new development environment, how to rebuild your data fetching layer, how to use the View Swapping Tool, and the specific gaps in documentation that cause migrations to fail.

## The October 31 Deadline: What Is Happening to Legacy CRM Cards?

Starting June 16, 2025, Classic CRM cards were no longer supported due to the upcoming sunset. Legacy CRM cards built with the legacy CRM Extensions API (`/crm/v3/extensions/cards` and related endpoints) will no longer render in HubSpot CRM records after October 31, 2026. UI Extensions built with Projects are not affected.

The deprecation is part of a broader platform shift:

- **Legacy public app creation is already dead.** Legacy public app creation was disabled as of June 23, 2026. Existing legacy public apps continue to function until the card rendering deadline.
- **Marketplace listings require migration.** Legacy CRM cards are no longer permitted for new app listings, certifications, or recertification submissions.
- **OAuth v1 is on its way out.** On February 16, 2027, HubSpot will deprecate the legacy OAuth v1 endpoints in favor of the latest OAuth API (`/oauth/2026-03`).
- **Platform versioning is enforced.** Apps must use a currently supported developer platform version — v2025.2 or v2026.03.
- **Projects v2025.1 is also being deprecated.** August 1, 2026 — two months before the CRM card deadline.

The timeline creates two compounding deadlines. If you're on Projects v2025.1, you must migrate to 2026.03 by August 1 before you can even complete the CRM card migration by October 31.

For ISVs with Marketplace-listed apps: you cannot recertify without migrating. For internal integrations: your users lose visibility into external data on November 1. For teams currently evaluating HubSpot: any custom CRM cards must be built on the Projects platform from day one.

> [!CAUTION]
> **View swapping is irreversible.** Once you initiate the Migrate Views API call, the legacy card is permanently hidden and replaced by the App Card. Test thoroughly in a sandbox before touching production.

## Architectural Shift: From API Callbacks to React-Based App Cards

This is not a config change. It's a full frontend rebuild.

### The Legacy Model: JSON Webhooks

Under the legacy system, HubSpot acted as a proxy. When a user loaded a Contact record, HubSpot made an HTTP GET request to your external server with CRM record context (object type, object ID, properties). Your server responded with a static JSON payload defining card sections, properties, and action URLs. HubSpot parsed that JSON and rendered a rigid sidebar card.

Classic CRM cards were static, limited to the right sidebar, and available for only a few standard CRM objects. Latency was entirely dependent on your external server's response time — any lag or timeout produced a broken card experience.

### The New Model: UI Extensions and Projects

App Cards operate on a fundamentally different architecture. A UI Extension is a React component that HubSpot renders inside their own UI — not an iframe, not a popup. It is an actual component using HubSpot's design system primitives, deployed to HubSpot's infrastructure, that communicates with your backend on the customer's behalf.

Here's what changes at each layer:

| Layer | Legacy CRM Card | UI Extension App Card |
|---|---|---|
| **Rendering** | HubSpot fetches JSON, renders server-side | React component runs client-side in HubSpot's UI |
| **Data fetching** | HubSpot calls your URL; you return JSON | You call `hubspot.serverless()` or `hubspot.fetch()` from React |
| **Interactivity** | Static properties, simple action URLs | Full React state, forms, buttons, modals, alerts |
| **Surfaces** | Right sidebar only | Sidebar, middle column, preview panel, helpdesk sidebar |
| **Development** | Configure a URL in HubSpot UI | HubSpot CLI + Projects framework + local dev server |
| **Deployment** | Deploy your server anywhere | `hs project upload` to HubSpot's infrastructure |
| **Backend secrets** | Stored on your server | Stored as HubSpot project environment variables |

The UI Extensions SDK provides hooks, context access, and data-fetching utilities. Extensions are written as React components but are registered with HubSpot via `hubspot.extend()` inside the component file rather than a standard export — a distinction that trips up most developers new to the platform (see the code examples below).

Support agents can now execute complex external system actions — issuing a refund in Stripe, updating a Jira ticket — directly from an interactive App Card without leaving HubSpot. That interactivity was architecturally impossible with the legacy JSON model.

The bottom line: if your team doesn't have React experience, this migration has a real learning curve. There's no way to preserve the old JSON-response model by pointing it at a new endpoint.

## Setting Up the HubSpot CLI and Projects Platform

### Prerequisites

Node.js 20 or higher is required. HubSpot CLI v8.0.0 introduced a breaking change establishing Node 20 as the minimum version — check your environment before installing.

### Installation and Authentication

```bash
# Install the HubSpot CLI globally
npm install -g @hubspot/cli@latest

# Authenticate with your HubSpot account
hs account auth
```

### Scaffolding the Project

```bash
hs project create
```

This generates a boilerplate directory with two critical files:
- `hsproject.json` — defines project dependencies, build settings, and the target developer platform version
- `hubspot.config.yml` — maps your local environment to your target HubSpot portal

The directory structure splits into `src/app.functions` (serverless backend logic) and `src/app/extensions` (React frontend components).

### Migrating an Existing App to the Projects Framework

Your migration path depends on your current app state:

1. **App is NOT on the Projects framework:** Run `hs app migrate`. This migrates your app configuration to the Projects-based structure without impacting installed customers.
2. **App IS already project-based:** Run `hs project migrate` to update to the latest platform version.

Your project must target **Developer Platform version 2026.03 or higher**. Specify this in `hsproject.json`:

```json
{
  "name": "your-app-name",
  "version": "2026.03"
}
```

```bash
# Migrate a legacy public app to the Projects framework
hs app migrate

# Update an existing project-based app to the latest platform version
hs project migrate
```

> [!WARNING]
> **Projects v2025.1 deprecation:** Projects v2025.1 will be deprecated on August 1, 2026 — two months before the CRM card deadline. If you're currently on 2025.1, migrate to 2026.03 first, or you'll hit a second forced migration mid-project.

## Rebuilding Your Integration: Data Fetching Layer

The biggest conceptual shift in this migration is ownership of the request cycle. With legacy cards, your server was passive — HubSpot called you. With App Cards, your React component is active — it initiates requests.

Your external API keys and secrets must now be stored in HubSpot as project environment variables (`hs project secretAdd`), not hardcoded into external server logic. Access them via `process.env.YOUR_SECRET_NAME` inside serverless functions.

You have two options depending on where your backend lives.

### Option 1: Serverless Functions (Backend Hosted on HubSpot)

Serverless functions live in `src/app/<AnyName>.functions/`. Each function folder requires two files:
- A JavaScript file exporting a `main` function
- A `serverless.json` configuration file that registers the function

**`serverless.json` example:**

```json
{
  "runtime": "nodejs18.x",
  "version": "1.0",
  "environment": {},
  "secrets": ["EXTERNAL_SYSTEM_KEY"],
  "endpoints": {
    "fetchExternalData": {
      "file": "fetchExternalData.js",
      "method": "GET"
    }
  }
}
```

**Serverless function — `fetchExternalData.js`:**

```javascript
// src/app/extensions/myCard.functions/fetchExternalData.js
const axios = require('axios');

exports.main = async (context = {}) => {
  const { hs_object_id } = context.propertiesToSend;
  const externalApiKey = process.env.EXTERNAL_SYSTEM_KEY;

  try {
    const response = await axios.get(
      `https://your-api.com/data/${hs_object_id}`,
      { headers: { Authorization: `Bearer ${externalApiKey}` } }
    );
    return { statusCode: 200, body: response.data };
  } catch (error) {
    return {
      statusCode: error.response?.status || 500,
      body: { error: error.message }
    };
  }
};
```

**React component — calling the serverless function:**

```jsx
// src/app/extensions/ExternalDataCard.jsx
import { hubspot } from '@hubspot/ui-extensions';
import { Text, Flex, Divider, Button, Alert } from '@hubspot/ui-extensions/components';
import { useState, useEffect } from 'react';

// hubspot.extend() registers this file with HubSpot — do NOT use a standard export
hubspot.extend(({ context }) => <ExternalDataCard context={context} />);

const ExternalDataCard = ({ context }) => {
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    hubspot
      .serverless('fetchExternalData', {
        propertiesToSend: ['hs_object_id'],
      })
      .then((res) => {
        setData(res.body);
        setLoading(false);
      })
      .catch((err) => {
        setError(err.message);
        setLoading(false);
      });
  }, []);

  if (loading) return <Text>Loading...</Text>;
  if (error) return <Alert title="Failed to load data" variant="error">{error}</Alert>;

  return (
    <Flex direction="column" gap="sm">
      <Text format={{ fontWeight: 'bold' }}>{data.accountName}</Text>
      <Divider />
      <Text>{data.description}</Text>
      <Button onClick={() => hubspot.serverless('syncRecord', { propertiesToSend: ['hs_object_id'] })}>
        Sync Record
      </Button>
    </Flex>
  );
};
```

Note: `hubspot.extend()` must be called at the module level, not inside the component function. This is the registration mechanism — without it, HubSpot will not render the extension regardless of how the React component is structured.

### Option 2: External API via `hubspot.fetch()` (Backend on Your Infrastructure)

For apps keeping backend logic on their own infrastructure (AWS Lambda, Vercel, Netlify, DigitalOcean Functions), use `hubspot.fetch()`:

```jsx
try {
  const response = await hubspot.fetch('https://your-api.com/data', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ objectId: context.crm.objectId }),
  });
  const result = await response.json();
  setData(result);
} catch (err) {
  setError(err.message);
}
```

> [!NOTE]
> **Content Security Policy (CSP) restriction:** Direct API calls from UI card code are blocked by HubSpot's CSP. The browser's native `fetch()` and `XMLHttpRequest` are not available. You must use `hubspot.serverless()` (for functions hosted on HubSpot) or `hubspot.fetch()` (for external endpoints). This applies even if your endpoint is on the same domain as your other HubSpot integrations.

### Ticket Cards: Required Separate Helpdesk Variant

This is an edge case that causes silent regressions. Legacy CRM cards automatically appeared in the helpdesk sidebar for tickets. App Cards do **not** inherit this behavior.

You must create **two separate App Cards** with distinct titles and extension IDs in your `hsproject.json`:

- Card 1: targeting surface `crm.record.sidebar` (CRM record views)
- Card 2: targeting surface `helpdesk.sidebar` (Service Hub agent workspace)

If you only build the `crm.record.sidebar` variant and run the View Swapping Tool, the card will vanish from the helpdesk entirely. The Migrate Views API accepts a separate `helpdeskAppCardId` parameter specifically for this reason (see the View Swapping section below).

## Using the Legacy CRM Card View Swapping Tool

Building the new App Card is only half the battle. If you have hundreds of customers actively using your legacy integration, you cannot expect their CRM admins to manually reconfigure record views.

Starting April 21, 2026, HubSpot introduced the Legacy CRM Card View Swapping Tool, which enables developers to migrate Legacy CRM Cards to UI Extension App Cards without disrupting existing customer CRM views.

### How the View Swapping Tool Works

The tool:
- Replaces a legacy CRM card with a UI extension app card across all app installs
- Automatically updates existing customer CRM views — both default views and team-specific views
- Requires no action from customers or their CRM admins
- Runs asynchronously as a background job across all portals

The migration hides the legacy card and displays the replacement App Card in the same positional location within each view. Customized view layouts are preserved; the card is swapped in place.

### The Migrate Views API Endpoint

The View Swapping Tool is invoked via the following API endpoint:

```
POST https://api.hubapi.com/crm/v3/extensions/cards/migrate-views
```

**Required parameters:**

```json
{
  "appId": 12345,
  "legacyCrmCardId": "legacy-card-id",
  "appCardId": "new-app-card-id",
  "helpdeskAppCardId": "helpdesk-app-card-id"
}
```

| Parameter | Required | Description |
|---|---|---|
| `appId` | Yes | Your HubSpot app's numeric ID |
| `legacyCrmCardId` | Yes | ID of the legacy card being replaced |
| `appCardId` | Yes | ID of the new `crm.record.sidebar` App Card |
| `helpdeskAppCardId` | Conditional | ID of the `helpdesk.sidebar` App Card variant; required if your card surfaces on ticket objects |

Authentication: Developer API key or OAuth token with app management permissions.

### Step-by-Step: Executing the View Swap

**Step 1: Delete the `hs-release-app-cards` feature flag.**
For apps migrated via `hs app migrate`, HubSpot auto-creates this feature flag. It gates all your App Cards and makes them invisible to customers. You must delete it before running the View Swapping Tool. If you trigger the swap with this flag active, the legacy card is hidden and the replacement card is invisible — your users see nothing.

**Step 2: Deploy and validate the App Card in a developer test account.**
Confirm the card renders correctly on all targeted object types (contacts, companies, deals, tickets). Confirm the helpdesk sidebar variant works in Service Hub if applicable. Confirm all serverless function secrets are configured in the production environment, not just your local `.env`.

**Step 3: Call the Migrate Views API.**
Send the POST request with all required parameters. The API returns a response indicating the total number of installs queued and how many are still processing.

**Step 4: Monitor processing.**
Poll the migration status endpoint or watch your app's install count against the processing count in the API response. Processing time varies with install volume — plan for longer windows on high-install-count apps.

**Step 5: Remove legacy card code.**
After migration completes across all installs, remove the legacy CRM card definition from your project. If you leave it in place, HubSpot will automatically hide it on October 31, 2026 regardless.

> [!WARNING]
> **Execution is irreversible.** Once view swapping has been initiated, it cannot be reversed. A broken App Card triggered prematurely will corrupt the CRM view for all users across all affected portals simultaneously. Use HubSpot's developer test accounts to validate the full flow — including error states — before touching production installs.

### Two Approaches to Building the Replacement Card

**Option A: Full redesign** using UI Extension React components. This is the recommended path for any card beyond simple read-only data display. App Cards support interactive forms, buttons, multi-step flows, and modals — capabilities that were architecturally impossible with legacy cards.

**Option B: Legacy Card Converter** for a quick migration that replicates your existing card's behavior. Available on [HubSpot's GitHub ui-extensions-examples repository](https://github.com/HubSpot/ui-extensions-examples/tree/main/legacy-card-converter). Note the behavioral differences: action requests use JSON bodies instead of URL-encoded bodies, and card titles are static.

The Converter is useful for getting something functional quickly and unblocking the view swap. A full redesign delivers better user experience and is necessary for any interactive functionality.

## The Broader Platform Migration Timeline

| Date | Event |
|---|---|
| **June 16, 2025** | Legacy CRM cards no longer supported; no new cards creatable via UI |
| **June 23, 2026** | Legacy public app creation disabled for all accounts |
| **August 1, 2026** | Projects v2025.1 deprecated |
| **October 31, 2026** | Legacy CRM cards stop rendering; CRM Extensions API endpoints removed |
| **February 16, 2027** | OAuth v1 endpoints fully deprecated |

If you're touching your app's auth layer during this migration, update your OAuth endpoints at the same time. The updated path is `/oauth/2026-03`.

## Why This Migration Fails When Underestimated

Three patterns account for most failed or delayed migrations:

**1. Misclassifying this as an API update.**
The legacy model was a server-side JSON callback — you owned the rendering logic on your server. The new model is a React frontend with a serverless or external backend. If your team hasn't worked with React or the HubSpot UI Extensions SDK, the ramp-up is measured in weeks, not hours. The `hubspot.extend()` registration pattern, CSP restrictions on fetch calls, and surface-specific deployment (sidebar vs. helpdesk vs. middle column) are all new concepts with no equivalent in the legacy system.

**2. Assuming standard HubSpot agencies or iPaaS tools can handle it.**
Most HubSpot agencies specialize in marketing configuration — workflows, email templates, landing pages. Building React-based UI Extensions with serverless functions is full-stack engineering work. iPaaS tools like Zapier and Make are designed for background data sync; they have no capability to build or deploy custom UI components inside the CRM. Verify that whoever you hire has shipped App Cards to production, not just configured legacy CRM cards.

**3. Deferring past the deadline.**
On October 31, 2026, Classic CRM cards stop rendering and the CRM Extensions API endpoints supporting them are removed. There is no grace period, rollback window, or emergency extension path documented by HubSpot. If the View Swapping Tool has not been run before that date, customer portals will show blank spaces where integrations previously existed, with no automated recovery.

For multi-portal deployments, the operational complexity compounds: the View Swapping Tool runs asynchronously, and debugging rendering failures across dozens or hundreds of portals requires staged rollout planning and per-install verification.

## What to Do Right Now

If you're reading this in mid-2026, you have roughly three months. Prioritized action list:

1. **Audit your apps.** Identify all apps using the CRM Extensions API (`/crm/v3/extensions/cards`) with legacy CRM cards that need migration.
2. **Check your platform version.** If you're on Projects v2025.1, migrate to 2026.03 before August 1 — this is a prerequisite for everything else.
3. **Set up the CLI and Projects environment.** Run `hs app migrate` or `hs project migrate` in a dev account.
4. **Build the replacement App Card.** Include the helpdesk sidebar variant if you support ticket objects. Handle error states in the React component.
5. **Configure production secrets.** Run `hs project secretAdd` for all environment variables your serverless functions need — don't assume your local `.env` carries over.
6. **Delete the `hs-release-app-cards` feature flag.** This step is required before view swapping and is commonly missed.
7. **Run the Migrate Views API** (`POST /crm/v3/extensions/cards/migrate-views`) after confirming the App Card is live and validated.
8. **Update OAuth endpoints** to `/oauth/2026-03` while you're in the codebase.
9. **Test migrated cards in a developer account** before the October 31, 2026, deadline.

The October 31 deadline is absolute. The tooling exists. The documentation is solid. The only risk is waiting too long to start.

## References

- HubSpot Developer Docs: [Migrate Legacy CRM Cards to App Cards](https://developers.hubspot.com/docs/guides/crm/app-cards/migrate-legacy-crm-cards)
- HubSpot Developer Docs: [UI Extensions Overview](https://developers.hubspot.com/docs/guides/crm/ui-extensions/overview)
- HubSpot Developer Docs: [Serverless Functions](https://developers.hubspot.com/docs/guides/crm/ui-extensions/serverless-functions)
- HubSpot Developer Docs: [Projects Platform](https://developers.hubspot.com/docs/guides/crm/projects/overview)
- HubSpot Developer Docs: [Legacy CRM Cards API](https://developers.hubspot.com/docs/reference/api/crm/extensions/crm-cards)
- HubSpot GitHub: [UI Extensions Examples — Legacy Card Converter](https://github.com/HubSpot/ui-extensions-examples/tree/main/legacy-card-converter)
- HubSpot Developer Changelog: [Projects v2025.1 Deprecation](https://developers.hubspot.com/changelog)
- HubSpot Developer Docs: [OAuth 2026-03 API](https://developers.hubspot.com/docs/reference/api/app-management/oauth)

> Need help migrating legacy CRM cards to App Cards — or building new integrations on HubSpot's Projects platform? ClonePartner's engineering team has shipped UI Extensions with serverless functions across multi-portal deployments. Let's talk.
>
> [Talk to us](https://cal.com/clonepartner/meet?duration=30)

## Frequently asked questions

### When are HubSpot Legacy CRM Cards being deprecated?

Legacy CRM Cards will be fully deprecated on October 31, 2026. After that date, they will no longer render in HubSpot CRM records and the CRM Extensions API endpoints that powered them will be removed. No new legacy CRM cards have been creatable since June 16, 2025.

### What is the HubSpot Legacy CRM Card View Swapping Tool?

The View Swapping Tool is a developer-initiated API (Migrate Views API) released on April 21, 2026. It replaces a legacy CRM card with a UI Extension App Card across all customer installs, automatically updating CRM views without requiring any action from end users. The swap runs asynchronously and is irreversible once initiated.

### Can I migrate a legacy CRM card without rebuilding it in React?

No. The legacy model used server-side JSON payloads; the new App Cards require React-based UI components built with HubSpot's UI Extensions SDK. HubSpot provides a Legacy Card Converter on GitHub that replicates basic legacy card behavior in the new framework to accelerate the transition, but you still need to work within the React-based Projects environment.

### Do legacy CRM cards work in the HubSpot helpdesk sidebar?

Legacy CRM cards automatically appeared in the helpdesk sidebar for tickets, but App Cards do not. You must create two separate App Cards — one for crm.record.sidebar and one for helpdesk.sidebar — each with a unique title and extension card ID. Missing the helpdesk variant will cause your card to disappear from Service Hub agent views.

### Can I still use OAuth v1 for my HubSpot integration?

OAuth v1 endpoints will be fully deprecated on February 16, 2027. HubSpot recommends updating your API paths to /oauth/2026-03 to align with date-based API versioning. If you're rebuilding your CRM cards, update your OAuth endpoints during the same migration.
