Calling Extensions SDK

Please note: Our calling app partners no longer need to create and update call engagements manually; HubSpot will do it for them. Learn more here.

The Calling Extensions SDK allows apps to provide a custom calling option to HubSpot users directly from a record in the CRM. 

A calling extension consists of three main components:

  1. The Calling Extensions SDK, a JavaScript SDK that enables communication between your app and HubSpot.
  2. The calling settings endpoints, which are used to set the calling settings for your app. Each HubSpot account that connects to your app will use these settings.
  3. The calling iframe, which is where your app appears to HubSpot users and is configured using the calling settings endpoints.

For more information on the in-app calling experience, review this knowledge base article. Once your calling extension-enabled app is connected to HubSpot, it will appear as an option in the call switcher whenever a user makes a call from HubSpot.

If you don't have an app, you can create one from your HubSpot developer account. If you don't already have a HubSpot developer account, sign up for one here.

Please note: only outgoing calls are currently supported.

Run the demo calling app

You have the option to test the Calling Extensions SDK on two different demo apps:

  • The demo-minimal-js features a minimal implementation of the SDK using JavaScript, HTML, and CSS. View how the SDK is instantiated in index.js.
  • The demo-react-ts features a real-life implementation of the SDK using React, TypeScript, and Styled Components to act as a blueprint for your app. View how the SDK is instantiated in useCti.ts.

Please note: these demo apps aren't fully functional calling apps and use mock data to provide a more realistic experience.

Install the demo calling app

You can run the demo apps with or without installation. To install the demo on your local environment:

  1. Install Node.js on your environment.
  2. Clone, fork, or download the ZIP of this repository.
  3. Open your terminal, and navigate to the root directory of the project.
  4. Run one of the following commands:
      • For the demo-minimal-js:
cd demos/demo-minimal-js && npm i && npm start
  • For the demo-react-ts:
cd demos/demo-react-ts && npm i && npm start

These will switch to the desired demo directory, install the Node.js dependencies required for the project using the npm CLI, and start the app. 

Please note: the npm start command will automatically open a new tab in your browser at https://localhost:9025/, and you may need to bypass a "Your connection is not secure" warning in order to access the application.

Launch the demo calling app from HubSpot

  1. Navigate to your records:.
    • Contacts: In your HubSpot account, navigate to Contacts > Contacts.
    • Company: In your HubSpot account, navigate to Contacts > Companies.
  2. Open your browser's developer console, and run the following command:
    • If you've completed the installation steps, for the demo-minimal-js or the demo-react-ts:
localStorage.setItem("LocalSettings:Calling:installDemoWidget", "local");
    • If you've skipped the installation steps:
      • For the demo-minimal-js:
localStorage.setItem("LocalSettings:Calling:installDemoWidget", "app:js");
  • For the demo-react-ts:
localStorage.setItem("LocalSettings:Calling:installDemoWidget", "app");
  1. Refresh the page, and click the Call icon in the left sidebar. Click the Call from dropdown menu, and select the name of the demo app from step 2 (e.g. Demo App Local, Demo App JS, Demo App React). 
    call-app-in-record
  2. Click Call to see how the demo app integrates with HubSpot via the Calling Extensions SDK. You can also see the events logged to your browser's developer console.

calling-sdk-in-app

 

Install the Calling Extensions SDK on your calling app

To add the Calling Extensions SDK as a Node.js dependency to your calling app:

  • For npm, run:
npm i --save @hubspot/calling-extensions-sdk
  • For yarn, run:
yarn add @hubspot/calling-extensions-sdk

Typical message flow between the calling app and HubSpot

The Calling Extensions SDK exposes a simple API for HubSpot and a calling app to exchange messages. The messages are sent through methods exposed by the SDK and received through eventHandlers.

