Building HubSpot Custom Objects in Gadget

Introducing Custom Objects for HubSpot

While building internal automations in HubSpot, I ran into a hard limit.

At Gadget, we manage a growing partner program. Franco, our Head of Partnerships, was manually tracking referrals in a sprawling spreadsheet — making sure each deal was properly attributed to the right partner account. As you can imagine, starting every day in spreadsheet triage mode wasn’t scalable.

The obvious answer? Track partner relationships inside the CRM itself. After all, CRM stands for Customer Relationship Management.

But then we hit a wall.

The HubSpot Object Limit Problem

By default, HubSpot gives you two core objects: Contacts and Companies. We needed an additional custom object to properly model partner relationships.

Turns out, unlocking even one more object required upgrading to a significantly more expensive plan. The price jump? Pure sticker shock.

That’s when the question shifted from:

“What plan do we need?”

to

“Why not build this ourselves in Gadget?”

And that idea quickly evolved into something bigger:

What if this wasn’t just a workaround for us — but a plug-and-play template other HubSpot users could deploy to avoid expensive upgrades?

That’s how the HubSpot Static Object template was born.

What This Template Helps You Do

This approach lets you:

  • Avoid costly plan upgrades

  • Build your own custom automations

  • Keep control of your data — even if you downgrade your HubSpot plan

Instead of paying to unlock a custom object inside HubSpot, you externalize the object into Gadget — while still using HubSpot as your primary interface.

The Architecture: HubSpot + Gadget

This template was built from our internal use case and refined for others facing similar limitations.

Here’s how it works under the hood:

1. Postgres-Backed Models in Gadget

Gadget rapidly provisions a Postgres database and generates CRUD APIs automatically. When you define a model, you instantly get:

  • A Postgres table

  • Create, Read, Update, Delete operations

  • Environment configuration

  • Authentication scaffolding

No boilerplate required.

2. Node as the Orchestration Layer

On top of Gadget’s data model, Node handles HubSpot-specific workflows such as:

  • JWT validation

  • Conditional business logic

  • Coordinated read/write operations between HubSpot and Gadget

This keeps complex integration logic outside of HubSpot while still allowing HubSpot to act as the system of record and primary user interface.

The Use Case: Teams Within a Company

One common issue in HubSpot is distinguishing multiple groups under the same company.

For example:

  • Two separate customers under the same domain

  • Multiple departments under one company account

HubSpot doesn’t make it easy to represent structured sub-groups without creating awkward workarounds or duplicating company objects.

This template solves that by creating a custom Team object managed externally in Gadget.

Defining the Custom Object

The core model lives in:

api/models/customObjectTeam

It includes:

  • teamName – Name of the team

  • teamContacts – Array of HubSpot contact IDs

  • parentCompany – HubSpot company ID

  • portalId – HubSpot portal/account ID

These fields allow you to:

  • Define teams clearly

  • Associate multiple contacts with each team

  • Link everything back to the correct company and portal

When this model is created, Gadget automatically provisions the database table and CRUD operations.

The API Layer

The template exposes the custom object to HubSpot through a set of Node-based API routes that coordinate requests between HubSpot and Gadget.

Primary routes:

  • api/routes/hubspot/GET-teams.ts

  • api/routes/hubspot/POST-action.ts

These routes:

  • Read, create, and update team records

  • Translate HubSpot identifiers into internal data models

  • Apply business logic conditionally

  • Keep workflows organized outside HubSpot

All route access is secured using JWT-based authentication.

Secure Authentication

Authentication follows HubSpot’s v3 specification and is handled in:

api/routes/hubspot/POST-auth.ts

This route:

  • Extracts the Bearer token from the Authorization header

  • Verifies the JWT using GADGET_ENVIRONMENT_JWT_SIGNING_KEY

  • Confirms the session exists and is within a 10-minute validity window

  • Throws an error if validation fails

This ensures that communication between HubSpot and Gadget remains secure and compliant.

Why This Approach Matters

By externalizing custom objects into Gadget, you:

  • Regain control of your data model

  • Avoid long-term subscription lock-in

  • Build automations on your own terms

  • Keep HubSpot as your relationship system of record

If you’ve ever hit the same “object limit” wall, this approach offers a practical, extensible way forward.

Instead of paying more to unlock flexibility, you build it yourself — and keep it.

If you’d like, I can also create:

  • A more SEO-optimized version

  • A punchier LinkedIn post version

  • Or a highly technical deep-dive version for developers