know.2nth.ai โ€บ Business โ€บ biz โ€บ crm โ€บ hubspot
biz/crm ยท HubSpot ยท Skill Leaf

HubSpot
with six GTM roles.

The CRM that grew into a customer platform โ€” sales, marketing, service, CMS, and operations on one set of objects. The 2nth skill wraps the v3 CRM API with the six go-to-market roles each business actually has, and gives every one of them an AI partner with its own tool set.

Production CRM API v3 Private Apps Associations v4 Search API v1.0.0

One graph of customers โ€” surfaced through five product hubs.

HubSpot started as an inbound marketing tool and grew, hub by hub, into a full customer platform. Underneath it's a single CRM graph โ€” contacts, companies, deals, tickets, line items โ€” with five product surfaces layered on top. You integrate with the graph once, and every hub works.

Hub What it owns Key CRM objects
Marketing Campaigns, landing pages, email, forms, ads, lists contacts, lists, campaigns, forms
Sales Pipelines, deals, quotes, sequences, meetings deals, contacts, companies, line items, quotes
Service Tickets, SLAs, knowledge base, feedback tickets, contacts, companies, conversations
CMS Website, blog, HubDB, smart content pages, HubDB rows, contacts
Operations Data sync, workflows, custom code, data quality properties, pipelines, workflows, all objects

The 2nth model: one GTM role + one AI. HubSpot is already multi-human by design โ€” sales reps, marketers, CS agents, and RevOps engineers all work in the same portal on the same records. The canonical 2nth skill formalises six roles and gives each one its own AI partner with a narrowly scoped tool set. The human decides; the AI enables โ€” never the other way around.

Role The human decides The AI enables
Revenue Leader Forecast, territory, quota, GTM strategy Pipeline health, win-rate drift, stalled-deal triage, forecast scoring
Sales Rep Deal relationships, call priorities, closing moves Next-best action, CRM hygiene, draft follow-ups, meeting summaries
Marketing Manager Campaign strategy, positioning, budget allocation Segment lists, subject-line A/B, attribution reports, asset performance
Service / CS Lead Escalations, voice of customer, NPS Ticket routing, sentiment flags, draft replies, SLA tracking
RevOps Engineer Object model, process design, pipeline stages Property inventories, workflow audits, data-quality reports, association mapping
Executive / Owner GTM priorities, resource allocation, board narrative Revenue dashboards, campaign ROI, churn signals, cross-hub rollups

HubSpot is the system of record. 2nth is the partner layer.

Don't try to replace HubSpot's internals. Read contacts, deals, tickets, and campaigns, enrich them with AI, and write back approved changes as first-class CRM mutations. The pipeline, workflow engine, and email sending infrastructure all stay where they are. Your job is the reasoning that sits on top.

Private Apps for internal, OAuth for the marketplace.

HubSpot has three auth flavours. For integrations inside a single portal โ€” which is what almost every 2nth deployment is โ€” you want a Private App. You'll never touch OAuth unless you're shipping a public marketplace app. API keys are deprecated and should never be used in new work.

Auth flavour Use when Header
Private App token Internal integration, single portal. Scopes defined at creation time in the HubSpot UI. Authorization: Bearer pat-na1-...
OAuth 2.0 Public marketplace app, multi-portal. Install flow + refresh tokens. Authorization: Bearer {access_token}
API key (legacy) Do not use. Deprecated in 2022, read-only grandfathered support only. ?hapikey=... (legacy)
# Every v3 CRM request takes the same header
Authorization: Bearer pat-na1-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Content-Type: application/json

# v3 CRM API base
https://api.hubapi.com/crm/v3/objects/{objectType}
https://api.hubapi.com/crm/v3/objects/{objectType}/search
https://api.hubapi.com/crm/v4/associations/{fromObjectType}/{toObjectType}/batch/read
// Cloudflare Worker โ€” HubSpot contact lookup by email
const res = await fetch(
  'https://api.hubapi.com/crm/v3/objects/contacts/search',
  {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${env.HUBSPOT_PRIVATE_APP_TOKEN}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      filterGroups: [{
        filters: [{ propertyName: 'email', operator: 'EQ', value: 'alex@example.com' }]
      }],
      properties: ['email', 'firstname', 'lastname', 'lifecyclestage'],
      limit: 1,
    }),
  }
);
const { results } = await res.json();

