skip to content
san.is
Table of Contents

Every HubSpot developer has been there. You’re building an integration, you need to associate a deal with a company, and you stumble across code examples using mysterious numbers like 341, 3, or 5. You search the documentation. You find examples. But nowhere does it explain where these numbers actually come from.

This article ends that confusion.

The Problem with Static ID Lists

Here’s the uncomfortable truth: there is no comprehensive static list of HubSpot association type IDs that you can simply reference. The legacy documentation that once contained these values has been deprecated, and the IDs themselves can vary depending on your portal’s configuration.

Why? Because HubSpot allows custom association labels, which create new type IDs unique to your portal. A “Billing Contact” label you create might be type ID 28 in your portal and 47 in another.

The v4 Associations API

HubSpot’s v4 Associations API is the current standard, replacing the older v3 endpoints. The key difference is how associations are structured:

{
"associations": [
{
"to": {
"id": "67891"
},
"types": [
{
"associationCategory": "HUBSPOT_DEFINED",
"associationTypeId": 341
}
]
}
]
}

Notice the structure: you specify both the associationCategory (either HUBSPOT_DEFINED or USER_DEFINED) and the associationTypeId. This distinction matters because custom labels you create fall under USER_DEFINED.

Discovering Your Association Type IDs

The reliable method to find association type IDs is to query the API directly. Make a GET request to:

GET /crm/v4/associations/{fromObjectType}/{toObjectType}/labels

For example, to find all association types between deals and contacts:

Terminal window
curl --request GET \
--url 'https://api.hubapi.com/crm/v4/associations/deals/contacts/labels' \
--header 'authorization: Bearer YOUR_ACCESS_TOKEN'

The response includes every available association type:

{
"results": [
{
"category": "HUBSPOT_DEFINED",
"typeId": 3,
"label": null
},
{
"category": "HUBSPOT_DEFINED",
"typeId": 4,
"label": null
},
{
"category": "USER_DEFINED",
"typeId": 28,
"label": "Decision Maker"
}
]
}

The typeId values are what you need for your integration.

Common Default Association Type IDs

While you should always verify against your specific portal, these are the commonly observed default IDs for standard objects:

Contact Associations

AssociationType ID
Contact to Company1
Contact to Company (Primary)1
Contact to Deal4
Contact to Ticket15
Contact to Note10

Deal Associations

AssociationType ID
Deal to Contact3
Deal to Company341
Deal to Line Item19
Deal to Ticket27

Company Associations

AssociationType ID
Company to Contact2
Company to Deal342
Company to Ticket25

Activity/Engagement Associations

AssociationType ID
Note to Contact202
Note to Company190
Note to Deal214
Call to Contact194
Email to Contact198
Meeting to Contact200

Important caveat: These IDs are observed defaults but may differ in your portal. Always verify using the labels endpoint.

Bidirectional Association Pairs

One quirk of HubSpot associations: they’re directional but often come in pairs. When you see:

  • Deal to Contact: 3
  • Contact to Deal: 4

These represent the same relationship but from different perspectives. When creating associations, use the ID that matches your “from” object.

The Primary Company Special Case

Contacts have a special relationship with companies: the Primary Company association. This uses type ID 1 with the “Primary” label. In v4 API responses, you’ll see it like this:

{
"category": "HUBSPOT_DEFINED",
"typeId": 1,
"label": "Primary"
}

When associating a contact to their primary company:

{
"associations": [
{
"to": { "id": "companyId" },
"types": [
{
"associationCategory": "HUBSPOT_DEFINED",
"associationTypeId": 1
}
]
}
]
}

Creating Associations in Batch

For bulk operations, use the batch endpoints. Creating multiple deal-to-contact associations:

const associations = {
inputs: contacts.map(contactId => ({
from: { id: dealId },
to: { id: contactId },
types: [
{
associationCategory: "HUBSPOT_DEFINED",
associationTypeId: 3
}
]
}))
};
await hubspotClient.crm.associations.v4.batchApi.create(
"deals",
"contacts",
associations
);

Using Custom Association Labels

If you’ve created custom labels (like “Decision Maker” or “Billing Contact”), you need to:

  1. First, discover the type ID using the labels endpoint
  2. Use USER_DEFINED as the category
  3. Include the specific type ID
{
"associations": [
{
"to": { "id": "contactId" },
"types": [
{
"associationCategory": "USER_DEFINED",
"associationTypeId": 28
}
]
}
]
}

The Default Association Shortcut

For simple unlabelled associations, v4 provides a convenience endpoint:

PUT /crm/v4/objects/{fromObjectType}/{fromObjectId}/associations/default/{toObjectType}/{toObjectId}

This creates a standard association without needing to know the type ID:

Terminal window
curl --request PUT \
--url 'https://api.hubapi.com/crm/v4/objects/deals/12345/associations/default/contacts/67890' \
--header 'authorization: Bearer YOUR_ACCESS_TOKEN'

Building a Discovery Script

For any new integration, I recommend creating a discovery script that queries all your association types upfront:

const objectPairs = [
['contacts', 'companies'],
['contacts', 'deals'],
['deals', 'companies'],
['deals', 'line_items'],
['tickets', 'contacts'],
['tickets', 'companies']
];
async function discoverAssociationTypes(hubspotClient) {
const associationMap = {};
for (const [from, to] of objectPairs) {
const response = await hubspotClient.crm.associations.v4.schema
.getAll(from, to);
associationMap[`${from}_to_${to}`] = response.results.map(r => ({
typeId: r.typeId,
category: r.category,
label: r.label
}));
}
return associationMap;
}

Store the results and reference them throughout your integration. This approach means you’re never guessing at IDs.

Common Mistakes to Avoid

Using v3 patterns with v4 endpoints: The v4 API requires the nested to: { id: ... } structure. Older examples might show flattened structures that won’t work.

Forgetting the association category: Always specify whether it’s HUBSPOT_DEFINED or USER_DEFINED.

Assuming IDs are universal: A type ID in one portal may not exist or may mean something different in another. Always discover dynamically when building multi-portal integrations.

Not handling bidirectional associations: When reading associations, remember that Deal→Contact is different from Contact→Deal. Query in the right direction.

Key Takeaways

  1. There’s no static master list of association type IDs - discover them dynamically
  2. Use /crm/v4/associations/{from}/{to}/labels to find all available types
  3. The default shortcut endpoint works for simple unlabelled associations
  4. Always specify the association category (HUBSPOT_DEFINED or USER_DEFINED)
  5. Build discovery scripts for robust integrations

The days of copying mysterious numbers from Stack Overflow are over. Query the API, cache the results, and build integrations that actually work across portals.


Building HubSpot integrations or need help with complex API implementations? Connect with me on LinkedIn.