返回 Skill 列表
extension
分类: 开发与工程无需 API Key

hubspot-app-builder

当用户要求“构建HubSpot应用”、“创建HubSpot应用”、“向HubSpot添加卡片”、“创建应用卡片”、“构建UI扩展”、“设置HubSpot Webhook”、“配置HubSpot应用”、“为HubSpot应用添加设置页面”、“构建HubSpot主页”、“在HubSpot扩展中获取数据”、“在HubSpot市场列出应用”、“提交HubSpot应用列表”、“市场列表要求”,或提到基于2025.2版的HubSpot开发者平台进行构建时,应使用此技能。提供构建完整HubSpot应用的最佳实践、CLI命令、文件结构以及App Marketplace列表要求的全面指导。

person作者: jakexiaohubgithub

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) or private (for specific accounts)
  • Auth: oauth (multiple accounts) or static (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:

  • uid must be globally unique within the project (up to 64 chars, alphanumeric + _, -, .)
  • type must match the parent folder name (app)
  • Use static auth + remove redirectUrls for single-account private apps
  • At minimum, include one read scope (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 + actions
  • useExtensionContext<location>() — access context only
  • useExtensionActions<location>() — access actions only
  • useCrmProperties(["prop1", "prop2"]) — from @hubspot/ui-extensions/crm
  • useAssociations({ 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:

  • body is a plain object — do not JSON.stringify() or set Content-Type manually
  • Only Authorization is supported as a custom header
  • URLs must be listed in permittedUrls.fetch; localhost is not allowed (use a proxy)
  • HubSpot appends userId, portalId, userEmail, appId as 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.json config instead of the single serverless.json used in 2025.1. Each function has its own config file in src/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.env for access tokens (not context.secrets)
  • Async/await required (callbacks no longer supported)
  • Log size up to 256KB, guaranteed execution order
  • Secrets managed via hs secret add CLI 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:

  1. Update platformVersion from "2025.2" to "2026.03" in hsproject.json
  2. Run hs project upload

From 2025.1, 2023.2, or 2023.1:

  1. Run hs project migrate in your project directory and follow the prompts

From legacy public apps (non-project-based):

  1. Run hs app migrate and follow the prompts

Key migration notes:

  • Environment variables from old serverless.json must be re-added as secrets using hs secret add
  • The old serverless.json single-config is replaced by individual -hsmeta.json files per function in src/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 workflowAction type 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 — Complete app-hsmeta.json schema, auth types
  • references/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 API
  • references/fetching-data.mdhubspot.fetch() full guide: differences from native fetch, limits, auto query params, Authorization header pattern, local dev proxy, signature validation
  • references/ui-components.md — All UI components with examples
  • references/features.md — App events, app objects (open beta), settings page, home page
  • references/signature-validation.md — Full signature validation implementation for webhooks and card/settings page fetch endpoints
  • references/marketplace-listing.md — Full App Marketplace listing requirements, brand rules, app card criteria, and review process

Official Documentation