TL;DR
- What many people still call "custom cards" HubSpot now documents as app cards, a type of UI extension.
- They're built with React inside an app on the HubSpot developer platform.
- They can appear on CRM records, preview panels, the help desk workspace and the sales workspace (HubSpot Developers).
- Cards call your backend with
hubspot.fetch(). URLs must be allow-listed. Requests time out after 15 seconds by default (120 seconds maximum) and carry a 1 MB payload limit (HubSpot Developers).- Want HubSpot-hosted serverless functions behind the card instead of your own backend? Installing an app with serverless functions requires an Enterprise subscription (HubSpot Developers).
- The current platform version is 2026.09. Version 2026.03 re-introduced serverless functions for developer-platform apps (HubSpot versioning).
Why reps ignore most CRM customisations
Reps live on the deal and contact record. If the information they need sits in another tab, another tool or a report, they won't look at it. They'll ask a colleague, or guess.
An app card puts a purpose-built mini-tool right on the record. For example:
- a price calculator;
- a product search over your ERP catalogue;
- a "create quote" button;
- a file area for the client.
It's the difference between "the data is in HubSpot somewhere" and "the next action is right in front of me".
What I've built with cards
- Dynamic quote card. A custom card powered by serverless functions creates quotes directly from deals. Dropdown options are stored in a database with limits, prices and conditions. The generated quote is linked to the deal and triggers an automated email chain.
- Product lookup / quote cards for an e-commerce business as part of a Shopify–HubSpot setup.
- Client portal card. Each deal had a card that creates folders, uploads files and generates a secure password for client access, built with serverless functions and React.
Naming in 2026
| What you might have heard | What HubSpot's docs call it now |
|---|---|
| Custom card, CRM card, UI extension | App card (an extension point of UI extensions) |
| Private app (created in Settings) | Legacy private app |
| Project-based app | App on the developer platform (versions 2025.2, 2026.03, 2026.09) |
Sources: app cards overview, legacy private apps, developer platform overview.
Platform versions you'll run into (HubSpot versioning):
- 2026.09 — current, available since September 8, 2026.
- 2026.03 — supported; re-introduced serverless functions.
- 2025.2 — supported; raised the minimum Node.js version to v22.
- 2025.1 — sunset on August 1, 2026.
- 2023.2 — sunset on October 1, 2025.
If you have an older card, HubSpot provides migration guides for legacy private and public apps.
How a card gets onto a record
- A developer builds an app containing the card and uploads it with the HubSpot CLI.
- The app is installed in the account.
- A Super Admin or a user with the Customize record page layout permission adds the card to the record view, on the tab and in the position they choose (HubSpot Developers).
That third step is a common "why can't I see it?" moment. The card exists, but nobody has placed it.
Technical depth
Project structure and CLI
Apps on the developer platform are created and deployed with the HubSpot CLI (developer platform overview). The core commands from HubSpot's card guide (create an app card):
hs project create # start a new project
hs project add # add a feature such as an app card
hs project install-deps # install the card's dependencies
hs project upload # upload to your default account (--account <HubID> for another)
hs project dev # local development server for fast iteration
A card consists of:
- a
*-hsmeta.jsonconfiguration file; - a React component file (
.jsx).
The boilerplate card from hs project add appears in the middle column of contact records once you add it to the record view.
Fetching data with hubspot.fetch()
From HubSpot's fetching-data reference (source):
- Allow-list. Every URL you call must be listed in
permittedUrls.fetchin the app configuration. Entries are prefixes. A URL that isn't listed fails with 403 Forbidden. - Timeout. The default is 15 seconds; you can raise it to 120 seconds with the
timeoutoption. - Retry. HubSpot automatically retries a request once if the connection fails or the request returns a 5XX within the timeout window.
- Payload. Requests and responses are each limited to 1 MB.
- Headers. You can pass a custom
Authorizationheader. All other client-supplied headers are stripped, and your backend's response headers aren't returned. HubSpot adds request-signing headers that your backend can verify.
Design implication: return small, card-shaped JSON (the 20 products that match, not the 50,000), and paginate.
Serverless functions vs your own backend
| HubSpot serverless functions (app) | Your own backend via hubspot.fetch() | |
|---|---|---|
| Subscription to install | Enterprise required | No serverless requirement |
| Hosting | HubSpot | You (Vercel, AWS, etc.) |
| Execution limit | 15-second timeout; cold starts possible | Up to your infrastructure (card waits ≤120 s) |
| Auth support | Privately distributed apps with static auth | Your choice (verify HubSpot's signature) |
Serverless details are from HubSpot's serverless functions overview. Public serverless endpoints are listed there as Content Hub Enterprise only.
Sensitive data
If your app uses Sensitive Data scopes, its cards cannot use hubspot.fetch() or serverless functions. HubSpot says you'd need separate apps for each capability (app cards overview).
Service keys don't work here
HubSpot's newer service keys are for direct REST API calls only. They can't be used to make calls within a UI extension, so build an app with a static or OAuth token for cards (HubSpot Developers — service keys).
Designing a card reps actually use
- One job per card. "Build a quote" or "Find a product", not both.
- Start from the record. Pre-fill everything you already know from the deal, contact or company.
- Write back to HubSpot. A card that only displays data is a report. A card that creates a quote, line items or a task changes behaviour.
- Keep it fast. Paginate, cache on your backend, and remember the 1 MB limit.
- Plan permissions. Decide which teams see the card and on which record types.
Sources
- HubSpot Developers — App cards overview: https://developers.hubspot.com/docs/apps/developer-platform/add-features/ui-extensions/extension-points/app-cards/overview
- HubSpot Developers — Create an app card: https://developers.hubspot.com/docs/apps/developer-platform/add-features/ui-extensions/extension-points/app-cards/create-an-app-card
- HubSpot Developers — Fetching data (
hubspot.fetch): https://developers.hubspot.com/docs/apps/developer-platform/add-features/ui-extensions/fetching-data - HubSpot Developers — Serverless functions overview: https://developers.hubspot.com/docs/apps/developer-platform/add-features/serverless-functions/overview
- HubSpot Developers — Platform versioning: https://developers.hubspot.com/docs/developer-tooling/platform/versioning
- HubSpot Developers — Developer platform overview: https://developers.hubspot.com/docs/apps/developer-platform/overview
- HubSpot Developers — Legacy private apps: https://developers.hubspot.com/docs/apps/legacy-apps/private-apps/overview
- HubSpot Developers — Service keys: https://developers.hubspot.com/docs/apps/developer-platform/build-apps/authentication/account-service-keys
FAQ
Are HubSpot "custom cards" and "app cards" the same thing? App card is HubSpot's current name for cards built as UI extensions in an app on the developer platform.
Do I need Enterprise for an app card?
For HubSpot-hosted serverless functions, yes: installing an app that uses them requires Enterprise. A card can instead call your own backend with hubspot.fetch().
Why can't my team see the card? Someone with Super Admin or Customize record page layout permission must add it to the record view after the app is installed.
How long can a card wait for my API?
15 seconds by default, up to 120 seconds with the timeout option.