CRM Custom Objects

Using custom objects:

  • You must have an Enterprise subscription to use the CRM Custom Objects API.
    Upgrade your subscription 
  • Any Enterprise account can create, read, and update custom objects using API key authentication.
  • CRM custom objects are account-specific.
  • An account can have up to 10 CRM custom objects.

Integrating custom objects using OAuth:

Are you interested in integrating a custom object with your HubSpot app using OAuth? You’ll need to fill out a short form to request access to our pilot program for this feature. The HubSpot app certification team will review your submission and contact you with any updates. Click the “Create request” button to get started. * Acceptance to the pilot program is subect to approval by HubSpot and is not guaranteed.



Your business data can be complex and unique. You should be able to represent and organize that data based on your business requirements. That is why HubSpot has created a public API to define and manage custom objects. Users and integrators can now use the public API to define custom objects, properties, and associations, and leverage HubSpot’s powerful marketing, sales, and service platform to grow their business.

Custom objects are specific to a portal. They live alongside standard objects in HubSpot - Contact, Company, Deal, and Tickets. You can define a custom object and custom properties to organize and represent the business data for your company or industry, as well as link (associate) it to standard HubSpot objects.

Here’s how it works:

To learn how to define custom objects, see the "Object Definition" tab, above. That page describes the CRM Object Schemas API, and outlines the endpoints, options, and responses.

  1. You should start by defining your custom object using: POST https://api.hubapi.com/crm-object-schemas/v3/schemas. This defines the object type and property definitions, as well as association definitions with other standard HubSpot objects, e.g. contact. By default, the property type and fieldType are string and text, respectively. The property must exist under properties for them to be specified as primaryDisplayProperty, requiredProperties, searchableProperties, primaryDisplayProperty, and secondaryDisplayProperties. As part of the schema, there are association definitions automatically created between the custom object and email, meeting_event, note, task, call, and conversation_session.
  2. At any time, you can retrieve your custom object using:GET https://api.hubapi.com/crm-object-schemas/v3/schemas/{objectType}, where objectType is either - {meta-type}-{object_type_id} or the fully qualified name - p{portal_id}_{object_name}. The meta-type is always 2 (i.e. portal-specific). You can also retrieve all portal-specific custom objects using:GET https://api.hubapi.com/crm-object-schemas/v3/schemas.
  3. Your object schema can be modified using: PATCH https://api.hubapi.com/crm-object-schemas/v3/schemas/{objectType}. Once your custom object is defined, the name and labels (singular and plural) cannot be changed. Only requiredProperties, searchableProperties, primaryDisplayProperty, and secondaryDisplayProperties can be modified after creation.
  4. To add other associations to your custom object, use: POST https://api.hubapi.com/crm-object-schemas/v3/schemas/{objectType}/associations. You can only associate your custom object with standard HubSpot objects (e.g. contact, company, deal, or ticket) or other custom objects.
  5. You can only delete a custom object after all object instances of that type are deleted. To delete a custom object, use: DELETE https://api.hubapi.com/crm-object-schemas/v3/schemas/{objectType}.

Technical limitations:

  • You are only allowed to create 10 custom objects per portal.
  • You can only delete a custom object type after all object instances of that type, associations, and custom object properties are deleted.
  • You can only associate your custom object with standard HubSpot objects or other custom objects.

Naming guidance:

Choose a label for your object that is clear, easily understood, and different from other objects. Avoid starting custom object labels with the same words.

  • Keep length in mind; long labels may be cut off in certain parts of the product.
  • Avoid using all caps; if possible, only capitalize the first letter of the first word.
  • Check the spelling of singular and plural labels (there’s no way to change these later).

For a more in-depth look at custom objects, check out the HubSpot Academy video Data in HubSpot: Custom Objects and Other Tools

 

Custom Object Example

Note that this is only an example and is not meant to be exhaustive. Please refer to the "Object Definition" tab, above, for more details.

This example scenario will cover:

  1. Creating a custom object schema (i.e. object type, properties, and associations)
  2. Fetching the custom object schema
  3. Creating a custom object instance
  4. Creating an association between the custom object instance and an existing HubSpot contact
  5. Creating a new association definition between the custom object and HubSpot ticket
  6. Creating a new property definition
  7. Updating the object schema (i.e. secondaryDisplayProperties) with the new property

Imagine you own a car dealership, called CarSpot. You want to use HubSpot’s marketing and sales functionality to communicate with potential buyers, track interest, and offer possible deals to them based on your inventory.

 

