Canvas Kit

Introduction

Canvas Kit allows you to build apps that work from directly within the Intercom UI.

This means you can bring your workflows and/or product into Intercom, making it easy for teammates and customers to see information or take actions that aren't specifically to do with Intercom.

For example, you could create an app that lets your leads quickly and easily book a meeting through the Messenger, like this:

Locations & Types

Canvas Kit apps can be viewed and used in either the Messenger (by users, leads and visitors) or in the Inbox product (by Intercom teammates). We make this distinction as Messenger and Inbox capabilities have slightly different requests, configurations, functionalities, and appearances from each other.

Apps can be added in five different places:

  • On the Messenger Home Messenger
  • Within Conversations Messenger
  • Within Operator flows Messenger
  • Within Messages Messenger
  • Within the Conversation Details Inbox

You may want your app only to be added within certain places (or to only be usable by teammates in the Inbox). You can choose where these can be added to on the Canvas Kit page of your app within the Developer Hub.

Requests & Responses

The Canvas Kit works by sending you POST requests to the URLs you provide us with. You then take any actions with your service and respond to these POST requests with JSON objects, letting us know what UI to show.

There's several different types of requests that we will (or optionally may) send to you:

Initialize

The Initialize request is triggered when a teammate:

  • Adds your app into to a conversation reply Messenger
  • Adds your app into an Operator flow Messenger
  • Adds your app into a Message Messenger
  • Adds your app on to the Messenger Home Messenger
  • Adds your app to the conversation details Inbox
  • Switches conversations when your app is present in the conversation details Inbox

Where do I enter the URL which will handle the Initialize requests?

For Messenger capabilities, go to your app in the Developer Hub, navigate to Configure > Canvas Kit, and open up the For users, leads and visitors dropdown. Select where the app can be added within and scroll down to see the Initialize flow webhook URL input field.

For Inbox capabilities, go to your app in the Developer Hub, navigate to Configure > Canvas Kit, and open up the For teammates dropdown. Select where the app can be added within and scroll down to see the Initialize flow webhook URL input field.

Initialize Request

The request payload will have all the data needed for you to understand who is using your app, where they are using it, and how you should respond. There are different request payloads for Messenger capabilities and Inbox capabilities.

{
	workspace_id: "abcd123",
	admin: { / Object: See link in table for the full model / },
	conversation: { / Object: See link in table for the full model / },
	customer: { / Object: See link in table for the full model / }
}
{
	workspace_id: "abcd123",
	card_creation_options: { "key": "value" }, //Can be more than one pair
	context: { / Object: See link in table for the full model / }  
}
Attribute
Type
Capabilities
Description

workspace_id

String

Messenger Inbox

The workspace ID of the teammate. Attribute is app_id for V1.2 and below.

admin

Object

Inbox

The Intercom teammate (admin) viewing the conversation.

card_creation_options

Object

Messenger

Key-value pairs which were given as results in response to the Configure request.

context

Object

Messenger

The context of where the app is added, where the user last visited, and information on the Messenger settings.

conversation

Object

Inbox

The conversation your app is being shown for.

customer

Object

Inbox

The user which is currently being viewed by the teammate in the conversation details panel. We send an individual initialize request for each customer when it's a group conversation.

Initialize Response

We expect a canvas object in response to the request. This is where you'll specify the UI for the first screen of the app using components.

{
	canvas: { / A canvas object with content and components / }
}


Submit

The Submit request is triggered when:

  • A component with a submit action is interacted with Messenger Inbox
{ 
  action: {
    "type": "submit"
  }
}

Where do I enter the URL which will handle the Submit requests?

For Messenger capabilities, go to your app in the Developer Hub, navigate to Configure > Canvas Kit, and open up the For users, leads and visitors dropdown. Select where the app can be added within and scroll down to see the Submit flow webhook URL input field.

For Inbox capabilities, go to your app in the Developer Hub, navigate to Configure > Canvas Kit, and open up the For teammates dropdown. Select where the app can be added within and scroll down to see the Submit flow webhook URL input field.

Submit Request

The format of the request we send is similar to the Initialize request but contains the current_canvas, input_values and the component_id. This allows you to understand what component the request came from, see what the value of any input was, action anything in your codebase, and then respond knowing what canvas was previously shown beforehand.

