Webhooks
Using webhooks
So far in this series we've added user data, custom and standard attributes, leads and companies, and then created events and segments to better understand our users' behavior and actions.
By doing this you may have identified that there are certain user actions you rate as particularly important and want to be notified of when they occur. One way you can do this is via webhooks, which allow you to receive notifications when users take specific actions.
You can read more about webhooks in our docs where we describe some common use cases and go over how you can interact with webhooks via the web app console.
Setting up your endpoint
Webhooks work by POSTing data to an endpoint which you specify when you setup the endpoint. So before we create a webhook we will need to create an endpoint which is capable of receiving a POST request. The easiest way to do this for testing purposes is via ngrok with a simple Sinatra route.
Ngrok setup
To setup ngrok simply download the free package from their website for your OS. Ngrok allows you to do you development on localhost but then make it available publicly via secure tunnels. You can find the simple setup steps on the ngrok website.
Once you have these setup you should be able to run the following command to make the tunnel available:
> ./ngrok http 4567
Tunnel Status online
Version 2.0.25/2.0.25
Region United States (us)
Web Interface http://127.0.0.1:4040
Forwarding http://71b0744d.ngrok.io -> localhost:4567
Forwarding https://71b0744d.ngrok.io -> localhost:4567
Sinatra setup
For now all we need is a simple endpoint which listens for a POST request. Sinatra is a lightweight framework which we can use to setup a quick and simple POST endpoint. You should be able to test this route with a simple curl POST as shown below. Ensure that you are using your ngrok tunnel as outlined in the output you see when running ngrok.
require 'sinatra'
require 'json'
#You may not need to bind the server to 0.0.0.0
#It automatically binds to 127.0.0.0
set :bind, '0.0.0.0'
post '/webhook' do
push = JSON.parse(request.body.read)
puts "Webhook JSON Data: #{push.inspect}"
end
> ruby webhooks_server.rb
> curl --data @test.json http://69b97777.ngrok.io/webhook
Webhook JSON Data: {"title"=>"TEST", "body"=>"This is a test post."}
89.101.228.226 - - [09/May/2016:15:07:40 +0000] "POST /webhook HTTP/1.1" 200 - 0.0051
{
"title": "TEST",
"body": "This is a test post."
}
Creating a webhook
Now that we have out server setup, we can create a webhook and see if we receive a notification when the relevant action occurs. When we create a webhook we subscribe to topics, which are the actions or events we want to be notified of when they occur.
Here is the list of topics which you can subscribe to (you can subscribe to multiple topics per webhook). You can also find the list on our API reference guide here. For the purposes of this tutorial we will look at user and event topics.
Topic | Item Type | Description |
---|---|---|
conversation | Conversation | Subscribe to all conversation notifications |
conversation.user.created | Conversation | Subscribe to user initiated messages |
conversation.user.replied | Conversation | Subscribe to user conversation replies |
conversation.admin.replied | Conversation | Subscribe to admin conversation replies |
conversation.admin.assigned | Conversation | Subscribe to admin conversation assignments |
conversation.admin.closed | Conversation | Subscribe to admin conversation closes |
conversation.admin.noted | Conversation | Subscribe to admin conversation notes |
conversation.admin.opened | Conversation | Subscribe to admin conversation opens |
user.created | User | Subscribe to user creations |
user.deleted | User | Subscribe to user deletions. Not sent for bulk deletions. |
user.unsubscribed | User | Subscribe to user unsubscriptions from email |
user.email.updated | User | Subscribe to user's email address being updated |
user.tag.created | User | Subscribe to users being tagged. Not sent for bulk deletions. |
user.tag.deleted | User | Subscribe to users being untagged |
contact.created | Lead | Subscribe to Lead creations |
contact.signed_up | Lead | Subscribe to Leads converting to a User |
contact.added_email | Lead | Subscribe to Leads adding email |
company.created | Company | Subscribe to company creations |
event.created | Event | Subscribe to events (Beta!) |
ping | Ping | Sent when a post to the Subscription's ping resource is received, or periodically by Intercom. Ping is always subscribed to. |
Create a file called webhook_tasks.rb and add the following code to it:
require './intercom_client'
class WebhookTasks < IntercomClient
def initialize
#Leave this empty as simple over-ride
end
def create_webhook(url, topics)
#Create a hash to pass through the Webhook data
webhook = {:url => url,
:topics => topics}
@@intercom.subscriptions.create(webhook)
end
end
> require './webhook_tasks'
=> true
> intercom = IntercomClient.new('<APP ID>','<API KEY>')
=> #<IntercomClient:0x00000001e240e0>
[6] pry(main)> web = WebhookTasks.new
=> #<WebhookTasks:0x00000001d75450>
[7] pry(main)> web.create_webhook('http://14856885.ngrok.io/webhook', ["users.created"])
=> #<Intercom::Subscription:0x00000001cc5f50
You should also see that you receive an immediate "ping" from the webhook you just setup to test if the URL is available for POST requests. You should see the Sinatra output indicating the a POST was received with some JSON data and an ngrok update to note that a POST was received successfully (via 200 HTTP status code). You can also check via the console to ensure that you can see that webhook successfully created:
Webhook JSON Data: {"type"=>"notification_event", "app_id"=>"<APP ID>", "data"=>{"type"=>"notification_event_data", "item"=>{"type"=>"ping", "message"=>"something something interzen"}}, "links"=>{}, "id"=>nil, "topic"=>"ping", "delivery_status"=>nil, "delivery_attempts"=>1, "delivered_at"=>0, "first_sent_at"=>1462880405, "created_at"=>1462880405, "self"=>nil}
54.87.242.237 - - [10/May/2016:11:40:05 +0000] "POST /webhook HTTP/1.1" 200 - 0.0009
Tunnel Status online
Version 2.0.25/2.1.1
Region United States (us)
Web Interface http://127.0.0.1:4040
Forwarding http://14856885.ngrok.io -> localhost:4567
Forwarding https://14856885.ngrok.io -> localhost:4567
Connections ttl opn rt1 rt5 p50 p90
5 0 0.00 0.00 0.28 39.97
HTTP Requests
-------------
POST /webhook 200 OK
You can check the developer hub dashboard to see that the webhook is setup correctly. You will see a list of your current webhooks:
Listing your webhooks
Now that you have created a webhook you'll want to be able to list your webhooks to see what is currently setup.
To make it easy to list your subscriptions, let's create a method which allows you to specify the attribute of the subscription model that you want to see. The method should then iterate over the subscriptions model (which can be seen here) and output that attribute from each subscription. Alternatively, if you do not specify any attribute, let's output a short list of the most important attributes such as subscriptions ID, callback URL, webhook status and the topics subscribed to:
def list_subscriptions(attrib=false)
#Check if there is an attribute specified
if attrib
#If there is then assume just this attribute is needed
@@intercom.subscriptions.all.each {|sub| puts "#{sub.send(attrib.to_sym)}"};
else
#Print out default of most useful attributes
@@intercom.subscriptions.all.each {|sub| puts "#{sub.id}, #{sub.url}, #{sub.active}, #{sub.topics}"};
end
end
require './intercom_client'
class WebhookTasks < IntercomClient
def initialize
#Leave this empty as simple over-ride
end
def create_webhook(url, topics)
#Create a hash to pass through the Webhook data
webhook = {:url => url,
:topics => topics}
@@intercom.subscriptions.create(webhook)
end
def list_subscriptions(attrib=false)
#Check if there is an attribute specified
if attrib
#If there is then assume just this attribute is needed
@@intercom.subscriptions.all.each {|sub| puts "#{sub.send(attrib.to_sym)}"};
else
#Print out default of most useful attributes
@@intercom.subscriptions.all.each {|sub| puts "#{sub.id}, #{sub.url}, #{sub.active}, #{sub.topics}"};
end
end
end
You can then list individual or default attributes as follows:
> require './webhook_tasks';
> intercom = IntercomClient.new('<ACCESS TOKEN>');
> web = WebhookTasks.new;
> web.list_subscriptions();
nsub_01033700-16ad-11e6-aa08-ed65c3040c0f, http://14856885.ngrok.io/webhook, true, ["user.created"]
> web.list_subscriptions("service_type");
web
If you want to access a single subscription object we can create a simple method to get this:
def get_subscription(id)
#The ID is the only unique element of the webhook
@@intercom.subscriptions.find(:id => id)
end
require './intercom_client'
class WebhookTasks < IntercomClient
def initialize
#Leave this empty as simple over-ride
end
def create_webhook(url, topics)
#Create a hash to pass through the Webhook data
webhook = {:url => url,
:topics => topics}
@@intercom.subscriptions.create(webhook)
end
def list_subscriptions(attrib=false)
#Check if there is an attribute specified
if attrib
#If there is then assume just this attribute is needed
@@intercom.subscriptions.all.each {|sub| puts "#{sub.send(attrib.to_sym)}"};
else
#Print out default of most useful attributes
@@intercom.subscriptions.all.each {|sub| puts "#{sub.id}, #{sub.url}, #{sub.active}, #{sub.topics}"};
end
end
def get_subscription(id)
#The ID is the only unique element of the webhook
@@intercom.subscriptions.find(:id => id)
end
end
We can use the list subscriptions method we just created to see the IDs and then get them individually:
> web.list_subscriptions();
nsub_01033700-16ad-11e6-aa08-ed65c3040c0f, http://14856885.ngrok.io/webhook, true, ["user.created"]
> sub = web.get_subscription("nsub_01033700-16ad-11e6-aa08-ed65c3040c0f");
> sub.topics
=> ["user.created"]
Test your webhook
Now that we have created a new webhook, we should test it. We subscribed to be notified whenever a new user is created, so let's create a new user and see if we get notified when this occurs.
Remember to watch the terminal to see that ngrok identifies a 200 HTTP Status code and that you see some output from your Sinatra server indicating you have received some JSON data about the action. Create a new user using some of the code we created in our earlier tutorials:
> require './user_tasks';
> intercom = IntercomClient.new('<ACCESS TOKEN>');
> usr = UserTasks.new;
> usr.create_user(:email => "[email protected]", :user_id => 20)
You should see a notification to your Sinatra server which looks something like this:
52.90.74.244 - - [10/May/2016:12:44:58 +0000] "POST /webhook HTTP/1.1" 200 - 0.0008
Webhook JSON Data: {"type"=>"notification_event", "app_id"=>"<APP ID>", "data"=>{"type"=>"notification_event_data", "item"=>{"type"=>"user", "id"=>"5731e7a3ec06c24bee0000f5", "user_id"=>"20", "anonymous"=>false, "email"=>"[email protected]", "name"=>nil, "pseudonym"=>nil, "avatar"=>{"type"=>"avatar", "image_url"=>nil}, "app_id"=>"a86dr8yl", "companies"=>{"type"=>"company.list", "companies"=>[]}, "location_data"=>{}, "last_request_at"=>nil, "last_seen_ip"=>nil, "created_at"=>"2016-05-10T13:52:35.548Z", "remote_created_at"=>nil, "signed_up_at"=>nil, "updated_at"=>"2016-05-10T13:52:35.548Z", "session_count"=>0, "social_profiles"=>{"type"=>"social_profile.list", "social_profiles"=>[]}, "unsubscribed_from_emails"=>false, "user_agent_data"=>nil, "tags"=>{"type"=>"tag.list", "tags"=>[]}, "segments"=>{"type"=>"segment.list", "segments"=>[]}, "custom_attributes"=>{}}}, "links"=>{}, "id"=>"notif_73538090-16b6-11e6-aa08-ed65c3040c0f", "topic"=>"user.created", "delivery_status"=>nil, "delivery_attempts"=>1, "delivered_at"=>0, "first_sent_at"=>1462888355, "created_at"=>1462888355, "self"=>nil}
Tunnel Status online
Version 2.0.25/2.1.1
Region United States (us)
Web Interface http://127.0.0.1:4040
Forwarding http://14856885.ngrok.io -> localhost:4567
Forwarding https://14856885.ngrok.io -> localhost:4567
Connections ttl opn rt1 rt5 p50 p90
8 0 0.00 0.00 0.16 39.97
HTTP Requests
-------------
POST /webhook 200 OK
POST /webhook 200 OK
Create another subscription
Let's create another subscription to notify us when users are deleted. This way we can delete the user we just created and receive a notification for this.
> require './webhook_tasks';
> intercom = IntercomClient.new('a86dr8yl','a42be65ea1cba4f4d6d6dfbe8ed5e2aa1f1ae32a');
> web = WebhookTasks.new;
> web.create_webhook('http://14856885.ngrok.io/webhook', ["user.deleted"])
> web.list_subscriptions
nsub_8208ed10-16ba-11e6-bbf4-310abb4a0524, http://14856885.ngrok.io/webhook, true, ["user.deleted"]
nsub_01033700-16ad-11e6-aa08-ed65c3040c0f, http://14856885.ngrok.io/webhook, true, ["user.created"]
You should see a Ping to note that the webhook was successfully created:
Webhook JSON Data: {"type"=>"notification_event", "app_id"=>"<APP ID>", "data"=>{"type"=>"notification_event_data", "item"=>{"type"=>"ping", "message"=>"something something interzen"}}, "links"=>{}, "id"=>nil, "topic"=>"ping", "delivery_status"=>nil, "delivery_attempts"=>1, "delivered_at"=>0, "first_sent_at"=>1462890098, "created_at"=>1462890098, "self"=>nil}
54.87.242.237 - - [10/May/2016:14:21:38 +0000] "POST /webhook HTTP/1.1" 200 - 0.0007
Now we should be able to delete our new user and receive a webhook notification for this:
> require './user_tasks';
> usr = UserTasks.new;
> usr.delete_user("[email protected]");
Webhook JSON Data: {"type"=>"notification_event", "app_id"=>"<APP ID>", "data"=>{"type"=>"notification_event_data", "item"=>{"type"=>"user", "id"=>"5731e7a3ec06c24bee0000f5", "user_id"=>"20", "email"=>"[email protected]"}}, "links"=>{}, "id"=>"notif_300fb510-16bb-11e6-bbf4-310abb4a0524", "topic"=>"user.deleted", "delivery_status"=>nil, "delivery_attempts"=>1, "delivered_at"=>0, "first_sent_at"=>1462890390, "created_at"=>1462890390, "self"=>nil}
54.87.242.237 - - [10/May/2016:14:26:30 +0000] "POST /webhook HTTP/1.1" 200 - 0.0006
Deleting a subscription
If you want to delete a subscription, you can do so via the web app console but for now we will delete our subscriptions using the API. We can create a simple wrapper for the API to delete our subscriptions:
def delete_subscriptions(id)
#Delete single subscription given Subscription ID
#We dont have any query parameters so 2nd function param is empty string
@@intercom.delete("/subscriptions/#{id}", "")
end
require './intercom_client'
class WebhookTasks < IntercomClient
def initialize
#Leave this empty as simple over-ride
end
def create_webhook(url, topics)
#Create a hash to pass through the Webhook data
webhook = {:url => url,
:topics => topics}
@@intercom.subscriptions.create(webhook)
end
def list_subscriptions(attrib=false)
#Check if there is an attribute specified
if attrib
#If there is then assume just this attribute is needed
@@intercom.subscriptions.all.each {|sub| puts "#{sub.send(attrib.to_sym)}"};
else
#Print out default of most useful attributes
@@intercom.subscriptions.all.each {|sub| puts "#{sub.id}, #{sub.url}, #{sub.active}, #{sub.topics}"};
end
end
def get_subscription(id)
#The ID is the only unique element of the webhook
@@intercom.subscriptions.find(:id => id)
end
def delete_subscriptions(id)
#Delete single subscription given Subscription ID
#We dont have any query parameters so 2nd function param is empty string
@@intercom.delete("/subscriptions/#{id}", "")
end
end
Then you should be able to simply delete a subscription via the following steps:
> require './webhook_tasks';
> intercom = IntercomClient.new('<ACCESS TOKEN>');
> web = WebhookTasks.new;
> web.list_subscriptions;
nsub_077a7a00-16ce-11e6-bbf4-310abb4a0524, http://14856885.ngrok.io/webhook, true, ["user.created"]
nsub_113181b0-16ce-11e6-bbf4-310abb4a0524, http://14856885.ngrok.io/webhook, true, ["user.deleted"]
> web.delete_subscriptions("nsub_113181b0-16ce-11e6-bbf4-310abb4a0524")
> web.list_subscriptions;
nsub_077a7a00-16ce-11e6-bbf4-310abb4a0524, http://14856885.ngrok.io/webhook, true, ["user.created"]
> web.delete_subscriptions("nsub_077a7a00-16ce-11e6-bbf4-310abb4a0524")
> web.list_subscriptions;
>
Event webhooks
As well as default topics, you can also subscribe for events which you have created. This allows you to track particular user behavior you may be interested in. There is an "event.created" topic that you can subscribe to in order to identify that there are custom events you want to be notified about. Then you need to pass in the event(s) you want to be notified about.
The easiest thing to do here would be to add the functionality to the method we already have for creating webhooks. We will need to provide some extra information when creating custom events so we can make this an optional parameter. This way we know if it is a standard topic or an event we are subscribing to.
def create_webhook(url, topics, events=false)
if events
event_data = {
service_type: "web",
topics: ["event.created"],
url: url,
metadata: {
event_names: events
}
}
#Need to POST event metadata for event.created topic
@@intercom.post("/subscriptions/", event_data)
else
#Create a hash to pass through the Webhook data
webhook = {:url => url,
:topics => topics}
@@intercom.subscriptions.create(webhook)
end
end
require './intercom_client'
class WebhookTasks < IntercomClient
def initialize
#Leave this empty as simple over-ride
end
def create_webhook(url, topics, events=false)
if events
event_data = {
service_type: "web",
topics: ["event.created"],
url: url,
metadata: {
event_names: events
}
}
#Need to POST event metadata for event.created topic
@@intercom.post("/subscriptions/", event_data)
else
#Create a hash to pass through the Webhook data
webhook = {:url => url,
:topics => topics}
@@intercom.subscriptions.create(webhook)
end
end
def list_subscriptions(attrib=false)
#Check if there is an attribute specified
if attrib
#If there is then assume just this attribute is needed
@@intercom.subscriptions.all.each {|sub| puts "#{sub.send(attrib.to_sym)}"};
else
#Print out default of most useful attributes
@@intercom.subscriptions.all.each {|sub| puts "#{sub.id}, #{sub.url}, #{sub.active}, #{sub.topics}"};
end
end
def get_subscription(id)
#The ID is the only unique element of the webhook
@@intercom.subscriptions.find(:id => id)
end
def delete_subscriptions(id)
#Delete single subscription given Subscription ID
#We dont have any query parameters so 2nd function param is empty string
@@intercom.delete("/subscriptions/#{id}", "")
end
end
Before we subscribe to an event let's create some events using the code from our previous platform tutorial. If you already have some events created from these tutorials or from some other source you can skip this step and just use those events.
> require './event_tasks'
> event = EventTasks.new()
> intercom = IntercomClient.new('<ACCESS TOKEN>');
> event.submit_event("wrote-fear-and-trembling", Time.now.to_i, "[email protected]")
> event.submit_event("wrote-either-or", Time.now.to_i, "[email protected]")
You should then be able to see your events in the Apps-Settings->Events section of the menu:
One point to note here is that the "EVENT NAME" may not match the event you created. For the UI the event name is slightly changed. For example, we created an event with the name "wrote-fear-and-trembling", but the "EVENT NAME" in the UI is "Wrote fear and trembling". This is important if you are referring to the events in actions such as webhooks. You will need to refer to the original event name and not the UI event name. You can always check the event names in the user's profile page:
Subscribe to events
Now that we have some events we can use our new code to create a webhook for them:
> require './webhook_tasks';
> intercom = IntercomClient.new('ACCESS TOKEN');
> web = WebhookTasks.new;
> web.list_subscriptions;
>web.create_webhook('http://123.ngrok.io', ["event.created"], events=["wrote-fear-and-trembling", "invited-philosopher"])
> web.list_subscriptions;
> nsub_97fa6750-184c-11e6-b3f3-4b0d158bae78, http://123.ngrok.io, true, ["event.created"]
> subs = web.get_subscription('nsub_97fa6750-184c-11e6-b3f3-4b0d158bae78');
)> subs.topics
=> ["event.created"]
> subs.metadata
=> {"event_names"=>["wrote-fear-and-trembling", "invited-philosopher"]}
> web.create_webhook('http://1234.ngrok.io', ["user.created"])
web.list_subscriptions;
nsub_61a1d3d0-184e-11e6-b3f3-4b0d158bae78, http://1234.ngrok.io, true, ["user.created"]
nsub_97fa6750-184c-11e6-b3f3-4b0d158bae78, http://123.ngrok.io, true, ["event.created"]
> subs.topics
=> ["user.created"]
> subs.metadata
=> {}
Updating webhooks
While you are working with webhooks you may find that there are times when you need to update a particular webhook, since the callback has changed or you need to subscribe to more topics. You can do this with a POST request to the ID of the subscription of the webhook you want to update.
You add the data you want to update to the body of the POST. We can create a new method to do this:
def update_webhook(id, url, new_topics)
#Create the data you are going to POST
updates = {
topics: new_topics,
url: url,
}
@@intercom.post("/subscriptions/#{id}", updates)
end
require './intercom_client'
class WebhookTasks < IntercomClient
def initialize
#Leave this empty as simple over-ride
end
def create_webhook(url, topics, events=false)
if events
event_data = {
service_type: "web",
topics: ["event.created"],
url: url,
metadata: {
event_names: events
}
}
#Need to POST event metadata for event.created topic
@@intercom.post("/subscriptions/", event_data)
else
#Create a hash to pass through the Webhook data
webhook = {:url => url,
:topics => topics}
@@intercom.subscriptions.create(webhook)
end
end
def update_webhook(id, url, new_topics)
#Create the data you are going to POST
updates = {
topics: new_topics,
url: url,
}
@@intercom.post("/subscriptions/#{id}", updates)
end
def list_subscriptions(attrib=false)
#Check if there is an attribute specified
if attrib
#If there is then assume just this attribute is needed
@@intercom.subscriptions.all.each {|sub| puts "#{sub.send(attrib.to_sym)}"};
else
#Print out default of most useful attributes
@@intercom.subscriptions.all.each {|sub| puts "#{sub.id}, #{sub.url}, #{sub.active}, #{sub.topics}"};
end
end
def get_subscription(id)
#The ID is the only unique element of the webhook
@@intercom.subscriptions.find(:id => id)
end
def delete_subscriptions(id)
#Delete single subscription given Subscription ID
#We dont have any query parameters so 2nd function param is empty string
@@intercom.delete("/subscriptions/#{id}", "")
end
end
Then we can update the webhooks we just created as follows to add the "user.delete" topic:
web.update_webhook('nsub_61a1d3d0-184e-11e6-b3f3-4b0d158bae78', 'http://1234.ngrok.io', ['user.created', 'user.deleted'])
web.list_subscriptions;
nsub_61a1d3d0-184e-11e6-b3f3-4b0d158bae78, http://1234.ngrok.io, true, ["user.created", "user.deleted"]
nsub_97fa6750-184c-11e6-b3f3-4b0d158bae78, http://123.ngrok.io, true, ["event.created"]
Updating event webhooks
There is an extra metadata parameter for event webhooks so we will need to change the code a little to allow for this. Add the following code to your update method and then you should be able to update your event webhooks:
def update_webhook(id, url, new_topics, events=false)
#Create the data you are going to POST
updates = {
topics: new_topics,
url: url,
}
if events
updates[:metadata] = {event_names: events}
end
@@intercom.post("/subscriptions/#{id}", updates)
end
require './intercom_client'
class WebhookTasks < IntercomClient
def initialize
#Leave this empty as simple over-ride
end
def create_webhook(url, topics, events=false)
if events
event_data = {
service_type: "web",
topics: ["event.created"],
url: url,
metadata: {
event_names: events
}
}
#Need to POST event metadata for event.created topic
@@intercom.post("/subscriptions/", event_data)
else
#Create a hash to pass through the Webhook data
webhook = {:url => url,
:topics => topics}
@@intercom.subscriptions.create(webhook)
end
end
def update_webhook(id, url, new_topics, events=false)
#Create the data you are going to POST
updates = {
topics: new_topics,
url: url,
}
if events
updates[:metadata] = {event_names: events}
end
@@intercom.post("/subscriptions/#{id}", updates)
end
def list_subscriptions(attrib=false)
#Check if there is an attribute specified
if attrib
#If there is then assume just this attribute is needed
@@intercom.subscriptions.all.each {|sub| puts "#{sub.send(attrib.to_sym)}"};
else
#Print out default of most useful attributes
@@intercom.subscriptions.all.each {|sub| puts "#{sub.id}, #{sub.url}, #{sub.active}, #{sub.topics}"};
end
end
def get_subscription(id)
#The ID is the only unique element of the webhook
@@intercom.subscriptions.find(:id => id)
end
def delete_subscriptions(id)
#Delete single subscription given Subscription ID
#We dont have any query parameters so 2nd function param is empty string
@@intercom.delete("/subscriptions/#{id}", "")
end
end
You can then update your events by simply adding an events parameter to the update method:
> sub = web.get_subscription('nsub_97fa6750-184c-11e6-b3f3-4b0d158bae78')
> sub.metadata
=> {"event_names"=>["wrote-fear-and-trembling", "invited-philosopher"]}
> web.update_webhook('nsub_97fa6750-184c-11e6-b3f3-4b0d158bae78', 'http://123.ngrok.io', ['event.created'], events=["invited-philosopher"])
> sub = = web.get_subscription('nsub_97fa6750-184c-11e6-b3f3-4b0d158bae78')
> sub.metadata
=> {"event_names"=>["invited-philosopher"]}
Ping test
There is also an option to allow you to send a ping request to the webhook to test that it is working. You can do this with a simple post as shown below:
def ping(id)
#ping subscription to test webhook
@@intercom.post("/subscriptions/#{id}/ping", "")
end
require './intercom_client'
class WebhookTasks < IntercomClient
def initialize
#Leave this empty as simple over-ride
end
def create_webhook(url, topics, events=false)
if events
event_data = {
service_type: "web",
topics: ["event.created"],
url: url,
metadata: {
event_names: events
}
}
#Need to POST event metadata for event.created topic
@@intercom.post("/subscriptions/", event_data)
else
#Create a hash to pass through the Webhook data
webhook = {:url => url,
:topics => topics}
@@intercom.subscriptions.create(webhook)
end
end
def update_webhook(id, url, new_topics, events=false)
#Create the data you are going to POST
updates = {
topics: new_topics,
url: url,
}
if events
updates[:metadata] = {event_names: events}
end
@@intercom.post("/subscriptions/#{id}", updates)
end
def list_subscriptions(attrib=false)
#Check if there is an attribute specified
if attrib
#If there is then assume just this attribute is needed
@@intercom.subscriptions.all.each {|sub| puts "#{sub.send(attrib.to_sym)}"};
else
#Print out default of most useful attributes
@@intercom.subscriptions.all.each {|sub| puts "#{sub.id}, #{sub.url}, #{sub.active}, #{sub.topics}"};
end
end
def get_subscription(id)
#The ID is the only unique element of the webhook
@@intercom.subscriptions.find(:id => id)
end
def delete_subscriptions(id)
#Delete single subscription given Subscription ID
#We dont have any query parameters so 2nd function param is empty string
@@intercom.delete("/subscriptions/#{id}", "")
end
def ping(id)
#ping subscription to test webhook
@@intercom.post("/subscriptions/#{id}/ping", "")
end
end
Then you can issue a ping like this to test your webhook:
web.ping("nsub_1b7d84f0-16a3-11e6-aa08-ed65c3040c0f")
Signed Notifications
One feature that you can also use is the ability to request Intercom to sign your notifications. This way you can provide a secret which we pass in the request that you can check to ensure the notification originated in Intercom. You can read more about it here.
The first thing we need to do is to add an option to pass in some text that acts as a secret when you create the webhook. Let's add another parameter to the create method which we can pass through when we want to add a secret to the webhook. We will default it to false so that we don't need to add anything unless we want to include a secret. We talk more about the hub_secret parameter here.
You could tidy up this method to add in the new option but for now let's just add in an extra check in each case, i.e. whether it is an event webhook or standard webhook, to check if the hub secret is set:
def create_webhook(url, topics, events=false, signed=false)
if events
event_data = {
service_type: "web",
topics: ["event.created"],
url: url,
metadata: {
event_names: events
}
}
#Check if user want to sign request
if signed
event_data[:hub_secret] = signed;
end
#Need to POST event metadata for event.created topic
@@intercom.post("/subscriptions/", event_data)
else
#Create a hash to pass through the Webhook data
webhook = {:url => url,
:topics => topics}
if signed
webhook[:hub_secret] = signed;
end
@@intercom.subscriptions.create(webhook)
end
end
require './intercom_client'
class WebhookTasks < IntercomClient
def initialize
#Leave this empty as simple over-ride
end
def create_webhook(url, topics, events=false, signed=false)
if events
event_data = {
service_type: "web",
topics: ["event.created"],
url: url,
metadata: {
event_names: events
}
}
#Check if user want to sign request
if signed
#events[hub_secret] = ["#{signed}"];
event_data[:hub_secret] = signed;
end
#Need to POST event metadata for event.created topic
@@intercom.post("/subscriptions/", event_data)
else
#Create a hash to pass through the Webhook data
webhook = {:url => url,
:topics => topics}
if signed
webhook[:hub_secret] = signed;
end
@@intercom.subscriptions.create(webhook)
end
end
def update_webhook(id, url, new_topics, events=false)
#Create the data you are going to POST
updates = {
topics: new_topics,
url: url,
}
if events
updates[:metadata] = {event_names: events}
end
@@intercom.post("/subscriptions/#{id}", updates)
end
def list_subscriptions(attrib=false)
#Check if there is an attribute specified
if attrib
#If there is then assume just this attribute is needed
@@intercom.subscriptions.all.each {|sub| puts "#{sub.send(attrib.to_sym)}"};
else
#Print out default of most useful attributes
@@intercom.subscriptions.all.each {|sub| puts "#{sub.id}, #{sub.url}, #{sub.active}, #{sub.topics}"};
end
end
def get_subscription(id)
#The ID is the only unique element of the webhook
@@intercom.subscriptions.find(:id => id)
end
def delete_subscriptions(id)
#Delete single subscription given Subscription ID
#We dont have any query parameters so 2nd function param is empty string
@@intercom.delete("/subscriptions/#{id}", "")
end
def ping(id)
#ping subscription to test webhook
@@intercom.post("/subscriptions/#{id}/ping", "")
end
end
The second thing we need to dois to update out Sinatra code to check when this option is set. When it is we need to generate a new hash and check if they match. We are setting the secret here to be "HELLO" and this will be hardcoded into your code. This is fixed when you pass the option through when you create the webhook. So if you set it to something else you will need to change it here in the code to match what you have set:
require 'sinatra'
require 'json'
set :bind, '0.0.0.0'
post '/webhook' do
body = request.body.read
push = JSON.parse(body)
puts "Webhook JSON Data: #{push.inspect}"
verify_signature(body)
end
def verify_signature(payload_body)
secret = "HELLO"
expected = request.env['HTTP_X_HUB_SIGNATURE']
if expected.nil? || expected.empty? then
puts "Not signed. Not calculating"
else
signature = 'sha1=' + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), secret, payload_body)
puts "Expected : #{expected}"
puts "Calculated: #{signature}"
if Rack::Utils.secure_compare(signature, expected) then
puts " Match"
else
puts " MISMATCH!!!!!!!"
return halt 500, "Signatures didn't match!"
end
end
end
Now you can test your new signed notification by creating a new webhook and then sending a ping request. Note you will need to restart your Sinatra server after making the above change.
>web.create_webhook('http://9382d4ee.ngrok.io/webhook', ["user.created"], events=false, signed="HELLO")
You should then see output like this in the terminal where your Sinatra server is running:
52.87.211.165 - - [13/May/2016:13:00:03 +0000] "POST /webhook HTTP/1.1" 200 - 0.0036
Webhook JSON Data: {"type"=>"notification_event", "app_id"=>"<APP ID>", "data"=>{"type"=>"notification_event_data", "item"=>{"type"=>"ping", "message"=>"something something interzen"}}, "links"=>{}, "id"=>nil, "topic"=>"ping", "delivery_status"=>nil, "delivery_attempts"=>1, "delivered_at"=>0, "first_sent_at"=>1463145837, "created_at"=>1463145837, "self"=>nil}
Expected : sha1=846db3ec80a138d191cffda086e36d1b0daf425e
Calculated: sha1=846db3ec80a138d191cffda086e36d1b0daf425e
Match
54.172.151.69 - - [13/May/2016:13:23:57 +0000] "POST /webhook HTTP/1.1" 200 - 0.0009
Great, it's a match! So you know it is Intercom sending the notification. You can change the secret here and test whether it matches or not or create new webhooks and test them via the ping.
You should now be ready to setup your webhooks to receive notifications relating to your customers and the actions they are taking. Have fun!
Updated over 6 years ago