Creating Object Schema: Let’s get started and define how to represent your car inventory in HubSpot. These are attributes of a car that you want to store in HubSpot. You want to represent a car as having the following properties:

  1. Condition (new or used): enumeration
  2. Date received (at dealership): date
  3. Year: number
  4. Make: string
  5. Model: string
  6. Color: string
  7. Mileage: number
  8. Price: number
  9. Notes: string

For you to connect with potential buyers, you have to first define a relationship between standard HubSpot contacts and your custom car object. By defining an association, the contact and car, it allows you to leverage HubSpot’s marketing and sales platform, to communicate with potential buyers and track your progress on a potential deal.

Once you have your data model finalized, let’s create the object schema using the API. This defines the object and its properties, as well as its association with other standard HubSpot objects, i.e. contact. It also creates a property group named {object_name}_information, i.e. car_information. You do not have to create all your object properties when creating the schema. You can add more properties and update your object schema later.

⇒ POST https://api.hubapi.com/crm-object-schemas/v3/schemas, where the request body"all

JSON
//sample request body

{
  "name": "car",
  "labels": {
    "singular": "Car",
    "plural": "Cars"
  },
  "primaryDisplayProperty": "model",
  "requiredProperties": [
    "year",
    "make",
    "model"
  ],
  "properties": [
    {
      "name": "condition",
      "label": "Condition",
      "type": "enumeration",
      "fieldType": "select",
      "options": [
        {
          "label": "New",
          "value": "new"
        },
        {
          "label": "Used",
          "value": "used"
        }
      ]
    },
    {
      "name": "date_received",
      "label": "Date received",
      "type": "date",
      "fieldType": "date"
    },
    {
      "name": "year",
      "label": "Year",
      "type": "number",
      "fieldType": "number"
    },
    {
      "name": "make",
      "label": "Make",
      "type": "string",
      "fieldType": "text"
    },
    {
      "name": "model",
      "label": "Model",
      "type": "string",
      "fieldType": "text"
    },
    {
      "name": "color",
      "label": "Color",
      "type": "string",
      "fieldType": "text"
    },
    {
      "name": "mileage",
      "label": "Mileage",
      "type": "number",
      "fieldType": "number"
    },
    {
      "name": "price",
      "label": "Price",
      "type": "number",
      "fieldType": "number"
    },
    {
      "name": "notes",
      "label": "Notes",
      "type": "string",
      "fieldType": "text"
    }
  ],
  "associatedObjects": [
    "CONTACT"
  ]
}

The response for this API call would look similar to:Copy all