{
	workspace_id: "abcd123",
	admin: { / Object: See link in table for the full model / },
	conversation: { / Object: See link in table for the full model / },
	current_canvas: { / Object: See link in table for the full model / },
	customer: { / Object: See link in table for the full model / },
	input_values: { / Object: See link in table for the full model / },
	component_id: "id-assigned-by-you"
}
{
	workspace_id: "abcd123",
	component_id: "<component_id>",
	context: { / Object: See link in table for the full model / },
	current_canvas: { / Object: See link in table for the full model / },
	input_values: { "<component_id>": "<Value entered in component>" }, //Can be more than one pair 
	user: { / Object: See link in table for the full model / }
}
Attribute
Type
Capabilities
Description

workspace_id

String

Messenger Inbox

The workspace ID of the teammate. Attribute is app_id for V1.2 and below.

admin

Object

Inbox

The Intercom teammate (admin) viewing the conversation.

component_id

String

Messenger Inbox

The id of the component clicked by the teammate to trigger the request.

context

Object

Messenger

The context of where the app is added, where the user last visited, and information on the Messenger settings.

conversation

Object

Inbox

The conversation where your app is being shown.

current_canvas

Object

Messenger Inbox

The current_canvas the teammate can see.

customer

Object

Inbox

The user which is currently being viewed by the teammate in the conversation details panel. We send an individual initialize request for each customer within a conversation.

input_values

Object

Messenger Inbox

A list of key/value pairs of data, inputted by the teammate on the current canvas.

user

Object

Messenger

The user who took the action.

Submit Response

We expect a canvas object in response to the request. This will replace the previous canvas that was visible until the app was interacted with.

You can optionally provide an event object with the attribute type given as completed to tell us if the app has completed its purpose. For example, an email collector app would be complete when the end-user submits their email address.

Apps in conversation details can also optionally insert an app into the conversation reply:

  1. You respond with a card_creation_options object.
  2. We send a request to the initialize URL for Messenger capabilities with the card_creation_options object present.
  3. You respond with a canvas object with the components you want to insert into the conversation reply.
{
	canvas: { / A canvas object with content and components / },
	card_creation_options: { "key": "value" }, // Optional. Can be more than one pair
	event: { "type": "completed" } // Optional. Recorded by Intercom.
}
{
	canvas: { / A canvas object with content and components / },
	event: { "type": "completed" } // Optional. Recorded by Intercom.
}


Configure (Optional)

Teammates can optionally configure an app before the initialize request is sent to insert the app. If you provide a URL to receive this request, it will trigger when a teammate:

  • Adds your app into to a conversation reply Messenger
  • Adds your app into an Operator flow Messenger
  • Adds your app into a Message Messenger
  • Adds your app on to the Messenger Home Messenger
  • Interacts with components which have an associated submit action Messenger

Where do I enter the URL which will handle the Configure requests?

Go to your app in the Developer Hub, navigate to Configure > Canvas Kit, and open up the For users, leads and visitors dropdown. Select where the app can be added within and scroll down to see the Configure flow webhook URL input field.

Configure Request

The first request we send will allow you to know the workspace where this is happening, the admin who will be configuring the app, and additional context such as where this will be added once complete.

For subsequent requests whereby an admin has interacted with a component with a submit action, the request payload will contain the same details with current_canvas, input_values and the component_id also present. This allows you to understand what component the request came from, see what the value of any input was, action anything in your codebase, and then respond knowing what canvas was previously shown beforehand.

{
  workspace_id: "abcd123",
  admin: { / Object: See link in table for the full model / },
  context: { / Object: See link in table for the full model / }
}
{
  workspace_id: "abcd123",
  admin: { / Object: See link in table for the full model / },
  component_id: "<component_id>",
  context: { / Object: See link in table for the full model / },
  current_canvas: { / Object: See link in table for the full model / },
  input_values: { "<component_id>": "<Value entered in component>" }, //Can be more than one pair 
}
Attribute
Type
Description

workspace_id

String

The workspace ID of the teammate. Attribute is app_id for V1.2 and below.

component_id

String

The id of the component clicked by the teammate to trigger the request.

context

Object

The context of where the app is added, where the user last visited, and information on the Messenger settings.

current_canvas

Object

The current_canvas the teammate can see.

input_values

Object

A list of key/value pairs of data, inputted by the teammate on the current canvas.

Configure Response

