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.
Prerequisites
- 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:
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:
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 https://github.com/intercom/ruby-tickets-tutorial
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(request.body.read)
ticket_title = notification["data"]["item"]["ticket_attributes"]["_default_title_"]
ticket_description = notification["data"]["item"]["ticket_attributes"]["_default_description_"]
ticket_id = notification["data"]["item"]["id"]
client = Octokit::Client.new(access_token: GITHUB_ACCESS_TOKEN)
# 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 = "https://api.intercom.io/tickets/#{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'})
end
// Find the code at https://github.com/intercom/node-tickets-tutorial
require("dotenv").config();
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());
app.post("/webhooks/intercom", async (req, res) => {
// Acknowledge Intercom webhook to prevent retries
res.status(200).end();
// Create a GitHub issue from Intercom ticket payload:
var notification = req.body;
var ticket_title = notification.data.item.ticket_attributes._default_title_;
var ticket_description =
notification.data.item.ticket_attributes._default_description_;
var ticket_id = notification.data.item.id;
const octokit = new Octokit({ auth: process.env.GITHUB_ACCESS_TOKEN });
const github_issue = await octokit.rest.issues.create({
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 = github_issue.data.html_url;
// Update Intercom ticket with GitHub issue URL
const intercom_ticket_endpoint = `https://api.intercom.io/tickets/${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":
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:
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:
The ticket you created in Intercom should be updated with a link to the 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(request.body.read)
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 = "https://api.intercom.io/tickets/#{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
end
app.post("/webhooks/github", async (req, res) => {
res.status(200).end();
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(`https://api.intercom.io/tickets/${intercom_ticket_id}`, options)
.then((response) => response.json())
.then((response) => console.log(response))
.catch((err) => console.error(err));
return;
}
console.log(notification.action);
});
Now, if you close the issue in GitHub, the Intercom ticket will be closed as well:
Congrats! You are done.
Next steps
- Learn more about Tickets as a product.
- Explore other Ticket webhook topics.
- Explore Ticket API.
Updated 15 days ago