JSON
//sample response body
{
  "id": "529881",
  "createdAt": "2020-02-23T01:24:54.508774Z",
  "updatedAt": "2020-02-23T01:24:54.508774Z",
  "properties": [
    {
      "updatedAt": "2020-02-23T01:24:54.665Z",
      "createdAt": "2020-02-23T01:24:54.665Z",
      "name": "condition",
      "label": "Condition",
      "type": "enumeration",
      "fieldType": "select",
      "groupName": "car_information",
      "options": [
        {
          "label": "New",
          "value": "new",
          "displayOrder": -1,
          "hidden": false
        },
        {
          "label": "Used",
          "value": "used",
          "displayOrder": -1,
          "hidden": false
        }
      ],
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": false,
      "archived": false,
      "hasUniqueValue": false,
      "hidden": false,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": false,
        "readOnlyValue": false
      },
      "formField": false
    },
    {
      "updatedAt": "2020-02-23T01:24:54.667Z",
      "createdAt": "2020-02-23T01:24:54.667Z",
      "name": "date_received",
      "label": "Date received",
      "type": "date",
      "fieldType": "date",
      "groupName": "car_information",
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": false,
      "archived": false,
      "hasUniqueValue": false,
      "hidden": false,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": false,
        "readOnlyValue": false
      },
      "formField": false
    },
    {
      "updatedAt": "2020-02-23T01:24:54.667Z",
      "createdAt": "2020-02-23T01:24:54.667Z",
      "name": "year",
      "label": "Year",
      "type": "number",
      "fieldType": "number",
      "groupName": "car_information",
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": false,
      "archived": false,
      "hasUniqueValue": false,
      "hidden": false,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": false,
        "readOnlyValue": false
      },
      "formField": false
    },
    {
      "updatedAt": "2020-02-23T01:24:54.670Z",
      "createdAt": "2020-02-23T01:24:54.670Z",
      "name": "model",
      "label": "Model",
      "type": "string",
      "fieldType": "text",
      "groupName": "car_information",
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": false,
      "archived": false,
      "hasUniqueValue": false,
      "hidden": false,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": false,
        "readOnlyValue": false
      },
      "formField": false
    },
    {
      "updatedAt": "2020-02-23T01:24:54.670Z",
      "createdAt": "2020-02-23T01:24:54.670Z",
      "name": "notes",
      "label": "Notes",
      "type": "string",
      "fieldType": "text",
      "groupName": "car_information",
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": false,
      "archived": false,
      "hasUniqueValue": false,
      "hidden": false,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": false,
        "readOnlyValue": false
      },
      "formField": false
    },
    {
      "updatedAt": "2020-02-23T01:24:54.670Z",
      "createdAt": "2020-02-23T01:24:54.670Z",
      "name": "color",
      "label": "Color",
      "type": "string",
      "fieldType": "text",
      "groupName": "car_information",
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": false,
      "archived": false,
      "hasUniqueValue": false,
      "hidden": false,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": false,
        "readOnlyValue": false
      },
      "formField": false
    },
    {
      "updatedAt": "2020-02-23T01:24:54.667Z",
      "createdAt": "2020-02-23T01:24:54.667Z",
      "name": "mileage",
      "label": "Mileage",
      "type": "number",
      "fieldType": "number",
      "groupName": "car_information",
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": false,
      "archived": false,
      "hasUniqueValue": false,
      "hidden": false,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": false,
        "readOnlyValue": false
      },
      "formField": false
    },
    {
      "updatedAt": "2020-02-23T01:24:54.667Z",
      "createdAt": "2020-02-23T01:24:54.667Z",
      "name": "price",
      "label": "Price",
      "type": "number",
      "fieldType": "number",
      "groupName": "car_information",
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": false,
      "archived": false,
      "hasUniqueValue": false,
      "hidden": false,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": false,
        "readOnlyValue": false
      },
      "formField": false
    },
    {
      "updatedAt": "2020-02-23T01:24:54.667Z",
      "createdAt": "2020-02-23T01:24:54.667Z",
      "name": "make",
      "label": "Make",
      "type": "string",
      "fieldType": "text",
      "groupName": "car_information",
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": false,
      "archived": false,
      "hasUniqueValue": false,
      "hidden": false,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": false,
        "readOnlyValue": false
      },
      "formField": false
    }
  ],
  "associations": [
  {
    "id": "1",
    "createdAt": null,
    "updatedAt": null,
    "fromObjectTypeId": "2-529881",
    "toObjectTypeId": "0-1",
    "name": "car_to_contact"
  },
  {
    "id": "2",
    "createdAt": null,
    "updatedAt": null,
    "fromObjectTypeId": "0-1",
    "toObjectTypeId": "2-529881",
    "name": "car_to_contact"
  },
  {
    "id": "13",
    "createdAt": null,
    "updatedAt": null,
    "fromObjectTypeId": "2-529881",
    "toObjectTypeId": "0-27",
    "name": "car_to_task"
  },
  {
    "id": "14",
    "createdAt": null,
    "updatedAt": null,
    "fromObjectTypeId": "0-27",
    "toObjectTypeId": "2-529881",
    "name": "car_to_task"
  },
  {
    "id": "15",
    "createdAt": null,
    "updatedAt": null,
    "fromObjectTypeId": "2-529881",
    "toObjectTypeId": "0-46",
    "name": "car_to_note"
  },
  {
    "id": "16",
    "createdAt": null,
    "updatedAt": null,
    "fromObjectTypeId": "0-46",
    "toObjectTypeId": "2-529881",
    "name": "car_to_note"
  },
  {
    "id": "7",
    "createdAt": null,
    "updatedAt": null,
    "fromObjectTypeId": "2-529881",
    "toObjectTypeId": "0-48",
    "name": "car_to_call"
  },
  {
    "id": "8",
    "createdAt": null,
    "updatedAt": null,
    "fromObjectTypeId": "0-48",
    "toObjectTypeId": "2-529881",
    "name": "car_to_call"
  },
  {
    "id": "11",
    "createdAt": null,
    "updatedAt": null,
    "fromObjectTypeId": "2-529881",
    "toObjectTypeId": "0-51",
    "name": "car_to_conversation_session"
  },
  {
    "id": "12",
    "createdAt": null,
    "updatedAt": null,
    "fromObjectTypeId": "0-51",
    "toObjectTypeId": "2-529881",
    "name": "car_to_conversation_session"
  },
  {
    "id": "9",
    "createdAt": null,
    "updatedAt": null,
    "fromObjectTypeId": "2-529881",
    "toObjectTypeId": "0-49",
    "name": "car_to_email"
  },
  {
    "id": "10",
    "createdAt": null,
    "updatedAt": null,
    "fromObjectTypeId": "0-49",
    "toObjectTypeId": "2-529881",
    "name": "car_to_email"
  },
  {
    "id": "5",
    "createdAt": null,
    "updatedAt": null,
    "fromObjectTypeId": "2-529881",
    "toObjectTypeId": "0-47",
    "name": "car_to_meeting_event"
  },
  {
    "id": "6",
    "createdAt": null,
    "updatedAt": null,
    "fromObjectTypeId": "0-47",
    "toObjectTypeId": "2-529881",
    "name": "car_to_meeting_event"
  }
  ],
  "labels": {
    "singular": "Car",
    "plural": "Cars"
  },
  "requiredProperties": [
    "year",
    "model",
    "make"
  ],
  "searchableProperties": [],
  "primaryDisplayProperty": "model",
  "secondaryDisplayProperties": [],
  "name": "car"
}