The following is a description of the events:

  1. Dial number: HubSpot sends the dial number event.
  2. Outbound call started: App notifies HubSpot when the call is started.
  3. Create engagement: HubSpot creates a call engagement with minimal information if requested by the app.
  4. Engagement created: HubSpot created an engagement.
  5. EngagementId sent to App: HubSpot sends the engagementId to the app.
  6. Call ended: App notifies when the call is ended.
  7. Call completed: App notifies when the user is done with the app user experience.
  8. Update engagement: App fetches the engagement by the engagementId, then merges and updates the engagement with additional call details. Learn more about updating a call engagement.

Using the Calling Extensions SDK

Create an instance

To begin, create an instance of the CallingExtensions object. You can define the behavior of your extension by providing an option's object when you create your extensions instance. This option's object provides an eventHandlers field where you can specify the behavior of your extension. The following code block illustrates the available options and event handlers you can define:

import CallingExtensions from "@hubspot/calling-extensions-sdk"; const options = { /** @property {boolean} debugMode - Whether to log various inbound/outbound debug messages to the console. If false, console.debug will be used instead of console.log */ debugMode: true | false, // eventHandlers handle inbound messages eventHandlers: { onReady: () => { /* HubSpot is ready to receive messages. */ }, onDialNumber: event => { /* HubSpot sends a dial number from the contact */ }, /** onEngagementCreated will be @deprecated in 2024 */ onEngagementCreated: event => { /* HubSpot has created an engagement for this call. */ }, onCreateEngagementSucceeded: event => { /* HubSpot has created an engagement for this call. */ } onEngagementCreatedFailed: event => { /* HubSpot has failed to create an engagement for this call. */ } onUpdateEngagementSucceeded: event => { /* HubSpot has updated an engagement for this call. */ }, onUpdateEngagementFailed: event => { /* HubSpot has failed to update an engagement for this call. */ } onVisibilityChanged: event => { /* Call widget's visibility is changed. */ } } }; const extensions = new CallingExtensions(options);

Sending messages to HubSpot

The extensions object provides the following event handlers that you can invoke to send messages to HubSpot or to specify other associated behavior. See examples below.

  • initialized: send a message indicating that the soft phone is ready for interaction. 
// The initialized call allows you to send a message indicating that the soft phone is ready for interaction. const payload = { // Whether a user is logged-in isLoggedIn: true|false, // Optionally send the desired widget size sizeInfo: { height: number, width: number } } extensions.initialized(payload);
  • userLoggedIn: sends a message indicating that the user has logged in.
// Sends a message indicating that user has logged in // This message is only needed when user isn't loged in when initialized extensions.userLoggedIn();
  • userLoggedOut: sends a message indicating that the user has logged out.
// Sends a message indicating that user has logged out extensions.userLoggedOut();
  • outgoingCall: sends a message to notify HubSpot that an outgoing call has started. 
// Sends a message to notify HubSpot that an outgoing call has started. const callInfo = { phoneNumber: string, // optional unless call is initiated by the widget createEngagement: true, // whether HubSpot should create an engagement for this call callStartTime: number // optional unless call is initiated by the widget }; extensions.outgoingCall(callInfo);
  • callAnswered: sends a message to notify HubSpot that an outgoing call is being answered.
extensions.callAnswered();
  • callEnded: sends a message to notify HubSpot that the call has ended.
// Sends a message to notify HubSpot that the call has ended. // After receiving the call ended event, the user can navigate away, can close the call widget. extensions.callEnded();
  • callCompleted: sends a message to notify HubSpot that the call has completed. Engagement properties are owned by HubSpot, and no longer need to be created or updated manually (see highlighted).
Please note: the hideWidget property will be ignored when the user is in a task queue with the Call task type.
// Sends a message to notify HubSpot that the call has completed. // After receiving the call completed event, HubSpot will // 1) insert the engagement into the timeline // 2) set the default associations on the engagement // 3) closes the widget unless `hideWidget` is set to false. // 4) update the engagement with any engagement properties const data = { engagementId: number, hideWidget: boolean, // (optional) defaults to true engagementProperties?: { [key: string]: string } // opt in to hs owned engagements by adding properties in https://developers.hubspot.com/docs/api/crm/calls#properties extensions.callCompleted(data);
  • sendError: sends a message to notify HubSpot that the calling app has encountered an error.
// Sends a message to notify HubSpot that the call widget has encountered an error. // After receiving the sendError event, HubSpot will display an alert popup to the user with the error message provided. const data = { message: string // error message to be displayed in the alert popup }; extensions.sendError(data);
  • resizeWidget: sends a message to notify HubSpot that the calling app needs to be resized.
// Sends a message to notify HubSpot that the call widget needs to be resized. // After receiving the resizeWidget event, HubSpot will use the provided height and width to resize the call widget. const data = { height: boolean // desired height of the call widget width: number, // desired width of the call widget }; extensions.resizeWidget(data);

Receive messages from HubSpot

The extensions object provides the following event handlers that you can invoke when receiving messages in HubSpot or to specify other associated behavior. See examples below.

  • onReady: message indicating that HubSpot is ready to receive messages.
// Message indicating that HubSpot is ready to receive messages onReady() { // Send initialized message to HubSpot to indicate that the call widget is also ready extensions.initialized(payload); ... }
  • onDial: message indicating that the user has triggered an outbound call.
// Message indicating that user has triggered an outbound call onDialNumber(data) { const { /* The phone nubmer to dial */ phoneNumber: string, /* The id of the logged in user. */ ownerId: number, /* The id of the hubspot account */ portalId: number, /* HubSpot object Id of the phoneNumber */ objectId: number, /* HubSpot object type of the phoneNumber */ objectType: CONTACT | COMPANY } = data; ... }
  • onEngagementCreated: message indicating that HubSpot has created onEngagementCreated data.
Please note: HubSpot is deprecating the onEngagementCreated event in favor of onCreateEngagementSucceeded in 2024.
/** @deprecated */ // Message indicating that HubSpot has created onEngagementCreated(data) { const { /* A HubSpot created engagement id. */ engagementId: number, } = data; ... }
  • onCreateEngagementSucceeded or onCreateEngagementFailed (NEW): HubSpot sends a message to notify the calling app partner that the engagement update succeeded or failed.
onCreateEngagementSucceeded: event => { /* HubSpot has created an engagement for this call. */ }, onCreateEngagementFailed: event => { /* HubSpot has failed to create an engagement for this call. */ }
  • onUpdateEngagementSucceeded or onUpdateEngagementFailed (NEW): HubSpot sends a message to notify the calling app partner that the engagement creation succeeded or failed.
onUpdateEngagementSucceeded: event => { /* HubSpot has updated an engagement for this call. */ }, onUpdateEngagementFailed: event => { /* HubSpot has failed to update an engagement for this call. */ }
  • onVisibilityChanged: message indicating if the user has minimized or hidden the calling app.
// Message indicating if user has minimized/hide the call widget onVisibilityChanged(data) { const { isMinimized, isHidden } = data; ... }
  • defaultEventHandler: default handler for events.
// Default handler for events. defaultEventHandler(event) { console.info("Event received. Do you need to handle it?", event); }

Test your app from a local environment

While you're in the process of building your application, you can manually set the iframe URL for your browser by setting a localStorage value. This allows you to set a localhost URL for local testing.

To set the value, open the developer tools for your browser, and run the following JavaScript command in the developer console:

localStorage.setItem( "LocalSettings:Calling:CallingExtensions", '{"name": "<your intended widget name>", "url": "<your dev url or prod url>"}' );

The name value will be the title that appears in the header of the calling app, and the url will be the URL used for the iframe. While this item is set, the name you set will appear as an option for the call provider when you click the call icon, and the calling app will use the iframe url you set.

Get your app ready for production

In order to launch the calling extensions iFrame for end users, HubSpot requires the following iFrame parameters.

{ name: string /* The name of your calling service to display to users. */, url: string /* The URL to your phone/calling UI, built with the Calling Extensions */, width: number /* The iFrame's width */, height: number /* The iFrame's height */, isReady: boolean /* Whether the widget is ready for users (default=true) */, supportsCustomObjects : true // indicate if calls can be placed from a custom object }

Using your API tool (e.g. Postman), send this payload to our settings API. Ensure you get the APP_ID of your calling app application and your app DEVELOPER_ACCOUNT_API_KEY.

# Example payload to add the call widget app settings curl --request POST \ --url 'https://api.hubapi.com/crm/v3/extensions/calling/APP_ID/settings?hapikey=DEVELOPER_ACCOUNT_API_KEY' \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data '{"name":"demo widget","url":"https://mywidget.com/widget","height":600,"width":400,"isReady":true}' # Note that this endpoint also support GET and DELETE

The isReady flag indicates whether the app is ready for production. This flag should be set to false during testing. 

Please note: this flag, or any other field can be overwritten through localStorage.
/** * Override the isReady flag for the call widget */ localStorage.setItem( "LocalSettings:Calling:CallingExtensions", '{"name": "<your intended widget name>", "isReady": true}' );

Publish your calling app to the HubSpot marketplace

The final step once your app is setup is to list your calling app in the HubSpot marketplace. You can find more details here . You can also choose not to list it in the marketplace if this application is for internal use only.

Calling SDK | Frequently Asked Questions

How is user authentication handled?

The calling app should handle authentication.

Is Calling Extensions hosted on a CDN?

No. The Calling Extensions is very small and should be bundled with the call app. If bundling the file is not possible, the npm package includes a compiled UMD bundle that can be included into HTML (../node_modules/@hubspot/calling-extensions-sdk/dist/main.js).

When should an engagement be created versus updated?

A user can initiate a call from inside the HubSpot UI and outside the HubSpot UI (e.g. mobile app, redirected number, etc.) If a call is initiated from within HubSpot UI, HubSpot will create a call engagement and send the engagement to the calling app. Once the call finishes, the call app can update this engagement with additional call details. If a call is initiated outside of HubSpot UI, the app should create the call engagement.

What scopes are required as a part of the integration?

Add contacts and timeline scopes are required. These scopes ensure your application has access to contacts and the ability to create and update call engagements in the CRM.

Can this functionality be added to an already existing application in the marketplace or do I create a new app?

If you already have an existing app that serves the calling use case then you can directly add this functionality to your existing app. All customers who already have your app installed will get access to this new functionality without having to install the app again.

Can I integrate my existing soft phone application in the SDK?

Yes, integrating your existing soft phone application should be very easy. Just follow the steps in the documentation above to have your application up and running.

Can users use multiple integrations at the same time?

Yes, users can use multiple third-party calling integrations at the same time. They can use the provider switcher presented after clicking on the call button to seamlessly switch between providers.

Can free users install app integrations?

Yes, all users can install the app.

If a user already has my app installed, does the integration automatically show up?

Yes, if a user already has installed your app, and you are updating the same app with the calling extensions, the integration will automatically show up. Currently, there is no way for the developer to enable the calling app only to a subset of customers.

Can any user install or uninstall an app?

No, only users who have necessary permissions can install and uninstall an app. Learn more about how to review a user's permissions

Can I place a call from a custom object?

Yes, calling integrations can place calls from custom objects as long as they only use the SDK to create the call. Each integration will need to verify that they only use the Calling SDK to create calls and to notify HubSpot in the outgoingCall event.

First, verify that the integration is using the Calling SDK to create engagements in the outgoingCall event:

outgoingCall({ createEngagement: true })

If createEngagement is true, learn how to update your app information here.

Here is the example for the entire outgoingCall event:

const callInfo = { phoneNumber: string, // optional unless call is initiated by the widget createEngagement: true // whether HubSpot should create an engagement for this call callStartTime: number // optional unless call is initiated by the widget }; extensions.outgoingCall(callInfo);

If you need further assistance, visit the HubSpot developer support forum.


Was this article helpful?
This form is used for documentation feedback only. Learn how to get help with HubSpot.