412 ms · 247 rows
██╗ ██╗██████╗ ██╗ ██╗██║ ██║██╔═══╝ ╚██╗██╔╝███████║██████╗ █████╗ ╚███╔╝ ██╔══██║╚═══██║ ╚════╝ ██╔██╗ ██║ ██║██████║ ██╔╝ ██╗╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝
The developer toolkit for HubSpot — build syncs, UI extensions, agent tools, workflow actions, triggers, and a runtime you own.
One install. The whole toolkit.
Four things you can build for HubSpot. One project, one deploy.
Sync any system into HubSpot, and keep it current.
Point a Worker at Airtable, Postgres, Snowflake, or any API. Its records land in HubSpot as contacts, companies, or any object you choose, and refresh on a schedule. Cursors, duplicate matching, retries, and drift detection are handled for you. You write the fetch.
import { defineSource, defineWorker } from "@hs-x/sdk"; const airtableContacts = defineSource({ name: "airtableContacts", auth: { type: "bearer", token: process.env.AIRTABLE_TOKEN }, async fetch({ cursor, http }) { const res = await http.get("https://api.airtable.com/v0/appXYZ/Contacts", { query: { pageSize: 100, offset: cursor }, }); return { cursor: res.body.offset, rows: res.body.records.map(r => ({ key: r.fields.Email, data: { email: r.fields.Email, lifecycle_stage: r.fields.Stage, annual_revenue: r.fields.ARR, owner: r.fields.OwnerEmail, }, })), }; }, }); const worker = defineWorker("contacts"); worker.sync(airtableContacts, { into: "contacts", schedule: "5m", schema: { email: "email", lifecycle_stage: { type: "enum", values: ["subscriber", "lead", "customer"] }, annual_revenue: "currency", owner: "user", }, }); export default worker;
Build real UI inside a HubSpot record.
Boards, tables, forms, feeds, calendars, and a filter builder that compiles to HubSpot CRM search — all typed from your portal’s own schema. hs-uix is our open-source component kit for HubSpot, and every card gets a backend in your Cloudflare, so a drag on this board is a write you own.
DealBoard.tsx the ~40 lines behind this board hs-uix · app card
import { useEffect, useState } from "react"; import { Kanban } from "hs-uix/kanban"; import { formatCurrency } from "hs-uix/utils"; import { hubspot } from "@hubspot/ui-extensions"; const STAGES = [ { value: "discovery", label: "Discovery" }, { value: "proposal", label: "Proposal sent", wipLimit: 3, variant: "warning" }, { value: "negotiation", label: "Negotiation" }, { value: "closedwon", label: "Closed won", variant: "success", terminal: true }, ]; const CARD_FIELDS = [ { field: "name", placement: "title" }, { field: "amount", placement: "meta", render: v => formatCurrency(v) }, { field: "owner", placement: "footer" }, ]; function DealBoard({ context, runServerless }) { const [deals, setDeals] = useState([]); useEffect(() => { runServerless({ name: "listDeals", parameters: { companyId: context.crm.objectId } }) .then(r => setDeals(r.response)); }, [context.crm.objectId]); return ( <Kanban data={deals} stages={STAGES} groupBy="stage" cardFields={CARD_FIELDS} selectable onStageChange={(row, stage) => runServerless({ name: "advanceDeal", parameters: { id: row.id, stage } })} onWipExceeded={(stage, count, limit) => runServerless({ name: "flagWip", parameters: { stage, count, limit } })} /> ); } hubspot.extend(() => <DealBoard />);
Write it once. Breeze can call it, and so can any workflow.
One TypeScript function becomes both an agent tool Breeze, HubSpot’s AI agent, can call and a custom action your team can drop into any workflow — same code, same auth. Test it locally from your coding agent through the HS-X MCP server before it ships.
worker.tool("createQuote", { label: "Create quote", description: "Generate a quote PDF and email it to the primary contact.", objectType: "deal", input: { template: { type: "enum", values: ["standard", "saas"] }, }, agent: { description: "Create and send a quote for this deal.", expose: ["template"], }, async handler({ input, enrolledObject, hubspot }) { const pdf = await renderQuote(enrolledObject, input.template); await hubspot.crm.objects.emails.basicApi.create({ /* … */ }); return { message: `Quote sent for deal ${enrolledObject.id}.` }; }, });
Run your code the moment anything changes in HubSpot.
A deal changes stage, a form comes in, any HubSpot event fires — and your handler runs on your runtime. We verify every signature, drop duplicate deliveries, and queue the flood when HubSpot pushes faster than you can handle. You write the handler.
Runs on your Cloudflare. Not ours.
Everything you build runs in a Cloudflare account you own — your code, your data, your customers’ tokens, on Workers, Durable Objects, Queues, KV, and D1. A deploy needs only your HubSpot and Cloudflare tokens. No HS-X account required. If HS-X disappeared tomorrow, every deployed app would keep running.
The deploy log is the whole setup: export two tokens and run hs-x deploy. No signup, no dashboard. Start without an account →
CLI. SDK. MCP. API.
One toolchain for the developers on your team: a CLI, an SDK that’s just TypeScript, an MCP server so your coding agent can do everything you can, and a REST API for everything else. Same types, same project, same deploy.
A single command. One toolkit.
Scaffold, deploy, tail logs, manage portals. Token-efficient — coding agents can drive it without burning context.
$ npm i -g @hs-x/cli $ hs-x init my-portal $ hs-x deployRead the CLI docs →
TypeScript-first. Schema-driven.
Syncs, workflow actions, triggers, UI extensions — declared in plain TypeScript with a schema that types the rest of your code for you.
import { defineSource, defineWorker } from "@hs-x/sdk"; const airtable = defineSource({ … }); const worker = defineWorker("contacts"); worker.sync(airtable, { into: "contacts", … }); export default worker;Read the SDK docs →
Your coding agent runs the dev loop.
An MCP server for your coding agent — validate the project, list capabilities, invoke any handler through the same dispatch path production uses.
$ claude mcp add hs-x -- \ npx -y @hs-x/mcp
$ codex mcp add hs-x -- \ npx -y @hs-x/mcp
// .cursor/mcp.json { "mcpServers": { "hs-x": { "command": "npx", "args": ["-y", "@hs-x/mcp"] } } }
// opencode.json { "mcp": { "hs-x": { "type": "local", "command": ["npx", "-y", "@hs-x/mcp"] } } }Read the MCP spec →
Plain HTTP, when that’s enough.
The same control plane the CLI and MCP speak — projects, deploys, revisions, rollbacks. Personal access tokens with deploy, read, and admin scopes, and an account audit log.
POST /v1/deploys/plan GET /v1/projects/{id}/revisions POST /v1/projects/{id}/rollbackSee the control-plane protocol →
We maintain the toolkit. You own the runtime.
HS-X handles the hard plumbing — types, deploys, token storage, per-portal rate limits, audit logs. HubSpot owns the portal. You own the Cloudflare account it all runs on, and every deploy writes a plain wrangler.toml you can ship without us. No lock-in, by design.
We build HubSpot apps, too.
Don’t have a developer? The team behind HS-X builds on HS-X — marketplace apps, syncs, agent tools, and moving legacy custom cards to the new framework. By the project or on retainer.
One install. The whole toolkit.
Scaffold an app, add a workflow action, and watch Breeze call it in a developer test account. All from hs-x init.