Scopes are the real security boundary.

Private App tokens are scope-gated at creation. A token with crm.objects.contacts.read can't touch deals, and a token with tickets can't read line items. Create one Private App per AI role rather than one omni-token โ€” a leaked marketer token should not be able to delete a deal. Store tokens in wrangler secret, rotate quarterly, and never expose them to the browser.

Objects, associations, search, webhooks.

Four patterns carry almost every HubSpot integration: the CRM object endpoints for straight CRUD, Associations v4 for the graph edges, the Search API for anything that needs filters, and webhooks for reactive flows. Learn these four and 90% of HubSpot work becomes shape-fitting.

CRM objects โ€” the endpoints you'll touch most. Every object in HubSpot โ€” standard or custom โ€” follows the same URL shape. Swap contacts for companies, deals, tickets, or a custom object name and the rest is identical.

# Read โ€” list and by ID
GET  /crm/v3/objects/contacts?limit=100&properties=email,firstname,lastname
GET  /crm/v3/objects/contacts/{id}?properties=email,lifecyclestage

# Write โ€” single and batch
POST /crm/v3/objects/contacts
PATCH /crm/v3/objects/contacts/{id}
POST /crm/v3/objects/contacts/batch/create
POST /crm/v3/objects/contacts/batch/update

# Same shape for deals, tickets, companies, line items, quotes, custom objects
GET  /crm/v3/objects/deals?properties=dealname,amount,dealstage,pipeline
GET  /crm/v3/objects/tickets?properties=subject,hs_ticket_priority,hs_pipeline_stage
GET  /crm/v3/objects/companies?properties=name,domain,industry

Associations v4 โ€” the graph lives in the edges. A contact without its company is half a record; a deal without its contacts is useless. Associations v4 is the good API for this โ€” labelled, typed, batchable. Don't use v3 in new work.

# Read associations for a single contact
GET /crm/v4/objects/contacts/{contactId}/associations/companies
GET /crm/v4/objects/contacts/{contactId}/associations/deals

# Batch read โ€” associations for many objects at once (fast)
POST /crm/v4/associations/contacts/deals/batch/read
{
  "inputs": [{ "id": "101" }, { "id": "102" }, { "id": "103" }]
}

# Create a labelled association (custom types live in v4)
PUT /crm/v4/objects/contacts/{contactId}/associations/deals/{dealId}
[{ "associationCategory": "HUBSPOT_DEFINED", "associationTypeId": 3 }]

Search API โ€” filterGroups are your friend. List endpoints are for simple paging. The moment you need to filter, sort, or combine conditions, you want /search. filterGroups are ORed together; filters inside a group are ANDed. The result cap is 10,000 per query โ€” paginate with after, never trust offset past 10k.

POST /crm/v3/objects/deals/search
{
  "filterGroups": [{
    "filters": [
      { "propertyName": "dealstage", "operator": "EQ", "value": "presentationscheduled" },
      { "propertyName": "amount",    "operator": "GT", "value": "50000" },
      { "propertyName": "hs_lastmodifieddate", "operator": "GT", "value": "1700000000000" }
    ]
  }],
  "sorts": [{ "propertyName": "amount", "direction": "DESCENDING" }],
  "properties": ["dealname", "amount", "dealstage", "hubspot_owner_id"],
  "limit": 100,
  "after": "0"
}

MCP tool matrix โ€” one GTM role, one tool set. Mapping the role model to actual MCP tools. Each role gets a bounded tool set and nothing more. The reader-heavy shape below is deliberate โ€” most HubSpot AI value is in diagnosis and drafting, not autonomous writes. Humans still approve the mutations that touch pipeline and money.

