Association Type IDs Demystified
/ 5 min read
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}/labelsFor example, to find all association types between deals and contacts:
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
| Association | Type ID |
|---|---|
| Contact to Company | 1 |
| Contact to Company (Primary) | 1 |
| Contact to Deal | 4 |
| Contact to Ticket | 15 |
| Contact to Note | 10 |
Deal Associations
| Association | Type ID |
|---|---|
| Deal to Contact | 3 |
| Deal to Company | 341 |
| Deal to Line Item | 19 |
| Deal to Ticket | 27 |
Company Associations
| Association | Type ID |
|---|---|
| Company to Contact | 2 |
| Company to Deal | 342 |
| Company to Ticket | 25 |
Activity/Engagement Associations
| Association | Type ID |
|---|---|
| Note to Contact | 202 |
| Note to Company | 190 |
| Note to Deal | 214 |
| Call to Contact | 194 |
| Email to Contact | 198 |
| Meeting to Contact | 200 |
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:
- First, discover the type ID using the labels endpoint
- Use
USER_DEFINEDas the category - 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:
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
- There’s no static master list of association type IDs - discover them dynamically
- Use
/crm/v4/associations/{from}/{to}/labelsto find all available types - The default shortcut endpoint works for simple unlabelled associations
- Always specify the association category (
HUBSPOT_DEFINEDorUSER_DEFINED) - 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.