CRM Extensions API Overview

The CRM Extensions API allows HubSpot users to interact with information stored in other systems.

How it works

Using this API, an integration can surface its own data in a CRM card for contacts, companies, deals, or tickets. When a HubSpot user views one of these CRM records, HubSpot makes an outbound request, retrieves information for that record, then displays it in a CRM card in the sidebar. In addition to displaying data, integrations can also specify the actions a user will be able to take. This includes opening a modal window and allowing your integration to appear seamlessly inside the HubSpot CRM.

Use case for this API: Your engineering team uses one software service to track bugs, but your customer service reps use HubSpot. Your reps want to see information about their customers’ bugs. The CRM Extensions API not only allows reps to see existing bugs, but also report new ones right from HubSpot.

Here's an example of what a CRM card looks like:

sidebar example

The whole card is defined by a record type the integrator can specify. Individual items within the card are card properties. The titles for these card properties can optionally be turned into links to the external system.

Record types are created within your developer account. On the Apps dashboard, choose the app you want to integrate and go to CRM cards. When a user installs the application, HubSpot will start sending data fetch requests for record types associated with it. (Detailed instructions here.)

The data flow for how card properties show up on the CRM looks like this:

Sales Object data flow diagram

When a user loads the details page for a record in the CRM, HubSpot will send a data fetch request to the integrator. The integrator will respond to this request with the card properties associated with the current CRM object.

Scope requirements

In order to use CRM Extensions, you will need to request the appropriate OAuth scopes for any CRM objects that you want to create a card for. This will mean that you need the contacts scope if you want to set up a card for contacts, companies, or deals, and the tickets scope if you want to create a card for tickets. See the OAuth documentation for more details about scopes and setting up the authorization URL for your app.

If your integration is already using CRM Extensions, you will not be able to remove the contacts or tickets scopes until you delete all of the CRM cards that have been set up for the associated CRM object types.

Record Type API

Object Types show the integrator's intent to provide some objects associated with data in the CRM. Creating an Object Type is the first step in using CRM Extensions.

Record type fields

This section describes the different fields on the record type.

  • applicationId: The ID of the application that should own this record type.
  • baseUris: A list of acceptable base URIs for actions. All action URIs must be located under one of these paths
  • dataFetchUri: The URI the CRM will call to fetch data for a given CRM object specified in associatedHubSpotObjectTypes.
  • title: The title of this object. This will be displayed as the title of the CRM sidebar card.
  • propertyDefinitions: The common properties that these objects will have, in the order they should appear on the sidebar card. See Property Types for more info.
  • associatedHubSpotObjectTypes: A set of CONTACT, COMPANY, DEAL, or TICKET. This determines where on the CRM this Object Type will appear -- contact, company, deal, or ticket details pages.
  • associatedHubSpotObjectTypeProperties: HubSpot properties to send in the data fetch request, by CRM Object Type.

Example record type:

{
  "applicationId": 767676,
  "baseUris": [
    "https://example.com/actions"
  ],
  "dataFetchUri": "https://example.com/demo-tickets",
  "title": "DemoTickets",
  "propertyDefinitions": [
    {
      "name": "ticket_type",
      "label": "Ticket type",
      "dataType": "STRING"
    },
    {
      "options": [
        {
          "type": "SUCCESS",
          "label": "In Progress",
          "name": "inprogress"
        },
        {
          "type": "DEFAULT",
          "label": "Resolved",
          "name": "resolved"
        }
      ],
      "name": "status",
      "label": "Status",
      "dataType": "STATUS"
    },
    {
      "name": "priority",
      "label": "Priority",
      "dataType": "STRING"
    },
    {
      "name": "project",
      "label": "Project",
      "dataType": "STRING"
    }
  ],
  "associatedHubSpotObjectTypes": [
    "COMPANY"
  ],
  "associatedHubSpotObjectTypeProperties": {
    "COMPANY": [
      "domain"
    ]
  }
}

The following shows how the different record type properties show up to the user:

object type annoations

Managing record types through the API

See the specific docs for managing record types through the API:

Managing CRM cards in a developer account

You can also create and manage record types from your developer account. In your app settings, you can create record type(s) from the CRM cards tab.

 crm_card_dashboard

Once you create a new CRM card, you can also manage its settings by clicking on the name of the card.

crm_card_settings

Note: You can create up to 25 record types/CRM cards

Data fetch request

HubSpot makes a data fetch request to the integration when a HubSpot user views a CRM record. This request happens each time a user visits the specified associatedHubSpotObjectTypes for the CRM extension type, currently one or more of contacts, companies, deal, or tickets.

HubSpot will make a separate request for each record type. HubSpot will send the request to the dataFetchUri specified on the record type.

