{"action":"created","comment":{"url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/pulls/comments/63518314","id":63518314,"diff_hunk":"@@ -0,0 +1,225 @@\n+---\n+layout: post\n+title:  \"Active Collab, Errbit, VoipCenter and GitLab notifications in Slack\"\n+date:   2016-01-01 13:37:01\n+image:  2016-06-20-slack-active-collab-errbit-voipcenter-gitlab.gif\n+comments: true\n+---\n+TL;DR: I wrote something to show notifications in specific channels from Active Collab, Errbit and GitLab, and I wanted\n+to brag about it.\n+\n+At SumoCoders we use some tools, each with their own speciality. We use separate tools because we believe there is no \n+\"all-in-one-holy-grail\"-application which will fit our needs, nor will it fit the ever changing way of work in our company. \n+\n+## Slack\n+\n+We use [Slack](https://slack.com/) to discus everything about a project. Each project\n+gets it onw specific channel. The purpose is to have al communication about the project in that single channel, so \n+everybody who works on the project or is interested in the project can follow all communication.\n+\n+On a personal note: although Slack promises to \"be less busy\" when you are using their app, I think with all the possible \n+integrations it will increase the noise in your channels, and therefore people will spend more time on communication.\n+\n+## Active Collab\n+\n+[Active Collab](https://activecollab.com/) is a project management tool. It allows us to organize tasks for a specific project.\n+It is also used for tracking time spent, and we use a custom written application for planning based on the estimates set on\n+a task.\n+\n+So wouldn't it be nice to see a message in Slack when a new task is created, or a task is completed. Active Collab offers\n+a native integration, it kinda sucks for us for some reasons:\n+\n+1. We have over 600 projects, with their interface we had to add each integration with about 5 clicks.\n+2. Their implementation introduces a lot of noise in the channel as they post a lot of events (task creation, completion, \n+   move, time or expenses are logged, user invitations, ...).\n+\n+## Errbit\n+\n+[Errbit](http://errbit.com/) is an Airbrake compliant error catcher. Actually it is an interface where errors are collected,\n+grouped per project and where the errors can be managed. In our projects we have an error-handler which posts the errors\n+to our Errbit-instance.\n+\n+So wouldn't it be nice to see a message when an error occurs? Errbit offers a native integration, but it has the disadvantage \n+that you need a specific hook url per project. Which would require a lot of administration.\n+\n+## VoipCenter + TeamLeader\n+\n+We use [VoipCenter](http://www.voipcenter.be/) as our PBX provider, in short, it is an online VOIPBox, and we use [TeamLeader](http://www.teamleader.eu/en)\n+as a CRM.\n+\n+Wouldn't it be nice if we got the client-details in Slack every time the phone rings?\n+\n+## GitLab (CE)\n+\n+Because we don't like to pay a [gazilion dollars/euro's](https://github.com/blog/2164-introducing-unlimited-private-repositories) \n+to be able to have Git repository management, we have used [GitLab](https://about.GitLab.com/) CE since (I think) the \n+start of SumoCoders. \n+\n+We use GitLab for Pull Request, Code reviewing, ... So every time a PR is made it has to be reviewed by another person. So \n+wouldn't it be nice if we get notifications when a new PR is made, a PR is merged?\n+\n+Integrated in GitLab is [GitLab CI](https://about.GitLab.com/GitLab-ci/), so when a PR is made it will run our tests. Wouldn't\n+it be nice if we got notified if a build passed or failed?\n+\n+GitLab offers a \"Slack-service\", which is ok, but just like Errbit it requires a lot of set up time.\n+\n+## How does it work?\n+\n+So as you can see we had some \"specific\" needs, and the native solutions didn't met these requirements. The only option\n+was to build it the way we want.\n+\n+### The architecture\n+\n+Actually one thing all of the products had in common: they have webhooks. So I have a \"server\" which acts on those webhooks.\n+\n+On the other end I have a \"client\" which is responsible to send the actual messages to Slack. I used a client as I can \n+see other possible \"clients\", eg: push notifications in an iOS-app, ...\n+\n+Everything is written in nodejs, with the [Express](http://expressjs.com/)-framework.\n+\n+The client is connected with websockets to the server, so we can push messages from the server to the client when needed.\n+It is the responsibility of the client to translate the incoming messages into nice formated Slack-messages.\n+\n+### A message\n+\n+The message object is pretty simple it has 3 properties:\n+\n+* project\n+* text\n+* data\n+\n+In the Slack client we use the project to decide in which channel the message needs to be posted, the text-property contains\n+the real message, and the data can be used to enrich the Slack-messages.\n+\n+### Slack-integration\n+\n+To configure everything without a lot of manual setup, I created a [Slack-Slash-command](https://api.slack.com/slash-commands).\n+This command has several subcommands.\n+\n+* `/sumo help`, this will show a usage-message to the user.\n+* `/sumo project`, this will show which Active Collab projects are linked to the current channel. \n+* `/sumo project $id`, this will link the project with the given $id to the current channel. \n+* `/sumo gitlab`, this will show which Gitlab projects are linked to the current channel. \n+* `/sumo gitlab $namespace/$project`, this will link the given repo to the current channel, and configure the needed webhooks.\n+* `/sumo errbit`, this will show which Errbit applications are linked to the current channel. \n+* `/sumo errbit $api-key`, this will link the given Errbit application to the current channel, and configure the needed webhooks. \n+\n+As you will understand this command is the core of the integration, as it will save the mapping between the different\n+applications and the Slack-channel, this is stored in a JSON-configuration on the server.\n+\n+This JSON-configuration is also used by the client to map the incomming messages to the correct Slack-channel.\n+\n+### Active Collab-integration\n+\n+In Active Collab we configured a general webhook to our server, which sends us a lot of notification each time something \n+happens in AC. At the server we will ignore al requests except for those when a task is created or completed.\n+\n+Once we receive such a request we will convert the incoming call into a nice object and find more details about the task\n+which will be converted by the Slack-client into a nice message, which shows the tasknumber, taskname, assignee, due-date, \n+estimate (if provided) and which is also a link to the task in Active Collab.\n+\n+<div class=\"thumbnail\" style=\"max-width: 722px;\">\n+    <img src=\"/assets/posts/2016-06-20-slack-active-collab-1.png\" class=\"img-responsive\">\n+</div>\n+\n+In the native implementation it showed a lot more information, for instance the whole description of the task, which is\n+kind of useless in the Slack-channel.\n+\n+When a task is completed it shows:\n+\n+<div class=\"thumbnail\" style=\"max-width: 722px;\">\n+    <img src=\"/assets/posts/2016-06-20-slack-active-collab-2.png\" class=\"img-responsive\">\n+</div>\n+\n+Where the native implementation shows the full information again, which is pointless, because the person who was assigned\n+only needs all the information, the other channel-members should only know that the task is completed.\n+\n+### Errbit-integration\n+\n+In the past we received a mail for every 1<super>st</super>, 10<super>th</super>, 100<super>th</super, ... time an error \n+occured via mail. While now we receive it every 1, 2, 3 and then every 10<super>th</super>-time, directly in the channel.\n+\n+<div class=\"thumbnail\" style=\"max-width: 722px;\">\n+    <img src=\"/assets/posts/2016-06-20-slack-errbit.png\" class=\"img-responsive\">\n+</div>\n+\n+This is not done by the native Slack-integration, but it is handled by our own implemenation because with the native \n+implementation every occurence is posted in the channel. The configuration of the webhook is done wit the \n+`/sumo errbit $api-key`-command.\n+\n+### VoipCenter-integration\n+\n+In VoipCenter we set up a webhook to our server. So every time we have an inbound or outbound call we get a notification \n+on the server. That incoming call contains a lot of information.\n+\n+Based on the information we build a `Payload`-object, and we check if it is an inbound call and it is the start of the call.\n+In that case we will search for the phone-number in the TeamLeader-contacts and companies.\n+\n+When a result is found (or not) we build a message object. This message is dispatched to the client.\n+\n+The Slack-client will transform the message into a message which contains the basic information about the company (name + phone), \n+or contact (name, compant, cell, phone, email), as you can see in the screenshot below:\n+\n+<div class=\"thumbnail\" style=\"max-width: 722px;\">\n+    <img src=\"/assets/posts/2016-06-20-slack-voipcenter-teamleader.png\" class=\"img-responsive\">\n+</div>\n+\n+### GitLab-integration\n+\n+When we link a GitLab-repo to a channel the webhook is configured by the `/sumo gitlab $namespace/$project`-command.\n+\n+This shows a message in Slack every time a Merge Request is created, with the name of name and assignee. When a Merge Request\n+is merged or closed it also shows a message.\n+\n+<div class=\"thumbnail\" style=\"max-width: 722px;\">\n+    <img src=\"/assets/posts/2016-06-20-slack-gitlab-merge-request.png\" class=\"img-responsive\">\n+</div>\n+\n+Once again the merge is handled differently then the creation of a new Merge Request to not clutter the channel with \n+useless information.\n+\n+When a build succeeds/fails a message will be shown in the channel like this:\n+\n+<div class=\"thumbnail\" style=\"max-width: 722px;\">\n+    <img src=\"/assets/posts/2016-06-20-slack-gitlab-build.png\" class=\"img-responsive\">\n+</div>\n+\n+## What did I learn\n+\n+I personally learned a lot. I learned how to work with nodejs and Express. But I think the best part of this (unfinished) \n+project is that most software-developers really should think about their API's. \n+\n+Most of the API's are designed for internal use, not for external use, or their design is useless because you need a lot\n+of calls to get all the data you need. For instance:\n+\n+To get the contact-details for a company or contact in TeamLeader I have to loop all contacts, companies until I find one, because \n+it <strike>is not</strike> was not possible to search based on a phone-number. The next problem was that I didn't have the full\n+detail of that contact/company directly, therefore an extra call is needed.\n+\n+So if a company does not yet exists, it would take:\n+\n+    ceil(total number of contacts / maximum paging amount) + ceil(total number of companies / maximum paging amount)\n+\n+In our case: \n+\n+    ceil( 1696 / 100 ) + ceil( 1289 / 100 ) = 30\n+\n+Also, a contact can be linked to multiple company, but in the details of a contact we only receive the id's for the linked \n+companies. So if the contact is on the last page this wil result in a bunch of calls:","path":"_posts/2016-06-20-slack-active-collab-errbit-gitlab.md","position":208,"original_position":208,"commit_id":"b5dab53f29b49f4e778e4f0e6ca53c6cfe056c98","original_commit_id":"b5dab53f29b49f4e778e4f0e6ca53c6cfe056c98","user":{"login":"matthiasmullie","id":312776,"avatar_url":"https://avatars.githubusercontent.com/u/312776?v=3","gravatar_id":"","url":"https://api.github.com/users/matthiasmullie","html_url":"https://github.com/matthiasmullie","followers_url":"https://api.github.com/users/matthiasmullie/followers","following_url":"https://api.github.com/users/matthiasmullie/following{/other_user}","gists_url":"https://api.github.com/users/matthiasmullie/gists{/gist_id}","starred_url":"https://api.github.com/users/matthiasmullie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/matthiasmullie/subscriptions","organizations_url":"https://api.github.com/users/matthiasmullie/orgs","repos_url":"https://api.github.com/users/matthiasmullie/repos","events_url":"https://api.github.com/users/matthiasmullie/events{/privacy}","received_events_url":"https://api.github.com/users/matthiasmullie/received_events","type":"User","site_admin":false},"body":"`will`","created_at":"2016-05-17T13:12:12Z","updated_at":"2016-05-17T13:12:12Z","html_url":"https://github.com/tijsverkoyen/tijsverkoyen.github.io/pull/7#discussion_r63518314","pull_request_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/pulls/7","_links":{"self":{"href":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/pulls/comments/63518314"},"html":{"href":"https://github.com/tijsverkoyen/tijsverkoyen.github.io/pull/7#discussion_r63518314"},"pull_request":{"href":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/pulls/7"}}},"pull_request":{"url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/pulls/7","id":70348654,"html_url":"https://github.com/tijsverkoyen/tijsverkoyen.github.io/pull/7","diff_url":"https://github.com/tijsverkoyen/tijsverkoyen.github.io/pull/7.diff","patch_url":"https://github.com/tijsverkoyen/tijsverkoyen.github.io/pull/7.patch","issue_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/issues/7","number":7,"state":"open","locked":false,"title":"Slack notifications","user":{"login":"tijsverkoyen","id":250042,"avatar_url":"https://avatars.githubusercontent.com/u/250042?v=3","gravatar_id":"","url":"https://api.github.com/users/tijsverkoyen","html_url":"https://github.com/tijsverkoyen","followers_url":"https://api.github.com/users/tijsverkoyen/followers","following_url":"https://api.github.com/users/tijsverkoyen/following{/other_user}","gists_url":"https://api.github.com/users/tijsverkoyen/gists{/gist_id}","starred_url":"https://api.github.com/users/tijsverkoyen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tijsverkoyen/subscriptions","organizations_url":"https://api.github.com/users/tijsverkoyen/orgs","repos_url":"https://api.github.com/users/tijsverkoyen/repos","events_url":"https://api.github.com/users/tijsverkoyen/events{/privacy}","received_events_url":"https://api.github.com/users/tijsverkoyen/received_events","type":"User","site_admin":false},"body":"","created_at":"2016-05-17T12:34:00Z","updated_at":"2016-05-17T13:12:12Z","closed_at":null,"merged_at":null,"merge_commit_sha":"7d6e85a05d78bca9382f40610bc16b412cf4eaaf","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/pulls/7/commits","review_comments_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/pulls/7/comments","review_comment_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/pulls/comments{/number}","comments_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/issues/7/comments","statuses_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/statuses/b5dab53f29b49f4e778e4f0e6ca53c6cfe056c98","head":{"label":"tijsverkoyen:slack-notifications","ref":"slack-notifications","sha":"b5dab53f29b49f4e778e4f0e6ca53c6cfe056c98","user":{"login":"tijsverkoyen","id":250042,"avatar_url":"https://avatars.githubusercontent.com/u/250042?v=3","gravatar_id":"","url":"https://api.github.com/users/tijsverkoyen","html_url":"https://github.com/tijsverkoyen","followers_url":"https://api.github.com/users/tijsverkoyen/followers","following_url":"https://api.github.com/users/tijsverkoyen/following{/other_user}","gists_url":"https://api.github.com/users/tijsverkoyen/gists{/gist_id}","starred_url":"https://api.github.com/users/tijsverkoyen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tijsverkoyen/subscriptions","organizations_url":"https://api.github.com/users/tijsverkoyen/orgs","repos_url":"https://api.github.com/users/tijsverkoyen/repos","events_url":"https://api.github.com/users/tijsverkoyen/events{/privacy}","received_events_url":"https://api.github.com/users/tijsverkoyen/received_events","type":"User","site_admin":false},"repo":{"id":41427765,"name":"tijsverkoyen.github.io","full_name":"tijsverkoyen/tijsverkoyen.github.io","owner":{"login":"tijsverkoyen","id":250042,"avatar_url":"https://avatars.githubusercontent.com/u/250042?v=3","gravatar_id":"","url":"https://api.github.com/users/tijsverkoyen","html_url":"https://github.com/tijsverkoyen","followers_url":"https://api.github.com/users/tijsverkoyen/followers","following_url":"https://api.github.com/users/tijsverkoyen/following{/other_user}","gists_url":"https://api.github.com/users/tijsverkoyen/gists{/gist_id}","starred_url":"https://api.github.com/users/tijsverkoyen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tijsverkoyen/subscriptions","organizations_url":"https://api.github.com/users/tijsverkoyen/orgs","repos_url":"https://api.github.com/users/tijsverkoyen/repos","events_url":"https://api.github.com/users/tijsverkoyen/events{/privacy}","received_events_url":"https://api.github.com/users/tijsverkoyen/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/tijsverkoyen/tijsverkoyen.github.io","description":"My public website","fork":false,"url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io","forks_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/forks","keys_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/keys{/key_id}","collaborators_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/teams","hooks_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/hooks","issue_events_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/issues/events{/number}","events_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/events","assignees_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/assignees{/user}","branches_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/branches{/branch}","tags_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/tags","blobs_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/git/refs{/sha}","trees_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/git/trees{/sha}","statuses_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/statuses/{sha}","languages_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/languages","stargazers_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/stargazers","contributors_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/contributors","subscribers_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/subscribers","subscription_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/subscription","commits_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/commits{/sha}","git_commits_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/git/commits{/sha}","comments_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/comments{/number}","issue_comment_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/issues/comments{/number}","contents_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/contents/{+path}","compare_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/compare/{base}...{head}","merges_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/merges","archive_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/downloads","issues_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/issues{/number}","pulls_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/pulls{/number}","milestones_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/milestones{/number}","notifications_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/labels{/name}","releases_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/releases{/id}","deployments_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/deployments","created_at":"2015-08-26T13:40:57Z","updated_at":"2016-04-28T15:18:38Z","pushed_at":"2016-05-17T12:34:00Z","git_url":"git://github.com/tijsverkoyen/tijsverkoyen.github.io.git","ssh_url":"git@github.com:tijsverkoyen/tijsverkoyen.github.io.git","clone_url":"https://github.com/tijsverkoyen/tijsverkoyen.github.io.git","svn_url":"https://github.com/tijsverkoyen/tijsverkoyen.github.io","homepage":null,"size":22175,"stargazers_count":0,"watchers_count":0,"language":"HTML","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":1,"mirror_url":null,"open_issues_count":1,"forks":1,"open_issues":1,"watchers":0,"default_branch":"master"}},"base":{"label":"tijsverkoyen:master","ref":"master","sha":"a9cae09fafa3b4bf04bdd11d6a78ee11de922ef4","user":{"login":"tijsverkoyen","id":250042,"avatar_url":"https://avatars.githubusercontent.com/u/250042?v=3","gravatar_id":"","url":"https://api.github.com/users/tijsverkoyen","html_url":"https://github.com/tijsverkoyen","followers_url":"https://api.github.com/users/tijsverkoyen/followers","following_url":"https://api.github.com/users/tijsverkoyen/following{/other_user}","gists_url":"https://api.github.com/users/tijsverkoyen/gists{/gist_id}","starred_url":"https://api.github.com/users/tijsverkoyen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tijsverkoyen/subscriptions","organizations_url":"https://api.github.com/users/tijsverkoyen/orgs","repos_url":"https://api.github.com/users/tijsverkoyen/repos","events_url":"https://api.github.com/users/tijsverkoyen/events{/privacy}","received_events_url":"https://api.github.com/users/tijsverkoyen/received_events","type":"User","site_admin":false},"repo":{"id":41427765,"name":"tijsverkoyen.github.io","full_name":"tijsverkoyen/tijsverkoyen.github.io","owner":{"login":"tijsverkoyen","id":250042,"avatar_url":"https://avatars.githubusercontent.com/u/250042?v=3","gravatar_id":"","url":"https://api.github.com/users/tijsverkoyen","html_url":"https://github.com/tijsverkoyen","followers_url":"https://api.github.com/users/tijsverkoyen/followers","following_url":"https://api.github.com/users/tijsverkoyen/following{/other_user}","gists_url":"https://api.github.com/users/tijsverkoyen/gists{/gist_id}","starred_url":"https://api.github.com/users/tijsverkoyen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tijsverkoyen/subscriptions","organizations_url":"https://api.github.com/users/tijsverkoyen/orgs","repos_url":"https://api.github.com/users/tijsverkoyen/repos","events_url":"https://api.github.com/users/tijsverkoyen/events{/privacy}","received_events_url":"https://api.github.com/users/tijsverkoyen/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/tijsverkoyen/tijsverkoyen.github.io","description":"My public website","fork":false,"url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io","forks_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/forks","keys_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/keys{/key_id}","collaborators_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/teams","hooks_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/hooks","issue_events_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/issues/events{/number}","events_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/events","assignees_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/assignees{/user}","branches_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/branches{/branch}","tags_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/tags","blobs_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/git/refs{/sha}","trees_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/git/trees{/sha}","statuses_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/statuses/{sha}","languages_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/languages","stargazers_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/stargazers","contributors_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/contributors","subscribers_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/subscribers","subscription_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/subscription","commits_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/commits{/sha}","git_commits_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/git/commits{/sha}","comments_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/comments{/number}","issue_comment_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/issues/comments{/number}","contents_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/contents/{+path}","compare_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/compare/{base}...{head}","merges_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/merges","archive_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/downloads","issues_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/issues{/number}","pulls_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/pulls{/number}","milestones_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/milestones{/number}","notifications_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/labels{/name}","releases_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/releases{/id}","deployments_url":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/deployments","created_at":"2015-08-26T13:40:57Z","updated_at":"2016-04-28T15:18:38Z","pushed_at":"2016-05-17T12:34:00Z","git_url":"git://github.com/tijsverkoyen/tijsverkoyen.github.io.git","ssh_url":"git@github.com:tijsverkoyen/tijsverkoyen.github.io.git","clone_url":"https://github.com/tijsverkoyen/tijsverkoyen.github.io.git","svn_url":"https://github.com/tijsverkoyen/tijsverkoyen.github.io","homepage":null,"size":22175,"stargazers_count":0,"watchers_count":0,"language":"HTML","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":1,"mirror_url":null,"open_issues_count":1,"forks":1,"open_issues":1,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/pulls/7"},"html":{"href":"https://github.com/tijsverkoyen/tijsverkoyen.github.io/pull/7"},"issue":{"href":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/issues/7"},"comments":{"href":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/issues/7/comments"},"review_comments":{"href":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/pulls/7/comments"},"review_comment":{"href":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/pulls/7/commits"},"statuses":{"href":"https://api.github.com/repos/tijsverkoyen/tijsverkoyen.github.io/statuses/b5dab53f29b49f4e778e4f0e6ca53c6cfe056c98"}}}}