We either expect:

  • A canvas object which will replace the previous canvas that was visible until the teammate interacted with your app.
  • A results object which will end the configuration and trigger the initialize request to be sent. There will be a card_creation_options object in the payload showing your key-value pairs from the results object.
{
  canvas: { / A canvas object with content and components / }
}
{
  results: { "key": "value" } // Can be more than one pair
}


Live Canvas (Optional)

Canvases are static by default and require a new request to come through in order to update them. Live canvases however will make requests every time the card is viewed without any interaction needed, meaning the canvas can be kept up-to-date with no action from the user.

This works for every Messenger request that you can respond with a canvas object to. Instead of returning the content object within the canvas object, you should provide a content_url attribute instead with the value being the URL you want us to send a POST request to when someone views the app.

Live Canvas Request

We send a POST request to the given content_url every time someone views the app.

{
	workspace_id: "abcd123",
	canvas: { / Object: See link in table for the full model / },
	context: { / Object: See link in table for the full model / },
	user: { / Object: See link in table for the full model / }
}
Attribute
Type
Description

workspace_id

String

The workspace ID of the teammate. Attribute is app_id for V1.2 and below.

canvas

Object

The current_canvas the teammate can see.

context

Object

The context of where the app is added, where the user last visited, and information on the Messenger settings.

user

Object

The user who viewed the card.

Live Canvas Response

We expect a content object back in response that contains the components you want to show.

{ 
  content: { 
    components : [
      {
        "type": "text",
        "text": "This is a text component"
     	}
    ]
  }
}


Sheets (Optional)

Sheets open an iframe within the entire Messenger window so that you can embed a completely custom UI. It's perfect for when you need to present your own UI to your users in a larger area and achieve more complicated workflows.

You can use both canvases and sheets within your app by:

  • Opening a sheet when users interact with a component in a canvas.
  • Returning to a canvas (either the old one or a new one) when they close the sheet.

Here is an example app that opens a sheet with the user clicks the "View property" button:

There are two different types of requests we send for Sheets to work:

  • Opening the Sheet Messenger
  • Submitting the Sheet Messenger

Open Sheet Request

When a user interacts with a component which has a sheet action associated to it, a POST request will be made to the URL within the action.

{
	workspace_id: "abcd123",
	component_id: "<component_id>",
	context: { / Object: See link in table for the full model / },
	current_canvas: { / Object: See link in table for the full model / },
	input_values: { "<component_id>": "<Value entered in component>" }, //Can be more than one pair 
	user: { / Encrypted Object: See link in table for the full model / }
}
# The user object that we send as part of the payload will be encrypted using AES256-CGM and then Base64 encoded.

intercom_data[:user] = encrypt({ test: "test" }.to_json)
encrypted_user = intercom_data[:user]
decoded_user = Base64.decode64(encrypted.encode('utf-8'))

secret = "<OAUTH_CLIENT_SECRET>"
key = Digest::SHA256.digest(secret)
decipher = ::OpenSSL::Cipher.new('aes-256-gcm')

GCM_IV_LENGTH = 12
GCM_AUTH_TAG_LENGTH = 16

decipher.decrypt
decipher.key = key
decipher.iv = decoded_user[0, GCM_IV_LENGTH]
decipher.auth_tag = decoded_user[(decoded_user.length - GCM_AUTH_TAG_LENGTH), GCM_AUTH_TAG_LENGTH]
ciphertext = decoded_user[GCM_IV_LENGTH, (decoded_user.length - GCM_AUTH_TAG_LENGTH - GCM_IV_LENGTH)]
decipher.update(ciphertext) + decipher.final
=> "{\"test\":\"test\"}"
/* The user object that we send as part of the payload will be encrypted using AES256-CGM and then Base64 encoded. */

<?php 
$encodedUser = "<USER-FROM-REQUEST>";

$decodedUser = base64_decode($encodedUser);

$secret = "<THIS-IS-A-SECRET>";
$key = openssl_digest($secret, 'sha256', TRUE);

$ivlen = 12;
$taglen = 16;

$iv = substr($decodedUser, 0, $ivlen);
$tag = substr($decodedUser, strlen($decodedUser) - $taglen, $taglen);

$cipherLen = strlen($decodedUser) - $taglen - $ivlen;
$cipherText = substr($decodedUser, $ivlen, $cipherLen);

$decryptedUser = openssl_decrypt($cipherText, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $tag);
?>
/* The user object that we send as part of the payload will be encrypted using AES256-CGM and then Base64 encoded. */