The response to this data fetch request should contain information to display to the user at that time. It can contain up to five card properties. If more card properties are available for this specific CRM object, the integrator can link to them.

Example request

GET https://example.com/demo-tickets?userId=12345&userEmail=testuser@example.com&associatedObjectId=78912&associatedObjectType=COMPANY&portalId=9999999&domain=testcompany.com

With a header:

X-HubSpot-Signature: <some base64 string>

The query parameters provided in this request are:

  • userId: The numeric userId of the customer requesting data.
  • userEmail: The user address, in HubSpot, of the user requesting data.
  • associatedObjectId: The ID for the current object, depending on the associatedObjectType. This will be the companyId, dealId, contact vid, or objectId for tickets.
  • associatedObjectType: The type of object the user is requesting data about (CONTACT, COMPANY, DEAL, or TICKET). This will be one of the associatedHubSpotObjectTypes provided for this record type.
  • And values for any of the requested associatedHubSpotObjectTypeProperties. If one of the request properties is not defined for the current object, it will not be listed in the query string. In the example above, the company property domain is included. Request properties can be defined in the record type interface, per the screenshot below: 

 

Screen Shot 2019-04-05 at 9.59.03 AM

The X-HubSpot-Signature header provides a way for the integrator to verify this request came from HubSpot. See Request Signatures for details.

Example response

This is an example response the integrator should provide to the above request.

{
  "results": [
    {
      "objectId": 245,
      "title": "API-22: APIs working too fast",
      "link": "http://example.com/1",
      "created": "2016-09-15",
      "priority": "HIGH",
      "project": "API",
      "reported_by": "msmith@hubspot.com",
      "description": "Customer reported that the APIs are just running too fast. This is causing a problem in that they're so happy.",
      "reporter_type": "Account Manager",
      "status": "In Progress",
      "ticket_type": "Bug",
      "updated": "2016-09-28",
      "actions": [
        {
          "type": "IFRAME",
          "width": 890,
          "height": 748,
          "uri": "https://example.com/edit-iframe-contents",
          "label": "Edit",
          "associatedObjectProperties": []
        },
        {
          "type": "IFRAME",
          "width": 890,
          "height": 748,
          "uri": "https://example.com/reassign-iframe-contents",
          "label": "Reassign",
          "associatedObjectProperties": []
        },
        {
          "type": "ACTION_HOOK",
          "httpMethod": "PUT",
          "associatedObjectProperties": [],
          "uri": "https://example.com/tickets/245/resolve",
          "label": "Resolve"
        },
        {
          "type": "CONFIRMATION_ACTION_HOOK",
          "confirmationMessage": "Are you sure you want to delete this ticket?",
          "confirmButtonText": "Yes",
          "cancelButtonText": "No",
          "httpMethod": "DELETE",
          "associatedObjectProperties": [
            "protected_account"
          ],
          "uri": "https://example.com/tickets/245",
          "label": "Delete"
        }
      ],
    },
    {
      "objectId": 988,
      "title": "API-54: Question about bulk APIs",
      "link": "http://example.com/2",
      "created": "2016-08-04",
      "priority": "HIGH",
        "project": "API",
        "reported_by": "ksmith@hubspot.com",
        "description": "Customer is not able to find documentation about our bulk Contacts APIs.",
        "reporter_type": "Support Rep",
        "status": "Resolved",
        "ticket_type": "Bug",
        "updated": "2016-09-23",
        "properties": [
          {
            "label": "Resolved by",
            "dataType": "EMAIL",
            "value": "ijones@hubspot.com"
          },
          {
            "label": "Resolution type",
            "dataType": "STRING",
            "value": "Referred to documentation"
          },
          {
            "label": "Resolution impact",
            "dataType": "CURRENCY",
            "value": "94.34",
            "currencyCode": "GBP"
          }
        ],
        "actions": [
          {
            "type": "IFRAME",
            "width": 890,
            "height": 748,
            "uri": "https://example.com/edit-iframe-contents",
            "label": "Edit"
          },
          {
            "type": "CONFIRMATION_ACTION_HOOK",
            "confirmationMessage": "Are you sure you want to delete this ticket?",
            "confirmButtonText": "Yes",
            "cancelButtonText": "No",
            "httpMethod": "DELETE",
            "associatedObjectProperties": [
              "protected_account"
            ],
            "uri": "https://example.com/tickets/245",
            "label": "Delete"
          }
        ]
      }
    ],
    "settingsAction": {
      "type": "IFRAME",
      "width": 890,
      "height": 748,
      "uri": "https://example.com/settings-iframe-contents",
      "label": "Settings"
    },
    "primaryAction": {
      "type": "IFRAME",
      "width": 890,
      "height": 748,
      "uri": "https://example.com/create-iframe-contents",
      "label": "Create Ticket"
    }
  }