const ROLE_TOOLS = {
  revenue_leader: ['get_pipeline_health', 'get_forecast_rollup', 'get_win_rate_trend', 'list_stalled_deals'],
  sales_rep:      ['get_my_deals', 'get_contact', 'draft_followup_email', 'log_engagement', 'get_next_best_action'],
  marketing:      ['get_campaign_analytics', 'get_campaign_asset_metrics', 'search_contacts', 'build_segment', 'draft_subject_lines'],
  service:        ['search_tickets', 'get_ticket', 'get_contact_history', 'draft_reply', 'flag_sentiment'],
  revops:         ['list_properties', 'search_properties', 'audit_workflows', 'map_associations', 'get_data_quality_report'],
  executive:      ['get_revenue_dashboard', 'get_campaign_roi', 'get_customer_growth', 'get_cross_hub_rollup'],
};

Load this context: the HubSpot MCP already wired into 2nth

The 2nth.ai environment exposes an official HubSpot MCP surface that maps cleanly to this role model. Current tools: get_campaign_analytics, get_campaign_asset_metrics, get_campaign_asset_types, get_campaign_contacts_by_type, get_crm_objects, search_crm_objects, get_properties, search_properties, search_owners, get_organization_details, get_user_details, tool_guidance. An agent loading this skill node pairs those tools with the role it's serving โ€” a marketing agent gets the campaign tools, a RevOps agent gets the property and owner tools.

Webhooks โ€” the reactive layer. HubSpot fires webhooks from a Public App (not a Private App โ€” one of the sharp edges). Subscribe to property changes and object creations, verify the X-HubSpot-Signature-v3 header, route to the right role's agent. Retries happen over 24 hours with exponential backoff, so idempotency on the receiver matters.

Subscription Use case
contact.creationWelcome sequence, segment assignment, lead scoring
contact.propertyChangeRe-score on lifecyclestage or persona change
deal.propertyChangeAlert on stage advance, stalled-deal detection
ticket.creationSentiment flag, SLA clock start, CS triage
company.propertyChangeTerritory reassignment, account-based marketing signals

Rate limits โ€” per portal, not per token. The daily and burst caps apply to the entire HubSpot account, so every Private App and OAuth token shares the same budget. Plan for this: one noisy batch job can starve the agents behind it.

Plan Burst Daily
Free / Starter100 req / 10 sec250,000
Professional100 req / 10 sec500,000
Enterprise150 req / 10 sec1,000,000+

Things that only bite in production.

Six specific failure modes โ€” the ones that actually cost time the first time they happen.

Properties need their internal name, not the label

The label in the UI is "Annual Revenue". The property name in the API is annualrevenue. If you filter on the label the response is silent โ€” empty results, no error. Always call GET /crm/v3/properties/{objectType} first to discover the internal names.

Search caps at 10,000 results

Paginate with after, and if your query might exceed 10k, split it by a time window โ€” hs_lastmodifieddate > T. Offset-based paging past 10,000 is silently truncated. Bulk backfills should use incremental windows, not one giant query.

Timestamps are Unix millis in GMT

All date properties โ€” createdate, hs_lastmodifieddate, custom datetimes โ€” are Unix milliseconds in GMT, not seconds, not ISO. Filtering with an ISO string returns empty results. Convert on the way in.

Private Apps can't receive webhooks

Webhooks only fire from Public Apps. If your integration is a Private App โ€” as most internal 2nth deployments are โ€” you need a minimal Public App alongside it just for the webhook subscription. This is a frequent surprise the first time someone needs reactive flows.

Rate limits are per portal, not per token

Creating ten Private Apps doesn't give you ten times the budget โ€” every token draws from the same burst and daily caps. Monitor X-HubSpot-RateLimit-Remaining headers across all tokens, not just the one you're currently using.

Associations v3 is a trap in new code

v3 still works but it pretends all associations are identical. v4 introduces labelled types โ€” primary, billing contact, influencer, decision-maker โ€” which is what you actually need for any real RevOps work. Build new integrations on v4 and never look back.

What HubSpot pulls on, and what pulls on it.

HubSpot sits in the middle of the GTM layer โ€” it reads from ERP for the real order history, feeds fin/reporting with pipeline and revenue numbers, and shares the six-role model with Shopify where the same customer may exist on both sides.

Go deeper.

HubSpot's developer docs are good โ€” and the v3 / v4 split can be confusing. Always check which version an endpoint sits on before copying code; associations are v4, most objects are v3, and webhooks live under the Public App surface.