String encodedUser = "<USER-FROM-REQUEST>";
byte[] decodedUser = Base64.getDecoder().decode(encodedUser.replaceAll("\n", "").getBytes());
String secret = "<THIS-IS-A-SECRET>";

MessageDigest sha256MessageDigest = MessageDigest.getInstance("SHA-256");
sha256MessageDigest.update(secret.getBytes());
byte[] keyBytes = sha256MessageDigest.digest();

int ivlen = 12;
byte[] iv = Arrays.copyOfRange(decodedUser, 0, ivlen);
byte[] cipherText = Arrays.copyOfRange(decodedUser, ivlen, decodedUser.length);

Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "SunJCE");
GCMParameterSpec spec = new GCMParameterSpec(128, iv);
SecretKey key = new SecretKeySpec(keyBytes, "AES");
cipher.init(Cipher.DECRYPT_MODE, key, spec);

byte[] res = cipher.doFinal(cipherText);
String decryptedUser = new String(res);
/* The user object that we send as part of the payload will be encrypted using AES256-CGM and then Base64 encoded. */

const masterkey = 'your-master-key';

// base64 decoding
const bData = Buffer.from(decoded, 'base64');

// convert data to buffers
const ivlen = 12;
const iv = bData.slice(0, ivlen);

const taglen = 16;
const tag = bData.slice(bData.length - taglen, bData.length);

const cipherLen = bData.length - taglen;
const cipherText = bData.slice(ivlen, cipherLen);

let hash = crypto.createHash('sha256').update(masterkey);
let key = Buffer.from(hash.digest("binary"), "binary");//buffer from binary string.

// AES 256 GCM Mode
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
decipher.setAuthTag(tag);

// Encrypt the given text
let decrypted = decipher.update(cipherText, 'binary', 'utf8');
decrypted += decipher.final('utf8');

console.log(decrypted);
# The user object that we send as part of the payload will be encrypted using AES256-CGM and then Base64 encoded.

import base64
import json

from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import (
    Cipher, algorithms, modes
)
from cryptography.hazmat.primitives.hashes import (
    Hash, SHA256
)

def decode_intercom_user(user_data: str) -> dict:
    """Decrpyt the user in the sheet POST request
      user_data is the encrypted user string"""

    # Decode Base64 encoded string
    data = base64.b64decode(user_data.encode('utf8'))
    client_secret = "Your client secret here".encode('utf8')

    # SHA256 digest of the client secret
    hasher = Hash(SHA256(), backend=default_backend())
    hasher.update(client_secret)
    key = hasher.finalize()

    iv_length = 12
    tag_length = 16

    # Extract IV and TAG
    iv = data[:iv_length]
    tag = data[len(data)-tag_length:]

    cipher_text = data[iv_length:len(data)-tag_length]

    # Decrypt with AES256-GCM
    cipher = Cipher(algorithms.AES(key), modes.GCM(iv, tag), backend=default_backend())

    decrypter = cipher.decryptor()
    text = decrypter.update(cipher_text)

    return json.loads(text)
Attribute
Type
Description

workspace_id

String

The workspace ID of the teammate. Attribute is app_id for V1.2 and below.

component_id

String

The id of the component clicked by the teammate to trigger the request.

context

Object

The context of where the app is added, where the user last visited, and information on the Messenger settings.

current_canvas

Object

The current_canvas the teammate can see.

input_values

Object

A list of key/value pairs of data, inputted by the teammate on the current canvas.

user

Object

The user who took the action. This is encrypted and should be decrypted to verify the request comes from a legitimate Intercom user.

Open Sheet Response

You send your HTML page that will be embedded within the Messenger as the response. This should contain a script which loads the Messenger Sheet Library script so you can use methods to see the title of the Sheet, close the Sheet, or submit the Sheet.

You will also need to ensure the Content Security Policy (CSP) header is sent by both the website being loaded in the Sheet and the website where the Sheet was opened from.

CSP Configuration Steps

If the website that hosts the Intercom Messenger sends the CSP header - intercom-sheets.com needs to be added to the frame-src directive of the CSP header.

If the website that hosts the Intercom Messenger does not send the CSP header - no action is needed.