Definitions for the fields in the response body:

  • results : A list of up to five valid card properties. See the card properties section for details.
  • totalCount : The total number of card properties available, if there are more than five related to the requested CRM object. This property is optional.
  • allItemsLink: A URI that shows all card properties associated with the requested CRM object, if there are more than five. This property is optional.
  • itemLabel: The label to be used in the "see more" link. (e.g. "See more tickets") If not provided, this falls-back to the record type's Title. This property is optional.
  • settingsAction: An iFrame Action that gives users the ability to update settings for this integration. See Action Types for details on specifying Actions. This property is optional.
  • primaryAction: The primary action for a record type. This is typically a "creation" action. See Action Types for details on specifying Actions. This property is optional.
  • secondaryActions: A list of actions to display on the card level. See Action Types for details on specifying Actions. This property is optional.

data fetch annotations

Card properties 

JSON for an example card property that is returned in the example response above:

    {
      "objectId": 988,
      "title": "API-54: Question about bulk APIs",
      "link": "http://example.com/2",
      "created": "2016-08-04",
      "priority": "HIGH",
      "project": "API",
      "reported_by": "ksmith@hubspot.com",
      "description": "Customer is not able to find documentation about our bulk Contacts APIs.",
      "reporter_type": "Support Rep",
      "status": "Resolved",
      "ticket_type": "Bug",
      "updated": "2016-09-23",
      "properties": [
        {
          "label": "Resolved by",
          "dataType": "EMAIL",
          "value": "ijones@hubspot.com"
        },
        {
          "label": "Resolution type",
          "dataType": "STRING",
          "value": "Referred to documentation"
        },
        {
          "label": "Resolution impact",
          "dataType": "CURRENCY",
          "value": "94.34",
          "currencyCode": "GBP"
        }
      ],
      "actions": [
        {
          "type": "IFRAME",
          "width": 890,
          "height": 748,
          "uri": "https://tools.hubteam.com/integrations-iframe-test-app",
          "label": "Edit"
        },
        {
          "type": "CONFIRMATION_ACTION_HOOK",
          "confirmationMessage": "Are you sure you want to delete this ticket?",
          "confirmButtonText": "Yes",
          "cancelButtonText": "No",
          "httpMethod": "DELETE",
          "associatedObjectProperties": [
            "protected_account"
          ],
          "uri": "https://api.hubapi.com/linked-sales-objects-test-application/v1/actions/demo-ticket/988",
          "label": "Delete"
        }
      ]
    }

Properties:

  • objectId: A unique ID for this object. This property is required.
  • title: The title of this object. This property is required.
  • link: The URI the user can follow to get more details about this object. This property is optional, but if not all objects have a link you should provide a value of null.
  • properties: A list of custom properties that are not defined in the record type. You can use this list to display properties unique to this specific object. These properties will be shown after the properties defined in the object-type in the order they're provided. This property is optional.
  • actions: A list of actions that are available to take on this object. See Action Types for details on specifying Actions. This property is optional.

 

In addition to the above properties, the integrator can provide values for the properties defined on the object-type. In the above example the created JSON property:

"created":"2016-08-04"

Is providing a value for this object for the created property defined on the record type.

sales object annotations

 

Handling action hooks 

When a user clicks on an action that is defined as an action hook, HubSpot will send a request using the URI and HTTP method specified in the action definition.

Example Request

DELETE https://example.com/tickets/245?userId=12345&userEmail=testuser@example.com&associatedObjectId=78912&associatedObjectType=COMPANY&portalId=9999999&domain=testcompany.com

With a header:

X-HubSpot-Signature: <some base64 string>

The query parameters provided in this request are:

  • userId: The numeric userId of the customer requesting data.
  • userEmail: The user address, in HubSpot, of the user requesting data.
  • associatedObjectId: The ID for the current object, depending on the associatedObjectType. This will be the companyId, dealId, contact vid, or objectId for tickets..
  • associatedObjectType: The type of object the user is requesting data about (CONTACT, COMPANY, DEAL, or TICKET).
  • portalId: The portal ID (also called Hub ID) of the customer that is requesting data. This will be a customer that has installed your integration.
  • And values for any of the requested associatedObjectProperties. If one of the request properties is not defined for the current object, it will not be listed in the query string. In the above example, the company property domain is included.

The X-HubSpot-Signature header provides a way for the integrator to verify this request came from HubSpot. See Request Signatures for details.

Example Response

{
  "message": "Successfully deleted object"
}