Take note of the new id. This is used in combination with the metaType (which is always 2 for portal-specific objects) to generate the objectTypeId. The objectTypeId for the car object is: 2-529881.


Fetching Object Schema: In this example, you can retrieve the object schema at any time using it’s objectTypeId (2-529881) or fully qualified name (p99688696_car). The objectTypeId is defined as {meta-type}-{object_type_id}, where the meta-type is always 2 (portal-specific). The fully qualified name is defined as p{portal_id}_{object_name}. The fully qualified name is more readable than the objectTypeId.

GET https://api.hubapi.com/crm-object-schemas/v3/schemas/p99688696_car?portalId=99688696


Create Object Instance: You now start using HubSpot to communicate with potential buyers, track potential interest, and offer possible deals for them based on your inventory. Below is an example of creating a new car and associating it with an existing contact (ID 34051).

POST https://api.hubapi.com/crm/v3/objects/p99688696_car?portalId=99688696, where the request body is:

JSON
// Sample request body
{
  "properties": {
    "condition": "used",
    "date_received": "1582416000000",
    "year": "2014",
    "make": "Nissan",
    "model": "Frontier",
    "color": "White",
    "mileage": "80000",
    "price": "12000",
    "notes": "Excellent condition. No accidents."
  }
}

The response for this API call would look similar to:Copy all

JSON
//Sample response
{
  "id": "181308",
  "properties": {
    "color": "White",
    "condition": "used",
    "make": "Nissan",
    "mileage": "80000",
    "model": "Frontier",
    "notes": "Excellent condition. No accidents.",
    "price": "12000",
    "year": "2014",
    "date_received": "1582416000000"
  },
  "createdAt": "2020-02-23T01:44:11.035Z",
  "updatedAt": "2020-02-23T01:44:11.035Z",
  "archived": false
}

Take note of the new id. This is the object ID, which you will use below to create an association between the car and an existing contact. At any point, you can retrieve the object and its properties again by using:

⇒ GET  https://api.hubapi.com/crm/v3/objects/p99688696_car/181308portalId=99688696&properties=year&properties=make&properties=model


Create Association to Object Instance: With the new object ID in hand and a target contact ID, we can create an association between the new car instance and the contact. For this example, we are creating an association with contact ID 51 (Brian Halligan).

POST https://api.hubapi.com/crm/v3/associations/p99688696_car/contact/batch/create?portalId=99688696, where the request body is:

JSON
//Sample request
{
  "inputs": [
    {
      "from": {
        "id": "181308"
      },
      "to": {
        "id": "51"
      },
      "type": "car_to_contact"
    }
  ]
}

The response for this API call would look similar to:Copy all

JSON
//Sample response
{
  "results": [
    {
      "from": {
        "id": "181308"
      },
      "to": {
        "id": "51"
      }
    },
    {
      "from": {
        "id": "51"
      },
      "to": {
        "id": "181308"
      }
    }
  ],
  "status": "COMPLETE",
  "startedAt": "2020-02-23T01:48:14.992046Z",
  "completedAt": "2020-02-23T01:48:15.102401Z"
}

