HubSpot App Builder (Platform 2026.03)
This skill guides the development of full HubSpot apps on the latest developer platform (version 2026.03), covering project creation, configuration, UI extensions, serverless functions, agent tools, webhooks, and distribution.
Platform version 2026.03 was released on March 30, 2026 (GA confirmed in Spring 2026 Spotlight, April 14, 2026). It re-introduces full serverless function support for apps (including static-auth apps), introduces agent tools for Breeze Agents, expands App Homes into multi-page App Pages, and adds code sharing via npm workspaces. Previous version 2025.2 is now in "Supported" status.
Prerequisites
- HubSpot CLI v8.3.0+:
npm install -g @hubspot/cli@latest - Authenticate:
hs account auth - Node.js 22+ (minimum raised to v22 in 2025.2 and carried into 2026.03; was v20 in 2025.1)
- A HubSpot developer account
- Enterprise subscription required for production serverless functions (developer test accounts work without)
Project Setup Workflow
1. Create the Project
hs project create
Follow CLI prompts to configure:
- Distribution:
marketplace(for App Marketplace listing) orprivate(for specific accounts) - Auth:
oauth(multiple accounts) orstatic(single account) - Features: Select from
card,settings,pages,app-function,serverless-function,webhooks,workflow-action
To add a feature later:
hs project add
2. Project File Structure
my-project-folder/
├── hsproject.json # Must include "platformVersion": "2026.03"
├── packages/ # Shared code (npm workspaces, optional)
│ └── shared-utils/
│ ├── index.ts
│ └── package.json
└── src/
└── app/
├── app-hsmeta.json # Top-level app config (required)
├── cards/ # UI extension cards
│ ├── MyCard.jsx
│ ├── my-card-hsmeta.json
│ └── package.json
├── pages/ # App Pages (multi-page, formerly App Homes)
│ ├── HomePage.jsx
│ ├── home-page-hsmeta.json
│ ├── DetailPage.jsx
│ ├── detail-page-hsmeta.json
│ └── package.json
├── settings/ # App settings page
│ ├── Settings.tsx
│ ├── settings-hsmeta.json
│ └── package.json
├── functions/ # Serverless functions (2026.03 format)
│ ├── myFunction.js
│ ├── myFunction-hsmeta.json # Individual config per function
│ └── package.json
├── app-events/ # App events (open beta)
│ └── my-event-hsmeta.json
├── app-objects/ # App objects (open beta)
│ └── my-object-hsmeta.json
├── webhooks/ # Webhook subscriptions
│ └── webhooks-hsmeta.json
└── workflow-actions/ # Custom workflow actions / Agent tools
└── custom-action-hsmeta.json
3. Configure app-hsmeta.json
{
"uid": "my_app_uid",
"type": "app",
"config": {
"name": "My App",
"description": "App description for installing users.",
"distribution": "marketplace",
"auth": {
"type": "oauth",
"redirectUrls": ["http://localhost:3000/oauth-callback"],
"requiredScopes": ["crm.objects.contacts.read"],
"optionalScopes": [],
"conditionallyRequiredScopes": []
},
"permittedUrls": {
"fetch": ["https://api.example.com"],
"iframe": [],
"img": []
},
"support": {
"supportEmail": "support@example.com",
"documentationUrl": "https://example.com/docs"
}
}
}
Key rules:
uidmust be globally unique within the project (up to 64 chars, alphanumeric +_,-,.)typemust match the parent folder name (app)- Use
staticauth + removeredirectUrlsfor single-account private apps - At minimum, include one
readscope (e.g.,crm.objects.contacts.read)
4. Upload and Deploy
hs project upload # Upload and trigger a build
hs project open # Open project in HubSpot browser
hs project dev # Start local dev server with hot reload
hs project install-deps # Install package.json dependencies
5. Install the App
After uploading, install via HubSpot UI:
- Navigate to Development > Projects > [Project Name] > [App UID]
- Click the Distribution tab
- For test accounts: click Add test install(s)
- For standard accounts: click Install now
UI Extensions
All UI extensions share the same structure: a *-hsmeta.json config + a React component file (.jsx or .tsx).
App Card Configuration (cards/*-hsmeta.json)
{
"uid": "my-card",
"type": "card",
"config": {
"name": "My Card",
"description": "Card description.",
"location": "crm.record.tab",
"entrypoint": "/app/cards/MyCard.jsx",
"objectTypes": ["contacts"]
}
}
Supported locations:
| Location | Value | Notes |
|---|---|---|
| CRM middle column | crm.record.tab | Most common; supports custom tabs |
| CRM right sidebar | crm.record.sidebar | No CRM data components here |
| CRM preview panel | crm.preview | Record previews across CRM |
| Help desk sidebar | helpdesk.sidebar | Requires tickets scope |
| App Pages (formerly App Home) | home | Multi-page full-screen experience; use PageRoutes and PageLink for navigation |
| App settings page | settings | Config UI in HubSpot settings |
Supported objectTypes: contacts, companies, deals, tickets, orders, carts, p_customObjectName, app_object_uid
React Component Pattern
import React from "react";
import { hubspot, Text, Button, Flex } from "@hubspot/ui-extensions";
// Required: register extension with HubSpot
hubspot.extend(({ context, actions }) => (
<MyCard context={context} addAlert={actions.addAlert} />
));
const MyCard = ({ context, addAlert }) => {
return (
<Flex direction="column" gap="medium">
<Text>Hello, {context.user.firstName}!</Text>
<Button onClick={() => addAlert({ type: "success", title: "Done", message: "Action completed" })}>
Click me
</Button>
</Flex>
);
};
SDK Hooks (Preferred Approach)
import { hubspot, Button, useExtensionApi } from "@hubspot/ui-extensions";
import { useCrmProperties } from "@hubspot/ui-extensions/crm";
hubspot.extend<'crm.record.tab'>(() => <MyCard />);
const MyCard = () => {
// Access both context and actions
const { context, actions } = useExtensionApi<'crm.record.tab'>();
// Fetch CRM properties from the current record
const { properties, isLoading } = useCrmProperties(["firstname", "lastname", "email"]);
if (isLoading) return <Text>Loading...</Text>;
return (
<Button onClick={() => actions.addAlert({ message: `Hello ${properties.firstname}!` })}>
Say Hello
</Button>
);
};
Available hooks:
useExtensionApi<location>()— access both context + actionsuseExtensionContext<location>()— access context onlyuseExtensionActions<location>()— access actions onlyuseCrmProperties(["prop1", "prop2"])— from@hubspot/ui-extensions/crmuseAssociations({ toObjectType, properties, pageLength })— from@hubspot/ui-extensions/crm
Fetching External Data
import { hubspot } from "@hubspot/ui-extensions";
// GET
const response = await hubspot.fetch("https://api.example.com/data", {
method: "GET",
timeout: 5000,
});
const data = await response.json();
// POST — body is a plain object, not JSON.stringify()
const response = await hubspot.fetch("https://api.example.com/data", {
method: "POST",
body: { key: "value" },
});
Key differences from native fetch:
bodyis a plain object — do notJSON.stringify()or setContent-Typemanually- Only
Authorizationis supported as a custom header - URLs must be listed in
permittedUrls.fetch;localhostis not allowed (use a proxy) - HubSpot appends
userId,portalId,userEmail,appIdas query params on every request - Max 20 concurrent requests; 15s timeout; 1MB payload limit
Your backend must validate X-HubSpot-Signature-v3 on every incoming request — see references/signature-validation.md.
For the full guide (proxy setup, Authorization header pattern, local dev signing, monitoring), see references/fetching-data.md.
Serverless Functions (2026.03 Format)
Platform version 2026.03 re-introduces full serverless function support for apps, including apps using static auth. Serverless functions execute server-side JavaScript within HubSpot's infrastructure, eliminating the need for external servers.
Important: 2026.03 uses a new per-function
-hsmeta.jsonconfig instead of the singleserverless.jsonused in 2025.1. Each function has its own config file insrc/app/functions/.
Types of Serverless Functions
- Private (App) functions — internal functions called by UI extensions from App Cards, App Pages, and App Settings. Requires Enterprise subscription or a free developer test account.
- Public endpoint functions — HTTP-accessible endpoints. Requires Content Hub Enterprise. Not available in developer test accounts.
Directory Structure (2026.03)
src/app/functions/
├── myFunction.js
├── myFunction-hsmeta.json # Individual config per function
└── package.json
This replaces the 2025.1 structure that used nested .functions folders with a single serverless.json.
Configuration (myFunction-hsmeta.json)
Private app function (no public endpoint):
{
"uid": "myFunction",
"type": "app-function",
"config": {
"entrypoint": "/app/functions/myFunction.js",
"secretKeys": ["my_api_key"]
}
}
App function with public endpoint:
{
"uid": "myPublicFunction",
"type": "app-function",
"config": {
"entrypoint": "/app/functions/myPublicFunction.js",
"secretKeys": ["my_api_key"],
"endpoint": {
"path": "my-endpoint",
"method": ["GET", "POST"]
}
}
}
Serverless Function Pattern
// myFunction.js
const hubspot = require("@hubspot/api-client");
exports.main = async (context = {}) => {
const hubspotClient = new hubspot.Client({
accessToken: process.env.PRIVATE_APP_ACCESS_TOKEN,
});
try {
const res = await hubspotClient.crm.contacts.basicApi.getPage();
return res;
} catch (err) {
console.error(err);
return err;
}
};
Key Notes
- Uses
process.envfor access tokens (notcontext.secrets) - Async/await required (callbacks no longer supported)
- Log size up to 256KB, guaranteed execution order
- Secrets managed via
hs secret addCLI command - NPM packages supported
- Enterprise subscription required for production (test accounts work without)
- Functions can now run in Developer Test Accounts for safer iteration before production
Migrating Serverless Functions to 2026.03
Migration path depends on your current version:
From 2025.2:
- Update
platformVersionfrom"2025.2"to"2026.03"inhsproject.json - Run
hs project upload
From 2025.1, 2023.2, or 2023.1:
- Run
hs project migratein your project directory and follow the prompts
From legacy public apps (non-project-based):
- Run
hs app migrateand follow the prompts
Key migration notes:
- Environment variables from old
serverless.jsonmust be re-added as secrets usinghs secret add - The old
serverless.jsonsingle-config is replaced by individual-hsmeta.jsonfiles per function insrc/app/functions/
Warning: Migrating legacy private or public apps to 2026.03 is irreversible — you cannot downgrade back.
Agent Tools (New in 2026.03)
Agent tools are enhanced custom workflow actions that HubSpot AI agents (Breeze Agents) can call to perform tasks on behalf of users. They are built using the Developer Projects framework.
How Agent Tools Work
- Built as enhanced
workflowActiontype in your project - Submitted for review before distribution
- Once approved, made available to customers via your app listing and Breeze Agents
- Marketplace-listed apps cannot deploy unapproved agent tools — deploys will fail until the tool passes review
Listing Requirements
Apps that ship agent tools must comply with agent tool listing requirements.
For more details, see the Agent Tools overview.
Code Sharing with npm Workspaces (New in 2026.03)
You can now share code across multiple UI extensions (app cards, settings pages, app pages) within a single project using npm workspaces.
Setup
Create shared packages alongside your extensions:
my-project-folder/
├── packages/
│ └── shared-utils/
│ ├── index.ts
│ └── package.json
└── src/
└── app/
├── cards/
│ └── package.json # declares shared-utils as dependency
└── pages/
└── package.json # declares shared-utils as dependency
Each extension's package.json references the shared package:
{
"dependencies": {
"shared-utils": "*"
}
}
Installation
Shared packages are installed automatically when you run hs project dev or hs project upload, or manually with:
hs project install-deps
For details, see Code sharing with npm workspaces.
Developer MCP Server (GA)
The local HubSpot Developer MCP server is now generally available, enabling app and CMS development through AI-powered code editors (Claude Code, Cursor, VS Code, etc.).
Setup
hs mcp setup
Requires HubSpot CLI v8.2.0+. Available tools include: create-project, add-feature-to-project, upload-project, deploy-project, validate-project, search-docs, fetch-doc, get-build-status, get-build-logs, and more.
For the full tool list and setup, see Developer MCP Server docs.
Available UI Components
Import from @hubspot/ui-extensions:
- Layout:
Flex,Box,Divider,Grid,AutoGrid,Spacer,Inline - Text/Display:
Text,Heading,Image,Link,Icon,Illustration - Input:
Input,TextArea,Select,MultiSelect,Checkbox,RadioButton,DateInput,NumberInput,CurrencyInput,SearchInput,StepperInput,Toggle,ToggleGroup - Actions:
Button,LoadingButton,IconButton,ButtonRow - Feedback:
Alert,LoadingSpinner,Tag,StatusTag,Badge,Tooltip,ProgressBar,EmptyState,ErrorState - Overlay:
Modal,ModalBody,ModalFooter,Panel,PanelBody,PanelFooter,Dropdown - Data:
Table,TableHead,TableBody,TableRow,TableCell,DescriptionList,Statistics,ScoreCircle - Navigation:
Tabs,StepIndicator,Accordion - Charts:
BarChart,LineChart - Container:
Tile - Form:
Form,FormField - List:
List
Import from @hubspot/ui-extensions/crm:
- CRM Data:
CrmPropertyList,CrmAssociationTable,CrmAssociationPivot,CrmAssociationPropertyList,CrmAssociationStageTracker,CrmDataHighlight,CrmReport,CrmStageTracker,CrmStatistics - CRM Actions:
CrmActionButton,CrmActionLink,CrmCardActions
Webhooks Configuration
Create src/app/webhooks/webhooks-hsmeta.json:
{
"uid": "my-webhooks",
"type": "webhooks",
"config": {
"settings": {
"targetUrl": "https://api.example.com/webhook",
"maxConcurrentRequests": 10
},
"subscriptions": {
"crmObjects": [
{
"subscriptionType": "object.creation",
"objectType": "contact",
"active": true
},
{
"subscriptionType": "object.propertyChange",
"objectType": "contact",
"active": true
}
],
"legacyCrmObjects": [
{
"subscriptionType": "contact.propertyChange",
"propertyName": "email",
"active": true
}
],
"hubEvents": [
{
"subscriptionType": "contact.privacyDeletion",
"active": true
}
]
}
}
}
Use crmObjects for new-format events (object.*). Use legacyCrmObjects for classic types like contact.creation. Use hubEvents for contact.privacyDeletion and conversation.*.
Signature Validation (Required)
HubSpot signs all outbound requests with X-HubSpot-Signature-v3:
- Webhook deliveries — HubSpot POSTs event payloads to your
targetUrl - Card / settings page fetch — any
hubspot.fetch()call from a UI extension is proxied and signed by HubSpot
Both must be validated the same way using Signature.isValid() from @hubspot/api-client. For the complete implementation, see references/signature-validation.md.
package.json for UI Extensions
{
"name": "my-card",
"version": "0.1.0",
"dependencies": {
"@hubspot/ui-extensions": "latest",
"react": "^18.2.0"
},
"devDependencies": {
"typescript": "^5.3.3"
}
}
Install: hs project install-deps
Distribution & Auth Summary
| Distribution | Auth Type | Install Limit |
|---|---|---|
| private | static | 1 standard account + 10 test accounts |
| private | oauth | Up to 10 allowlisted accounts |
| marketplace | oauth | 25 before listing; unlimited after |
App Marketplace Listing
Before submitting to the HubSpot App Marketplace, the app must meet these key requirements:
Technical minimums:
- OAuth is the sole authorization method — no API keys or private app tokens
- At least 3 active installs from unaffiliated accounts with OAuth-authenticated API activity in the past 30 days
- Only request scopes the app actually uses; all requested scopes must appear in the Shared data table
- Must run on a supported platform version (2025.2+ today, 2026.03 recommended)
- Must use a supported date-based API version for certified apps
- Classic CRM cards are not allowed for new listings/certifications (must migrate to App Cards by October 31, 2026)
- Agent tools are a reviewable surface — deploys fail until tools pass review for compliance with agent tool listing requirements
Listing content:
- Content must be integration-specific (not general product marketing)
- All URLs must be live, publicly accessible, and under 250 characters — add HubSpot Crawler to your site allow list before submitting
- Include: setup documentation, Install button URL, support resources, Terms of Service, Privacy Policy, and pricing (matching your website exactly)
- Bi-directional sync must be declared in Shared data when both read and write scopes are requested for the same object
App cards (if using UI extensions):
- Do not use HubSpot brand names in card names or icons
- One primary button per surface; destructive buttons must use destructive styling
- Must not access or display sensitive data
Review process: Initial review within 10 business days; full cycle up to 60 days. Only one app can be under review at a time.
For the complete requirements checklist, see references/marketplace-listing.md.
Local Development
hs project dev # Starts dev server with hot reload
After starting, a local development homepage appears in the test account showing active dev sessions. Changes to .jsx/.tsx files reload automatically.
Note (Chrome 142+): Accept the local network access popup from app.hubspot.com on first launch.
Debugging
View logs: Development > Monitoring > Logs > UI Extensions in HubSpot.
In code, use the logger:
import { logger } from "@hubspot/ui-extensions";
logger.info("Info message");
logger.debug("Debug message");
logger.warn("Warning message");
logger.error("Error message");
Additional Resources
Reference Files
For detailed configuration and patterns, consult:
references/app-configuration.md— Completeapp-hsmeta.jsonschema, auth typesreferences/scopes.md— Full list of available OAuth scopes grouped by category (CRM, CMS, settings, marketing, etc.)references/ui-extensions-sdk.md— SDK hooks, context fields, actions APIreferences/fetching-data.md—hubspot.fetch()full guide: differences from native fetch, limits, auto query params, Authorization header pattern, local dev proxy, signature validationreferences/ui-components.md— All UI components with examplesreferences/features.md— App events, app objects (open beta), settings page, home pagereferences/signature-validation.md— Full signature validation implementation for webhooks and card/settings page fetch endpointsreferences/marketplace-listing.md— Full App Marketplace listing requirements, brand rules, app card criteria, and review process
Official Documentation
- Create an app
- App configuration reference
- Manage apps in HubSpot
- UI extensions overview
- App cards reference
- UI extensions SDK
- Fetching data
- UI components overview
- App Pages (formerly App Homes)
- Settings page
- App events (beta)
- App objects (beta)
- Configure webhooks
- App Marketplace listing requirements
- How to list your app
- App certification requirements
- Serverless functions overview
- Agent tools overview
- Agent tool listing requirements
- Code sharing with npm workspaces
- Developer MCP Server setup
- MCP Auth Apps (integrate with remote MCP Server)
- Migrate to 2026.03
- Platform versioning
Scan to join WeChat group