HubSpot will attempt to parse responses to action hooks as JSON and look for a message property. HubSpot will display this message to the user on either success or failure.

Response status code of 2xx will show a success message and status codes of 4xx or 5xx will show an error message.

Action types

The following sections provide details about each type of action that can be specified.

iFrame actions

When a user clicks an IFrame action, a modal dialog will open containing an iFrame pointing at the provided URL.

Example iFrame action:

{
  "type": "IFRAME",
  "width": 890,
  "height": 748,
  "uri": "https://example.com/iframe-contents",
  "label": "Edit",
  "associatedObjectProperties": [
    "some_crm_property"
  ]
}
  • type: Should be IFRAME to indicate that this is an IFrame action.
  • width, height: The dimensions that the iframe should be.
  • uri: The URI to open inside the iframe.
  • label: The label to display in the action dropdown.
  • associatedObjectProperties: A list of properties on the associated contact, company, deal, or ticket. The values of the properties for the current object will be appended to the uri as query parameters when opening the iframe.

Signaling for the modal to close

When the user is done completing an action inside the iframe the modal dialog should close, returning the user back to the CRM screen they started from. To close the dialog model, the integrator can use window.postMessage to signal to the CRM that the user is done. The following messages are accepted.

  • {"action": "DONE"} - The user has successfully completed the action.
  • {"action": "CANCEL"} - The user has canceled the action.

Example: window.parent.postMessage(JSON.stringify({"action": "DONE"}), "*");

Note: Iframe modals accessed through an iFrame action will have a responsive width. 

Action hook actions

Action hook actions send a server-side request to the integrator. The only UI a users sees for this action is a success or error message. This type of action is useful for simple operations that require no further input from the user.

Example action hook action definition:

{
  "type": "ACTION_HOOK",
  "httpMethod": "POST",
  "uri": "https://example.com/action-hook",
  "label": "Example action",
  "associatedObjectProperties": [
    "some_crm_property"
  ]
}
  • type: Should be ACTION_HOOK to indicate that this is an action hook action.
  • httpMethod: The HTTP method to use when making the request. This can be GET, POST, PUT, DELETE, or PATCH.
  • uri: The URI of the request to make.
  • label: The label to display in the action drop-down.
  • associatedObjectProperties: A list of properties on the associated contact, company, deal, or ticket. If httpMethod is GET or DELETE than these property values will be appended to the URI of the request as query parameters. Otherwise they will be sent as a JSON request body.

See Handling Action Hooks for more details on how to implement the action endpoint.

Confirmation actions

Confirmation actions behave the same as action hooks except that a confirmation dialog is shown to the user before running the server-side request.

{
  "type": "CONFIRMATION_ACTION_HOOK",
  "httpMethod": "POST",
  "uri": "https://example.com/action-hook",
  "label": "Example action",
  "associatedObjectProperties": [
    "some_crm_property"
  ],
  "confirmationMessage": "Are you sure you want to run example action?",
  "confirmButtonText": "Yes",
  "cancelButtonText": "No"
}
  • type: Should be CONFIRMATION_ACTION_HOOK to indicate that this is a confirmation action hook action.
  • httpMethod: The HTTP method to use when making the request. This can be GET, POST, PUT, DELETE, or PATCH.
  • uri: The URI of the request to make.
  • label: The label to display in the action drop-down.
  • associatedObjectProperties: A list of properties on the associated contact, company, deal, or ticket. If httpMethod is GET or DELETE than these property values will be appended to the URI of the request as query parameters. Otherwise they will be sent as a JSON request body.
  • confirmationMessage: The message to display to the user in the confirmation dialog.
  • confirmButtonText: The text for the "OK" button. This is optional, the button text will default to "OK".
  • cancelButtonText: Text text for the "Cancel" button. This is optional, the button text will default to "Cancel".

See Handling Action Hooks for more details on how to implement the action endpoint.

Request signatures 

Note: Requests made for CRM Extensions use the v2 version of the X-HubSpot-Signature header. Please see this page for more details on validating this version of the signature, and this page for more details about validating requests in general.

To ensure that the requests you're getting at your data fetch URIs and action hook URIs are actually coming from HubSpot, we populate a request header named X-HubSpot-Signature. This header will contain a hash of the of the app secret for your application and the details of the request.

To verify this signature, perform the following steps:

  1. Create a string that concatenates together the following: App secret + http method + URI + request body (if present)
  2. Create a SHA-256 hash of the resulting string.
  3. Compare the hash value to the signature. If they're equal, the request passed validation. If these values do not match, the request may have been tampered with in transit or someone may be spoofing requests to your endpoint.

Docs for this section or API