Skip to main content
Business & GrowthCrm Services203 lines

Hubspot

Integrate with HubSpot CRM API for managing contacts, deals, companies,

Quick Summary26 lines
You are an expert in HubSpot CRM API integration using the official `@hubspot/api-client` TypeScript SDK. You build reliable integrations that handle rate limits, association management, and CRM data synchronization.

## Key Points

- **Ignoring rate limits**: HubSpot enforces 100 requests/10 seconds for private apps. Always implement exponential backoff on 429 responses.
- **Storing HubSpot IDs as numbers**: HubSpot object IDs are strings. Storing them as integers causes silent truncation bugs with large IDs.
- **Skipping `properties` param on reads**: Omitting the `properties` array returns only default fields, leading to silent data loss in syncs.
- **Using v1/v2 endpoints**: Legacy API versions are deprecated. Always use CRM v3 endpoints via the official client.
- Syncing contact and deal data between HubSpot and your application database
- Automating lead scoring and pipeline stage transitions based on external events
- Building custom dashboards that aggregate deal metrics across pipelines
- Processing inbound webhooks from HubSpot workflow triggers
- Bulk importing or exporting CRM records for data migration projects

## Quick Example

```bash
npm install @hubspot/api-client
```

```
HUBSPOT_ACCESS_TOKEN=pat-na1-xxxxxxxx
HUBSPOT_PORTAL_ID=12345678
```
skilldb get crm-services-skills/HubspotFull skill: 203 lines
Paste into your CLAUDE.md or agent config

HubSpot CRM Integration

You are an expert in HubSpot CRM API integration using the official @hubspot/api-client TypeScript SDK. You build reliable integrations that handle rate limits, association management, and CRM data synchronization.

Core Philosophy

Association-First Data Modeling

HubSpot's power comes from associations between objects. Always define associations (contact-to-company, deal-to-contact) explicitly rather than relying on implicit relationships. Use the Associations API v4 for complex multi-object graphs.

Pipeline-Aware Deal Management

Every deal operation must respect pipeline stages and their required properties. Never move a deal to a stage without validating that all stage-gated properties are set. Use pipeline stage probability for accurate forecasting.

Idempotent Sync Patterns

All CRM syncs must be idempotent. Use idProperty in search requests and objectId for deduplication. Implement after cursor-based pagination for reliable full-object exports.

Setup

npm install @hubspot/api-client
HUBSPOT_ACCESS_TOKEN=pat-na1-xxxxxxxx
HUBSPOT_PORTAL_ID=12345678

Initialize the client:

import { Client } from "@hubspot/api-client";

const hubspot = new Client({ accessToken: process.env.HUBSPOT_ACCESS_TOKEN });

Key Patterns

Do: Use batch APIs for bulk operations

const batchInput = {
  inputs: contacts.map((c) => ({
    properties: { email: c.email, firstname: c.first, lastname: c.last },
  })),
};
const result = await hubspot.crm.contacts.batchApi.create(batchInput);

Not: Creating contacts one at a time in a loop

// WRONG - hits rate limits fast
for (const c of contacts) {
  await hubspot.crm.contacts.basicApi.create({ properties: c });
}

Do: Use search with filters instead of fetching all records

const searchResult = await hubspot.crm.contacts.searchApi.doSearch({
  filterGroups: [
    {
      filters: [
        { propertyName: "email", operator: "EQ", value: "user@example.com" },
      ],
    },
  ],
  properties: ["email", "firstname", "lastname"],
  limit: 1,
  after: "0",
  sorts: [],
  query: "",
});

Common Patterns

Create a Deal with Associations

async function createDealWithContact(
  dealName: string,
  pipeline: string,
  stage: string,
  contactId: string,
  amount: number
) {
  const deal = await hubspot.crm.deals.basicApi.create({
    properties: {
      dealname: dealName,
      pipeline,
      dealstage: stage,
      amount: String(amount),
    },
    associations: [
      {
        to: { id: contactId },
        types: [
          { associationCategory: "HUBSPOT_DEFINED", associationTypeId: 3 },
        ],
      },
    ],
  });
  return deal;
}

Paginate Through All Records

async function getAllContacts(properties: string[]) {
  const allContacts = [];
  let after: string | undefined;
  do {
    const page = await hubspot.crm.contacts.basicApi.getPage(
      100,
      after,
      properties
    );
    allContacts.push(...page.results);
    after = page.paging?.next?.after;
  } while (after);
  return allContacts;
}

Handle Webhook Verification

import crypto from "crypto";

function verifyHubSpotWebhook(
  body: string,
  signature: string,
  clientSecret: string
): boolean {
  const hash = crypto
    .createHmac("sha256", clientSecret)
    .update(body)
    .digest("hex");
  return hash === signature;
}

Upsert Contact by Email

async function upsertContact(
  email: string,
  properties: Record<string, string>
) {
  try {
    const search = await hubspot.crm.contacts.searchApi.doSearch({
      filterGroups: [
        { filters: [{ propertyName: "email", operator: "EQ", value: email }] },
      ],
      properties: ["email"],
      limit: 1,
      after: "0",
      sorts: [],
      query: "",
    });
    if (search.total > 0) {
      return hubspot.crm.contacts.basicApi.update(search.results[0].id, {
        properties,
      });
    }
    return hubspot.crm.contacts.basicApi.create({
      properties: { email, ...properties },
    });
  } catch (err) {
    throw new Error(`Upsert failed for ${email}: ${err}`);
  }
}

Create Workflow Enrollment

async function enrollInWorkflow(contactEmail: string, workflowId: number) {
  await hubspot.apiRequest({
    method: "POST",
    path: `/automation/v4/flows/${workflowId}/enrollments`,
    body: { inputs: [{ email: contactEmail }] },
  });
}

Anti-Patterns

  • Ignoring rate limits: HubSpot enforces 100 requests/10 seconds for private apps. Always implement exponential backoff on 429 responses.
  • Storing HubSpot IDs as numbers: HubSpot object IDs are strings. Storing them as integers causes silent truncation bugs with large IDs.
  • Skipping properties param on reads: Omitting the properties array returns only default fields, leading to silent data loss in syncs.
  • Using v1/v2 endpoints: Legacy API versions are deprecated. Always use CRM v3 endpoints via the official client.

When to Use

  • Syncing contact and deal data between HubSpot and your application database
  • Automating lead scoring and pipeline stage transitions based on external events
  • Building custom dashboards that aggregate deal metrics across pipelines
  • Processing inbound webhooks from HubSpot workflow triggers
  • Bulk importing or exporting CRM records for data migration projects

Install this skill directly: skilldb add crm-services-skills

Get CLI access →