Note that the response returns two associations. This is expected because HubSpot defines associations in both directions - car → contact and contact → car.


New Association Definition: Your car dealership is doing well. Now, you want to start tracking services done on the cars you have sold. We can use HubSpot tickets to log any maintenance performed. Let’s create a new relationship between cars and tickets by creating a new association.

⇒ POST https://api.hubapi.com/crm-object-schemas/v3/schemas/p99688696_car/associations?portalId=99688696, where the request body is:

JSON
// Sample request
{
"fromObjectTypeId": "2-529881",
"toObjectTypeId": "ticket"
}

The response for this API call would look similar to:Copy all

JSON
// Sample response
{
  "id": "121",
  "createdAt": "2020-02-23T01:52:12.893826Z",
  "updatedAt": "2020-02-23T01:52:12.893826Z",
  "fromObjectTypeId": "2-529881",
  "toObjectTypeId": "0-5",
  "name": "car_to_ticket"
}

For the toObjectTypeId, you can specify the HubSpot object type name or it’s objectTypeId: contact (objectTypeId: 0-1), company (objectTypeId: 0-2), deal (objectTypeId: 0-3), or ticket (objectTypeId: 0-5).


New Property Definition: Your customers are happy with their cars. Business is doing great. Now you start offering maintenance packages for your customers, e.g. oil change at every 5k miles, and you want to track them by adding a new property.

POST https://api.hubapi.com/crm/v3/properties/2-529881?portalId=99688696, where the request body is:

JSON
// Sample request
{
  "groupName": "car_information",
  "name": "maintenance_package",
  "label": "Maintenance Package",
  "type": "enumeration",
  "fieldType": "select",
  "options": [
    {
      "label": "Basic",
      "value": "basic"
    },
    {
      "label": "Oil change only",
      "value": "oil_change_only"
    },
    {
      "label": "Scheduled",
      "value": "scheduled"
    }
  ]
}

The response for this API call would look similar to:Copy all

JSON
// Sample response
{
  "updatedAt": "2020-02-23T02:08:20.055Z",
  "createdAt": "2020-02-23T02:08:20.055Z",
  "name": "maintenance_package",
  "label": "Maintenance Package",
  "type": "enumeration",
  "fieldType": "select",
  "groupName": "car_information",
  "options": [
    {
      "label": "Basic",
      "value": "basic",
      "displayOrder": -1,
      "hidden": false
    },
    {
      "label": "Oil change only",
      "value": "oil_change_only",
      "displayOrder": -1,
      "hidden": false
    },
    {
      "label": "Scheduled",
      "value": "scheduled",
      "displayOrder": -1,
      "hidden": false
    }
  ],
  "displayOrder": -1,
  "calculated": false,
  "externalOptions": false,
  "archived": false,
  "hasUniqueValue": false,
  "hidden": false,
  "modificationMetadata": {
    "archivable": true,
    "readOnlyDefinition": false,
    "readOnlyValue": false
  },
  "formField": false
}

You want to make this new field show up on the HubSpot sidebar as a secondary property, so you can quickly identify the level of service when a car comes into the shop.

PATCH https://api.hubapi.com/crm-object-schemas/v3/schemas/p99688696_car?portalId=99688696, where the request body is:Copy all

JSON
// Sample request
{
  "secondaryDisplayProperties": [
    "maintenance_package"
  ]
}

The response for this API call would look similar to:Copy all

JSON
// Sample response
{
  "id": "529881",
  "createdAt": "2020-02-23T01:24:54.537Z",
  "updatedAt": "2020-02-23T02:12:24.175874Z",
  "labels": {
    "singular": "Car",
    "plural": "Cars"
  },
  "requiredProperties": [
    "year",
    "model",
    "make"
  ],
  "searchableProperties": [],
  "primaryDisplayProperty": "model",
  "secondaryDisplayProperties": [
    "maintenance_package"
  ],
  "portalId": 99688696,
  "name": "car"
}

Now, when you open a contact that is associated with a car, it will look like the following:

Screen Shot 2020-03-06 at 11.08.33 AM

 

The HubSpot sidebar for cars will look like the following:

Screen Shot 2020-03-06 at 11.08.41 AM

Now you have mastered custom objects! This was only a simple example for you to get started. The HubSpot public APIs offer a wide range of possibilities. Please explore Objects and Object Definition tabs for more information.