For the website loaded inside the Sheet - as your Sheet is being loaded as an iframe inside the https://intercom-sheets.com domain with child-src: https security policy, you must follow these rules:

  • It must be served via HTTPS and the sheet URL must use https:// protocol (i.e. no redirects)*
  • It must not send the X-Frame-Options header.
  • If the page sends the CSP header, the frame-ancestors directive must not be included. This allows the sheet to be loaded within Messenger host pages in any domain.
  • If the page sends the CSP header, add https://js.intercomcdn.com/ to the script-src CSP directive. This allows the Messenger Sheet Library script to load inside the sheet.
<!DOCTYPE html>
<html>
<head>
  <title>Sheet 1</title>
  <!-- Adding the Messenger Sheet Library -->
  <script src="https://s3.amazonaws.com/intercom-sheets.com/messenger-sheet-library.latest.js"></script>
</head>
<body>
  <button id="done">Done</button>

  <!-- Example of using a Messenger Sheet Library method -->
  <script>
    const done = document.querySelector('#done');
    done.addEventListener('click', () => {
      INTERCOM_MESSENGER_SHEET_LIBRARY.submitSheet({ super: "secret" });
    });
  </script>
</body>
</html>
/* Sets the `title` of the sheet in the Messenger. The Messenger will call this method automatically when the script loads. This will default to `document.title` if not provided. */

INTERCOM_MESSENGER_SHEET_LIBRARY.setTitle(<Insert Title Here>)
/* Closes the sheet and triggers a POST request to be sent to the Submit Sheet URL so you are aware of the actions end users have taken in the sheet and can update the canvas where the sheet was opened from. See more about the request payload and expected responses in the Submit Sheet sections below. */

INTERCOM_MESSENGER_SHEET_LIBRARY.submitSheet({<Insert Key-Value Pairs Here>})
/* Closes the sheet immediately. The Messenger will not be aware of any actions the end user may have taken in the sheet and you will not be able to update the canvas where the sheet was opened from. */

INTERCOM_MESSENGER_SHEET_LIBRARY.closeSheet()

Submit Sheet Request

This allows you to both close the Sheet and collect information on the actions taken by the user within the Sheet. For the request to be sent, you must call the INTERCOM_MESSENGER_SHEET_LIBRARY.submitSheet({<Insert Key-Value Pairs Here>}) method and insert the key-value pairs of data that you want to collect.

We send you a POST request which will contain contextual data alongside an object containing the canvas which originally opened the sheet (current_canvas) and the key-value pairs you gave in the method (sheet_values).

Where do I enter the URL which will handle the Submit Sheet requests?

Go to your app in the Developer Hub, navigate to Configure > Canvas Kit, and open up the For users, leads and visitors dropdown. Select where the app can be added within and scroll down to see the Sheets flow webhook URL input field.

{
	workspace_id: "abcd123",
	context: { / Object: See link in table for the full model / },
	current_canvas: { / Object: See link in table for the full model / },
	sheet_values: { "key": "value" }, //Can be more than one pair 
	user: { / Object: See link in table for the full model / }
}
Attribute
Type
Description

workspace_id

String

The workspace ID of the teammate. Attribute is app_id for V1.2 and below.

context

Object

The context of where the app is added, where the user last visited, and information on the Messenger settings.

current_canvas

Object

The current_canvas the teammate can see.

sheet_values

Object

Key-value pairs which were given within the Submit Sheet method.

user

Object

The user who took the action.

Submit Sheet Response

We expect a canvas object in response to the request. This will replace the previous canvas that was visible until the sheet was opened.

You can optionally provide an event object with the attribute type given as completed to tell us if the app has completed its purpose. For example, an email collector app would be complete when the end-user submits their email address.

{
	canvas: { / A canvas object with content and components / },
	event: { "type": "completed" } // Optional. Recorded by Intercom.
}


Signing Notifications

Each Canvas Kit request is signed by Intercom via an X-Body-Signature header. We do this so that you can check that each request is actually sent by Intercom by decoding the signature.

The value is computed by creating a signature using the body of the JSON request and your app's OAuth client_secret value, which you can find on the Basic Info page of your app.

It is a hexadecimal (64-byte) value that is computed using the HMAC-SHA256 algorithm as defined in RFC2104.

-H X-Body-Signature: 21ff2e149e0fdcac6f947740f6177f6434bda921

What's Next

Our Canvas Kit Reference has information on the objects that we send in requests, those that you have to respond with, and the components you can return for the UI. We also have code examples that you can easily take as templates for the UI of your app.

Canvas Kit Reference

Canvas Kit


Suggested Edits are limited on API Reference Pages

You can only suggest edits to Markdown body content, but not to the API spec.