Build an integration with GitHub issues

Tickets are a great way to track work in Intercom, but frequently your engineering team will have their workflows in a tool like GitHub. In this tutorial we'll walk you through how you would sync information between Intercom tickets and GitHub issues. We'll cover registering a webhook, creating an Intercom ticket, writing a listener for your app and creating a corresponding GitHub issue. The Intercom ticket will also be updated with a link to the GitHub issue to make navigation between systems easier.


  • You'll need an Intercom developer workspace and an Intercom app. If you don't, follow this guide.
  • You'll need access to the Tickets feature. If you don’t, follow this guide.
  • You'll need a token with access to a GitHub repo where you'd like to create your new issues.

Step 1: Register a “ticket.created” webhook

To be notified when a new ticket is created in Intercom, you need to subscribe to “ticket.created” webhook. Follow this guide to subscribe to “ticket.created” topic:

Ticket created webhook

You'll also need to add an url pointing to an endpoint in your app that will handle webhook payload from Intercom.

When you're configuring the webhook, you can also get a preview of the payload by hovering over the corresponding row and clicking the Payload preview icon shown below:

Ticket created webhook payload

Step 2: Handle webhook payload

Your endpoint needs to handle a POST request that Intercom will send to it once a new ticket is created in Intercom.

Here's some sample code for handling the payload: it creates an issue in GitHub and then updates the Intercom ticket with a link to this issue.

# Find the code at
require 'sinatra'
require 'json'
require 'octokit'
require 'httparty'

REPO_NAME_WITH_OWNER = 'owner/repo'
GITHUB_ACCESS_TOKEN = 'your_github_api_access_token'
INTERCOM_ACCESS_TOKEN = 'your_intercom_api_access_token'

post '/tickets' do
  # Create a GitHub issue from Intercom ticket payload:
  notification = JSON.parse(
  ticket_title = notification["data"]["item"]["ticket_attributes"]["_default_title_"]
  ticket_description = notification["data"]["item"]["ticket_attributes"]["_default_description_"]
  ticket_id = notification["data"]["item"]["id"]


  # Note that we store Intercom ticket id within the issue title, we'll need it in Step 5 of this tutorial:
  github_issue = client.create_issue(REPO_NAME_WITH_OWNER, ticket_title + " [Intercom ticket number: " + ticket_id + "]", ticket_description)

  # Update the Intercom ticket with the GitHub issue link:
  github_issue_url = github_issue.html_url

  intercom_ticket_endpoint = "{ticket_id}"
  token_string= "Token token = #{INTERCOM_ACCESS_TOKEN}"
  data = { ticket_attributes: { github_issue_url: "#{github_issue_url}" } }
  response = HTTParty.put(intercom_ticket_endpoint, body: data.to_json, headers: { 'Content-Type': 'application/json', 'Authorization': token_string, 'Intercom-Version': '2.9'})
// Find the code at
const express = require("express");
const bodyParser = require("body-parser");
const { Octokit } = require("octokit");

// Initialize express and define a port
const app = express();
const PORT = 3000;

// Tell express to use body-parser's JSON parsing
app.use(bodyParser.json());"/webhooks/intercom", async (req, res) => {
// Acknowledge Intercom webhook to prevent retries

  // Create a GitHub issue from Intercom ticket payload:
  var notification = req.body;
  var ticket_title =;
  var ticket_description =;
  var ticket_id =;

  const octokit = new Octokit({ auth: process.env.GITHUB_ACCESS_TOKEN });

  const github_issue = await{
    owner: "your_github_org", // TODO replace with your GitHub org e.g. intercom
    repo: "your_github_repo", // TODO replace with your GitHub repo e.g. intercom-github-integration
    // Note that we store Intercom ticket id within the issue title, we'll need it in Step 5 of this tutorial: In real world scenarios you would probably store this elsewhere.
    title: `${ticket_title} [Intercom ticket number: ${ticket_id}]`,
    body: ticket_description,

  const github_issue_url =;

  // Update Intercom ticket with GitHub issue URL
  const intercom_ticket_endpoint = `${ticket_id}`;
  const intercom_payload = { ticket_attributes: { github_issue_url } };

  const options = {
    method: "PUT",
    body: JSON.stringify(intercom_payload),
    headers: {
      accept: "application/json",
      "content-type": "application/json",
      "Intercom-Version": "2.9",
      authorization: `Bearer ${process.env.INTERCOM_ACCESS_TOKEN}`,

  fetch(intercom_ticket_endpoint, options)
    .then((response) => response.json())
    .then((response) => console.log(response))
    .catch((err) => console.error(err));


Step 3: Create a ticket in Intercom Inbox

Follow the steps in this guide to create your first ticket of ticket type "bug":

New ticket

Note that when creating the ticket type, you should also set "github_issue_url" as an attribute shared between tickets of this type. This is necessary to update the ticket once the corresponding GitHub issue gets created:

GitHub issue attribute

Step 4: An issue gets created in GitHub

At this point, the data about your Intercom ticket should be synced to a corresponding GitHub issue:

New issue

The ticket you created in Intercom should be updated with a link to the GitHub issue:

Link to GitHub issue

Step 5: Close the issue in GitHub

When it's time to close this GitHub issue, you'll probably want to mark the corresponding Intercom ticket as "resolved".

To achieve this, you first need to subscribe your GitHub repo to a webhook that will fire when an issue is closed. Refer to GitHub documentation for how to do this.

You'll also need to add another endpoint to your app that will handle the incoming webhook from GitHub:

post '/issues' do
  notification = JSON.parse(

  if notification["action"] == "closed"
    # Parse the ticket id from the issue title:
    ticket_id = notification["issue"]["title"][/Intercom ticket number:\s*(\d+)\]/, 1]

    intercom_ticket_endpoint = "{ticket_id}"
    token_string= "Token token=#{INTERCOM_ACCESS_TOKEN}"
    data = { state: "resolved" }

    # Update Intercom ticket state to mark it as "resolved":
    response = HTTParty.put(intercom_ticket_endpoint, body: data.to_json, headers: { 'Content-Type': 'application/json', 'Authorization': token_string, 'Intercom-Version': '2.9'})
end"/webhooks/github", async (req, res) => {
  const notification = req.body;
  if (notification.action === "closed") {
    console.log("github issue closed");
    const gh_issue_title = notification.issue.title;
    const ticket_id_regex = /Intercom ticket number:\s*(\d+)\]/;
    const intercom_ticket_id = gh_issue_title.match(ticket_id_regex)[1];

    const options = {
      method: "PUT",
      body: JSON.stringify({
        state: "resolved",
      headers: {
        accept: "application/json",
        "content-type": "application/json",
        "Intercom-Version": "2.9",
        authorization: `Bearer ${process.env.INTERCOM_ACCESS_TOKEN}`,

    fetch(`${intercom_ticket_id}`, options)
      .then((response) => response.json())
      .then((response) => console.log(response))
      .catch((err) => console.error(err));



Now, if you close the issue in GitHub, the Intercom ticket will be closed as well:

Ticket closed

Congrats! You are done.

Next steps