diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index 97e39ce99cb8e33f0abaffd2d56abeee7253b4e9..77ad4753c84e2cb9bbff0a1beedabc885d13fde8 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -268,6 +268,7 @@ Rails/Presence:
     - 'app/models/clusters/platforms/kubernetes.rb'
     - 'app/models/concerns/mentionable.rb'
     - 'app/models/concerns/token_authenticatable.rb'
+    - 'app/models/project_services/hipchat_service.rb'
     - 'app/models/project_services/irker_service.rb'
     - 'app/models/project_services/jira_service.rb'
     - 'app/models/project_services/kubernetes_service.rb'
diff --git a/Gemfile b/Gemfile
index 6052018754ac477e34c06b9e6ec87f9ae8df347b..1c7ad5abcb56bb4231db9e3d08c166e3e553d63f 100644
--- a/Gemfile
+++ b/Gemfile
@@ -201,6 +201,9 @@ gem 'connection_pool', '~> 2.0'
 # Discord integration
 gem 'discordrb-webhooks-blackst0ne', '~> 3.3', require: false
 
+# HipChat integration
+gem 'hipchat', '~> 1.5.0'
+
 # JIRA integration
 gem 'jira-ruby', '~> 1.4'
 
diff --git a/Gemfile.lock b/Gemfile.lock
index b522aa85b3931a8af8db7d5a8bf83bd92e1b2517..3314a769949a022d5161edc5988b90d0c01c4501 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -366,6 +366,9 @@ GEM
       hashie (>= 3.0)
     health_check (2.6.0)
       rails (>= 4.0)
+    hipchat (1.5.2)
+      httparty
+      mimemagic
     html-pipeline (2.8.4)
       activesupport (>= 2)
       nokogiri (>= 1.4)
@@ -1040,6 +1043,7 @@ DEPENDENCIES
   hangouts-chat (~> 0.0.5)
   hashie-forbidden_attributes
   health_check (~> 2.6.0)
+  hipchat (~> 1.5.0)
   html-pipeline (~> 2.8)
   html2text
   httparty (~> 0.16.4)
diff --git a/app/models/project.rb b/app/models/project.rb
index 66fc83113eac7932f57b87f33c6055688fcca98d..cb9b2a02eef8e3c74ecddd80abca20fec3711f89 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -146,6 +146,7 @@ class Project < ApplicationRecord
   has_one :pipelines_email_service
   has_one :irker_service
   has_one :pivotaltracker_service
+  has_one :hipchat_service
   has_one :flowdock_service
   has_one :assembla_service
   has_one :asana_service
diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a69b7b4c4b6cdd3639a49a9ca6d7cd8ef9a2e86d
--- /dev/null
+++ b/app/models/project_services/hipchat_service.rb
@@ -0,0 +1,311 @@
+# frozen_string_literal: true
+
+class HipchatService < Service
+  include ActionView::Helpers::SanitizeHelper
+
+  MAX_COMMITS = 3
+  HIPCHAT_ALLOWED_TAGS = %w[
+    a b i strong em br img pre code
+    table th tr td caption colgroup col thead tbody tfoot
+    ul ol li dl dt dd
+  ].freeze
+
+  prop_accessor :token, :room, :server, :color, :api_version
+  boolean_accessor :notify_only_broken_pipelines, :notify
+  validates :token, presence: true, if: :activated?
+
+  def initialize_properties
+    if properties.nil?
+      self.properties = {}
+      self.notify_only_broken_pipelines = true
+    end
+  end
+
+  def title
+    'HipChat'
+  end
+
+  def description
+    'Private group chat and IM'
+  end
+
+  def self.to_param
+    'hipchat'
+  end
+
+  def fields
+    [
+      { type: 'text', name: 'token',     placeholder: 'Room token', required: true },
+      { type: 'text', name: 'room',      placeholder: 'Room name or ID' },
+      { type: 'checkbox', name: 'notify' },
+      { type: 'select', name: 'color', choices: %w(yellow red green purple gray random) },
+      { type: 'text', name: 'api_version',
+        placeholder: 'Leave blank for default (v2)' },
+      { type: 'text', name: 'server',
+        placeholder: 'Leave blank for default. https://hipchat.example.com' },
+      { type: 'checkbox', name: 'notify_only_broken_pipelines' }
+    ]
+  end
+
+  def self.supported_events
+    %w(push issue confidential_issue merge_request note confidential_note tag_push pipeline)
+  end
+
+  def execute(data)
+    return unless supported_events.include?(data[:object_kind])
+
+    message = create_message(data)
+    return unless message.present?
+
+    gate[room].send('GitLab', message, message_options(data)) # rubocop:disable GitlabSecurity/PublicSend
+  end
+
+  def test(data)
+    begin
+      result = execute(data)
+    rescue StandardError => error
+      return { success: false, result: error }
+    end
+
+    { success: true, result: result }
+  end
+
+  private
+
+  def gate
+    options = { api_version: api_version.present? ? api_version : 'v2' }
+    options[:server_url] = server unless server.blank?
+    @gate ||= HipChat::Client.new(token, options)
+  end
+
+  def message_options(data = nil)
+    { notify: notify.present? && Gitlab::Utils.to_boolean(notify), color: message_color(data) }
+  end
+
+  def create_message(data)
+    object_kind = data[:object_kind]
+
+    case object_kind
+    when "push", "tag_push"
+      create_push_message(data)
+    when "issue"
+      create_issue_message(data) unless update?(data)
+    when "merge_request"
+      create_merge_request_message(data) unless update?(data)
+    when "note"
+      create_note_message(data)
+    when "pipeline"
+      create_pipeline_message(data) if should_pipeline_be_notified?(data)
+    end
+  end
+
+  def render_line(text)
+    markdown(text.lines.first.chomp, pipeline: :single_line) if text
+  end
+
+  def create_push_message(push)
+    ref_type = Gitlab::Git.tag_ref?(push[:ref]) ? 'tag' : 'branch'
+    ref = Gitlab::Git.ref_name(push[:ref])
+
+    before = push[:before]
+    after = push[:after]
+
+    message = []
+    message << "#{push[:user_name]} "
+
+    if Gitlab::Git.blank_ref?(before)
+      message << "pushed new #{ref_type} <a href=\""\
+                 "#{project_url}/commits/#{CGI.escape(ref)}\">#{ref}</a>"\
+                 " to #{project_link}\n"
+    elsif Gitlab::Git.blank_ref?(after)
+      message << "removed #{ref_type} <b>#{ref}</b> from <a href=\"#{project.web_url}\">#{project_name}</a> \n"
+    else
+      message << "pushed to #{ref_type} <a href=\""\
+                  "#{project.web_url}/commits/#{CGI.escape(ref)}\">#{ref}</a> "
+      message << "of <a href=\"#{project.web_url}\">#{project.full_name.gsub!(/\s/, '')}</a> "
+      message << "(<a href=\"#{project.web_url}/compare/#{before}...#{after}\">Compare changes</a>)"
+
+      push[:commits].take(MAX_COMMITS).each do |commit|
+        message << "<br /> - #{render_line(commit[:message])} (<a href=\"#{commit[:url]}\">#{commit[:id][0..5]}</a>)"
+      end
+
+      if push[:commits].count > MAX_COMMITS
+        message << "<br />... #{push[:commits].count - MAX_COMMITS} more commits"
+      end
+    end
+
+    message.join
+  end
+
+  def markdown(text, options = {})
+    return "" unless text
+
+    context = {
+      project: project,
+      pipeline: :email
+    }
+
+    Banzai.render(text, context)
+
+    context.merge!(options)
+
+    html = Banzai.render_and_post_process(text, context)
+    sanitized_html = sanitize(html, tags: HIPCHAT_ALLOWED_TAGS, attributes: %w[href title alt])
+
+    sanitized_html.truncate(200, separator: ' ', omission: '...')
+  end
+
+  def create_issue_message(data)
+    user_name = data[:user][:name]
+
+    obj_attr = data[:object_attributes]
+    obj_attr = HashWithIndifferentAccess.new(obj_attr)
+    title = render_line(obj_attr[:title])
+    state = obj_attr[:state]
+    issue_iid = obj_attr[:iid]
+    issue_url = obj_attr[:url]
+    description = obj_attr[:description]
+
+    issue_link = "<a href=\"#{issue_url}\">issue ##{issue_iid}</a>"
+
+    message = ["#{user_name} #{state} #{issue_link} in #{project_link}: <b>#{title}</b>"]
+    message << "<pre>#{markdown(description)}</pre>"
+
+    message.join
+  end
+
+  def create_merge_request_message(data)
+    user_name = data[:user][:name]
+
+    obj_attr = data[:object_attributes]
+    obj_attr = HashWithIndifferentAccess.new(obj_attr)
+    merge_request_id = obj_attr[:iid]
+    state = obj_attr[:state]
+    description = obj_attr[:description]
+    title = render_line(obj_attr[:title])
+
+    merge_request_url = "#{project_url}/merge_requests/#{merge_request_id}"
+    merge_request_link = "<a href=\"#{merge_request_url}\">merge request !#{merge_request_id}</a>"
+    message = ["#{user_name} #{state} #{merge_request_link} in " \
+      "#{project_link}: <b>#{title}</b>"]
+
+    message << "<pre>#{markdown(description)}</pre>"
+    message.join
+  end
+
+  def format_title(title)
+    "<b>#{render_line(title)}</b>"
+  end
+
+  def create_note_message(data)
+    data = HashWithIndifferentAccess.new(data)
+    user_name = data[:user][:name]
+
+    obj_attr = HashWithIndifferentAccess.new(data[:object_attributes])
+    note = obj_attr[:note]
+    note_url = obj_attr[:url]
+    noteable_type = obj_attr[:noteable_type]
+    commit_id = nil
+
+    case noteable_type
+    when "Commit"
+      commit_attr = HashWithIndifferentAccess.new(data[:commit])
+      commit_id = commit_attr[:id]
+      subject_desc = commit_id
+      subject_desc = Commit.truncate_sha(subject_desc)
+      subject_type = "commit"
+      title = format_title(commit_attr[:message])
+    when "Issue"
+      subj_attr = HashWithIndifferentAccess.new(data[:issue])
+      subject_id = subj_attr[:iid]
+      subject_desc = "##{subject_id}"
+      subject_type = "issue"
+      title = format_title(subj_attr[:title])
+    when "MergeRequest"
+      subj_attr = HashWithIndifferentAccess.new(data[:merge_request])
+      subject_id = subj_attr[:iid]
+      subject_desc = "!#{subject_id}"
+      subject_type = "merge request"
+      title = format_title(subj_attr[:title])
+    when "Snippet"
+      subj_attr = HashWithIndifferentAccess.new(data[:snippet])
+      subject_id = subj_attr[:id]
+      subject_desc = "##{subject_id}"
+      subject_type = "snippet"
+      title = format_title(subj_attr[:title])
+    end
+
+    subject_html = "<a href=\"#{note_url}\">#{subject_type} #{subject_desc}</a>"
+    message = ["#{user_name} commented on #{subject_html} in #{project_link}: "]
+    message << title
+
+    message << "<pre>#{markdown(note, ref: commit_id)}</pre>"
+    message.join
+  end
+
+  def create_pipeline_message(data)
+    pipeline_attributes = data[:object_attributes]
+    pipeline_id = pipeline_attributes[:id]
+    ref_type = pipeline_attributes[:tag] ? 'tag' : 'branch'
+    ref = pipeline_attributes[:ref]
+    user_name = (data[:user] && data[:user][:name]) || 'API'
+    status = pipeline_attributes[:status]
+    duration = pipeline_attributes[:duration]
+
+    branch_link = "<a href=\"#{project_url}/commits/#{CGI.escape(ref)}\">#{ref}</a>"
+    pipeline_url = "<a href=\"#{project_url}/pipelines/#{pipeline_id}\">##{pipeline_id}</a>"
+
+    "#{project_link}: Pipeline #{pipeline_url} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status(status)} in #{duration} second(s)"
+  end
+
+  def message_color(data)
+    pipeline_status_color(data) || color || 'yellow'
+  end
+
+  def pipeline_status_color(data)
+    return unless data && data[:object_kind] == 'pipeline'
+
+    case data[:object_attributes][:status]
+    when 'success'
+      'green'
+    else
+      'red'
+    end
+  end
+
+  def project_name
+    project.full_name.gsub(/\s/, '')
+  end
+
+  def project_url
+    project.web_url
+  end
+
+  def project_link
+    "<a href=\"#{project_url}\">#{project_name}</a>"
+  end
+
+  def update?(data)
+    data[:object_attributes][:action] == 'update'
+  end
+
+  def humanized_status(status)
+    case status
+    when 'success'
+      'passed'
+    else
+      status
+    end
+  end
+
+  def should_pipeline_be_notified?(data)
+    case data[:object_attributes][:status]
+    when 'success'
+      !notify_only_broken_pipelines?
+    when 'failed'
+      true
+    else
+      false
+    end
+  end
+end
diff --git a/app/models/service.rb b/app/models/service.rb
index c6d5eb353dc71b27346c59f5589a853dadbb71d9..de549becf719093773975ce84273d711e8d66f8f 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -255,6 +255,7 @@ def self.available_services_names
       external_wiki
       flowdock
       hangouts_chat
+      hipchat
       irker
       jira
       kubernetes
diff --git a/config/initializers/hipchat_client_patch.rb b/config/initializers/hipchat_client_patch.rb
new file mode 100644
index 0000000000000000000000000000000000000000..aec265312bb3b3222e84fca2579506bfdd19b79b
--- /dev/null
+++ b/config/initializers/hipchat_client_patch.rb
@@ -0,0 +1,14 @@
+# This monkey patches the HTTParty used in https://github.com/hipchat/hipchat-rb.
+module HipChat
+  class Client
+    connection_adapter ::Gitlab::ProxyHTTPConnectionAdapter
+  end
+
+  class Room
+    connection_adapter ::Gitlab::ProxyHTTPConnectionAdapter
+  end
+
+  class User
+    connection_adapter ::Gitlab::ProxyHTTPConnectionAdapter
+  end
+end
diff --git a/db/migrate/20190107151020_add_services_type_index.rb b/db/migrate/20190107151020_add_services_type_index.rb
deleted file mode 100644
index 26b5bd5875056e3a42f86995719b3743cc993e77..0000000000000000000000000000000000000000
--- a/db/migrate/20190107151020_add_services_type_index.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-# frozen_string_literal: true
-
-# See http://doc.gitlab.com/ce/development/migration_style_guide.html
-# for more information on how to write migrations for GitLab.
-
-class AddServicesTypeIndex < ActiveRecord::Migration[5.0]
-  include Gitlab::Database::MigrationHelpers
-
-  DOWNTIME = false
-
-  disable_ddl_transaction!
-
-  def up
-    add_concurrent_index :services, :type unless index_exists?(:services, :type)
-  end
-
-  def down
-    remove_concurrent_index :services, :type if index_exists?(:services, :type)
-  end
-end
diff --git a/db/migrate/20190107151029_remove_hipchat_services.rb b/db/migrate/20190107151029_remove_hipchat_services.rb
deleted file mode 100644
index 4741ec88907a5a3eab10d45c31d74070d4c9e5f2..0000000000000000000000000000000000000000
--- a/db/migrate/20190107151029_remove_hipchat_services.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-# frozen_string_literal: true
-
-# See http://doc.gitlab.com/ce/development/migration_style_guide.html
-# for more information on how to write migrations for GitLab.
-
-class RemoveHipchatServices < ActiveRecord::Migration[5.0]
-  DOWNTIME = false
-
-  def up
-    execute "DELETE FROM services WHERE type = 'HipchatService'"
-  end
-
-  def down
-    # no-op
-  end
-end
diff --git a/db/schema.rb b/db/schema.rb
index d1b3672725d4d2c69171a88a54e1bfd5db356f92..df47f988901d9d8ac1c1b94f9a65aa396b18be12 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -1996,7 +1996,6 @@
     t.boolean "confidential_note_events", default: true
     t.index ["project_id"], name: "index_services_on_project_id", using: :btree
     t.index ["template"], name: "index_services_on_template", using: :btree
-    t.index ["type"], name: "index_services_on_type", using: :btree
   end
 
   create_table "shards", force: :cascade do |t|
diff --git a/doc/api/services.md b/doc/api/services.md
index 1f84e2de7de04ba7bd589a8d35b1b306c500d1ee..e8ae7ff78f4bf5d86bbd04dfddd98284cd57c690 100644
--- a/doc/api/services.md
+++ b/doc/api/services.md
@@ -449,6 +449,45 @@ Get Hangouts Chat service settings for a project.
 GET /projects/:id/services/hangouts-chat
 ```
 
+## HipChat
+
+Private group chat and IM
+
+### Create/Edit HipChat service
+
+Set HipChat service for a project.
+
+```
+PUT /projects/:id/services/hipchat
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `token` | string | true | Room token |
+| `color` | string | false | The room color |
+| `notify` | boolean | false | Enable notifications |
+| `room` | string | false |Room name or ID |
+| `api_version` | string | false | Leave blank for default (v2) |
+| `server` | string | false | Leave blank for default. For example, `https://hipchat.example.com`. |
+
+### Delete HipChat service
+
+Delete HipChat service for a project.
+
+```
+DELETE /projects/:id/services/hipchat
+```
+
+### Get HipChat service settings
+
+Get HipChat service settings for a project.
+
+```
+GET /projects/:id/services/hipchat
+```
+
 ## Irker (IRC gateway)
 
 Send IRC messages, on update, to a list of recipients through an Irker gateway.
diff --git a/doc/integration/README.md b/doc/integration/README.md
index f5bc0693b84ded3da3edb5a4274b443c2abc201d..a539933f2234b4d6d777d6bed443c74182c0faca 100644
--- a/doc/integration/README.md
+++ b/doc/integration/README.md
@@ -29,8 +29,8 @@ See the documentation below for details on how to configure these services.
 
 ## Project services
 
-Integration with services such as Campfire, Flowdock, Pivotal Tracker, and Slack
-are available in the form of a [Project Service][].
+Integration with services such as Campfire, Flowdock, HipChat,
+Pivotal Tracker, and Slack are available in the form of a [Project Service][].
 
 [Project Service]: ../user/project/integrations/project_services.md
 
diff --git a/doc/project_services/hipchat.md b/doc/project_services/hipchat.md
new file mode 100644
index 0000000000000000000000000000000000000000..4ae9f6c6b2e4dab73105602a3cdf6bcd1a4e09c3
--- /dev/null
+++ b/doc/project_services/hipchat.md
@@ -0,0 +1 @@
+This document was moved to [user/project/integrations/hipchat.md](../user/project/integrations/hipchat.md).
diff --git a/doc/university/glossary/README.md b/doc/university/glossary/README.md
index 254e234a22cd0245604e95a5b281070e1d620df5..0af2f8d2f542fff266bb8f7bf48b8b2dd8dc0c2e 100644
--- a/doc/university/glossary/README.md
+++ b/doc/university/glossary/README.md
@@ -41,7 +41,7 @@ Objects (usually binary and large) created by a build process. These can include
 
 ### Atlassian
 
-A [company](https://www.atlassian.com) that develops software products for developers and project managers including Bitbucket, Jira, Confluence, Bamboo.
+A [company](https://www.atlassian.com) that develops software products for developers and project managers including Bitbucket, Jira, Hipchat, Confluence, Bamboo.
 
 ### Audit Log
 
diff --git a/doc/user/index.md b/doc/user/index.md
index 626246447f3bf2766190cf9d6e30eb381d4f802b..8164b31c37e80726ec746f00c8dca1441e1594c0 100644
--- a/doc/user/index.md
+++ b/doc/user/index.md
@@ -65,9 +65,7 @@ With GitLab Enterprise Edition, you can also:
 - View the current health and status of each CI environment running on Kubernetes with [Deploy Boards](https://docs.gitlab.com/ee/user/project/deploy_boards.html).
 - Leverage continuous delivery method with [Canary Deployments](https://docs.gitlab.com/ee/user/project/canary_deployments.html).
 
-You can also [integrate](project/integrations/project_services.md) GitLab with
-numerous third-party applications, such as Mattermost, Microsoft Teams, Trello,
-Slack, Bamboo CI, JIRA, and a lot more.
+You can also [integrate](project/integrations/project_services.md) GitLab with numerous third-party applications, such as Mattermost, Microsoft Teams, HipChat, Trello, Slack, Bamboo CI, JIRA, and a lot more.
 
 ## Projects
 
diff --git a/doc/user/project/integrations/hipchat.md b/doc/user/project/integrations/hipchat.md
new file mode 100644
index 0000000000000000000000000000000000000000..0fd847d415f9ceaf536899741a19c4728d769dc2
--- /dev/null
+++ b/doc/user/project/integrations/hipchat.md
@@ -0,0 +1,53 @@
+# Atlassian HipChat
+
+GitLab provides a way to send HipChat notifications upon a number of events,
+such as when a user pushes code, creates a branch or tag, adds a comment, and
+creates a merge request.
+
+## Setup
+
+GitLab requires the use of a HipChat v2 API token to work. v1 tokens are
+not supported at this time. Note the differences between v1 and v2 tokens:
+
+HipChat v1 API (legacy) supports "API Auth Tokens" in the Group API menu. A v1
+token is allowed to send messages to *any* room.
+
+HipChat v2 API has tokens that are can be created using the Integrations tab
+in the Group or Room admin page. By design, these are lightweight tokens that
+allow GitLab to send messages only to *one* room.
+
+### Complete these steps in HipChat
+
+1. Go to: <https://admin.hipchat.com/admin>
+1. Click on "Group Admin" -> "Integrations".
+1. Find "Build Your Own!" and click "Create".
+1. Select the desired room, name the integration "GitLab", and click "Create".
+1. In the "Send messages to this room by posting this URL" column, you should
+see a URL in the format:
+
+```
+https://api.hipchat.com/v2/room/<room>/notification?auth_token=<token>
+```
+
+HipChat is now ready to accept messages from GitLab. Next, set up the HipChat
+service in GitLab.
+
+### Complete these steps in GitLab
+
+1. Navigate to the project you want to configure for notifications.
+1. Navigate to the [Integrations page](project_services.md#accessing-the-project-services)
+1. Click "HipChat".
+1. Select the "Active" checkbox.
+1. Insert the `token` field from the URL into the `Token` field on the Web page.
+1. Insert the `room` field from the URL into the `Room` field on the Web page.
+1. Save or optionally click "Test Settings".
+
+## Troubleshooting
+
+If you do not see notifications, make sure you are using a HipChat v2 API
+token, not a v1 token.
+
+Note that the v2 token is tied to a specific room. If you want to be able to
+specify arbitrary rooms, you can create an API token for a specific user in
+HipChat under "Account settings" and "API access". Use the `XXX` value under
+`auth_token=XXX`.
diff --git a/doc/user/project/integrations/project_services.md b/doc/user/project/integrations/project_services.md
index e2f23827360b6dd00fc5d5b8c3ebf40b698c9e15..42c7824a12540d50faa67e691dd2c6d5025fe95e 100644
--- a/doc/user/project/integrations/project_services.md
+++ b/doc/user/project/integrations/project_services.md
@@ -36,6 +36,7 @@ Click on the service links to see further configuration instructions and details
 | External Wiki | Replaces the link to the internal wiki with a link to an external wiki |
 | Flowdock | Flowdock is a collaboration web app for technical teams |
 | [Hangouts Chat](hangouts_chat.md) | Receive events notifications in Google Hangouts Chat |
+| [HipChat](hipchat.md) | Private group chat and IM |
 | [Irker (IRC gateway)](irker.md) | Send IRC messages, on update, to a list of recipients through an Irker gateway |
 | [JIRA](jira.md) | JIRA issue tracker |
 | JetBrains TeamCity CI | A continuous integration and build server |
diff --git a/lib/api/helpers/services_helpers.rb b/lib/api/helpers/services_helpers.rb
index 8582c45798f63b22095972a938f11d88f5544bb0..953be7f3798dd713c6151234eecbd5dd4e9b47cb 100644
--- a/lib/api/helpers/services_helpers.rb
+++ b/lib/api/helpers/services_helpers.rb
@@ -1,3 +1,4 @@
+# coding: utf-8
 # frozen_string_literal: true
 
 module API
@@ -386,6 +387,44 @@ def self.services
             },
             chat_notification_events
           ].flatten,
+          'hipchat' => [
+            {
+              required: true,
+              name: :token,
+              type: String,
+              desc: 'The room token'
+            },
+            {
+              required: false,
+              name: :room,
+              type: String,
+              desc: 'The room name or ID'
+            },
+            {
+              required: false,
+              name: :color,
+              type: String,
+              desc: 'The room color'
+            },
+            {
+              required: false,
+              name: :notify,
+              type: Boolean,
+              desc: 'Enable notifications'
+            },
+            {
+              required: false,
+              name: :api_version,
+              type: String,
+              desc: 'Leave blank for default (v2)'
+            },
+            {
+              required: false,
+              name: :server,
+              type: String,
+              desc: 'Leave blank for default. https://hipchat.example.com'
+            }
+          ],
           'irker' => [
             {
               required: true,
@@ -690,6 +729,7 @@ def self.service_classes
           ::ExternalWikiService,
           ::FlowdockService,
           ::HangoutsChatService,
+          ::HipchatService,
           ::IrkerService,
           ::JiraService,
           ::KubernetesService,
diff --git a/spec/factories/services.rb b/spec/factories/services.rb
index 70c34f8640be02b872c73139f94c1809e28651fd..0d8c26a2ee93d95a213a490ba12c3dbd3fe0f5f6 100644
--- a/spec/factories/services.rb
+++ b/spec/factories/services.rb
@@ -62,4 +62,10 @@
       project_key: 'jira-key'
     )
   end
+
+  factory :hipchat_service do
+    project
+    type 'HipchatService'
+    token 'test_token'
+  end
 end
diff --git a/spec/features/projects/services/disable_triggers_spec.rb b/spec/features/projects/services/disable_triggers_spec.rb
index 65b597da26977b8fec5360c83824481e42701b15..1a13fe03a67524c9e7c46fd48cd8e6df4852c69b 100644
--- a/spec/features/projects/services/disable_triggers_spec.rb
+++ b/spec/features/projects/services/disable_triggers_spec.rb
@@ -14,11 +14,10 @@
   end
 
   context 'service has multiple supported events' do
-    let(:service_name) { 'JIRA' }
+    let(:service_name) { 'HipChat' }
 
     it 'shows trigger checkboxes' do
-      event_count = JiraService.supported_events.count
-      expect(event_count).to be > 1
+      event_count = HipchatService.supported_events.count
 
       expect(page).to have_content "Trigger"
       expect(page).to have_css(checkbox_selector, count: event_count)
diff --git a/spec/features/projects/services/user_activates_hipchat_spec.rb b/spec/features/projects/services/user_activates_hipchat_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2f5313c91f91d0086d12730d6a6657973ee1d7b6
--- /dev/null
+++ b/spec/features/projects/services/user_activates_hipchat_spec.rb
@@ -0,0 +1,38 @@
+require 'spec_helper'
+
+describe 'User activates HipChat' do
+  let(:project) { create(:project) }
+  let(:user) { create(:user) }
+
+  before do
+    project.add_maintainer(user)
+    sign_in(user)
+
+    visit(project_settings_integrations_path(project))
+
+    click_link('HipChat')
+  end
+
+  context 'with standart settings' do
+    it 'activates service' do
+      check('Active')
+      fill_in('Room', with: 'gitlab')
+      fill_in('Token', with: 'verySecret')
+      click_button('Save')
+
+      expect(page).to have_content('HipChat activated.')
+    end
+  end
+
+  context 'with custom settings' do
+    it 'activates service' do
+      check('Active')
+      fill_in('Room', with: 'gitlab_custom')
+      fill_in('Token', with: 'secretCustom')
+      fill_in('Server', with: 'https://chat.example.com')
+      click_button('Save')
+
+      expect(page).to have_content('HipChat activated.')
+    end
+  end
+end
diff --git a/spec/features/projects/services/user_views_services_spec.rb b/spec/features/projects/services/user_views_services_spec.rb
index b0a838a7d2b83fe47711557be95981f6e4cda4f6..e9c8cf0fe34d5da32e04547c417a30fbc9cc1886 100644
--- a/spec/features/projects/services/user_views_services_spec.rb
+++ b/spec/features/projects/services/user_views_services_spec.rb
@@ -14,6 +14,7 @@
   it 'shows the list of available services' do
     expect(page).to have_content('Project services')
     expect(page).to have_content('Campfire')
+    expect(page).to have_content('HipChat')
     expect(page).to have_content('Assembla')
     expect(page).to have_content('Pushover')
     expect(page).to have_content('Atlassian Bamboo')
@@ -21,7 +22,5 @@
     expect(page).to have_content('Asana')
     expect(page).to have_content('Irker (IRC gateway)')
     expect(page).to have_content('Packagist')
-    expect(page).to have_content('Mattermost')
-    expect(page).to have_content('Slack')
   end
 end
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index ed557ffd4e3950451225a0ed75046445621423c5..54369ff75f4081386da24d21604d74ebf75afb82 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -223,6 +223,7 @@ project:
 - packagist_service
 - pivotaltracker_service
 - prometheus_service
+- hipchat_service
 - flowdock_service
 - assembla_service
 - asana_service
diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json
index 773651dd22637d0310575406db93103bd484dbc2..4a7accc4c5236e68d389ba1a8425898528fa1148 100644
--- a/spec/lib/gitlab/import_export/project.json
+++ b/spec/lib/gitlab/import_export/project.json
@@ -6794,6 +6794,28 @@
       "default": false,
       "wiki_page_events": true
     },
+    {
+      "id": 93,
+      "title": "HipChat",
+      "project_id": 5,
+      "created_at": "2016-06-14T15:01:51.219Z",
+      "updated_at": "2016-06-14T15:01:51.219Z",
+      "active": false,
+      "properties": {
+        "notify_only_broken_pipelines": true
+      },
+      "template": false,
+      "push_events": true,
+      "issues_events": true,
+      "merge_requests_events": true,
+      "tag_push_events": true,
+      "note_events": true,
+      "pipeline_events": true,
+      "type": "HipchatService",
+      "category": "common",
+      "default": false,
+      "wiki_page_events": true
+    },
     {
       "id": 91,
       "title": "Flowdock",
diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b0fd2ceead02fdb3960182b6627a2878e941a471
--- /dev/null
+++ b/spec/models/project_services/hipchat_service_spec.rb
@@ -0,0 +1,408 @@
+require 'spec_helper'
+
+describe HipchatService do
+  describe "Associations" do
+    it { is_expected.to belong_to :project }
+    it { is_expected.to have_one :service_hook }
+  end
+
+  describe 'Validations' do
+    context 'when service is active' do
+      before do
+        subject.active = true
+      end
+
+      it { is_expected.to validate_presence_of(:token) }
+    end
+
+    context 'when service is inactive' do
+      before do
+        subject.active = false
+      end
+
+      it { is_expected.not_to validate_presence_of(:token) }
+    end
+  end
+
+  describe "Execute" do
+    let(:hipchat) { described_class.new }
+    let(:user)    { create(:user) }
+    let(:project) { create(:project, :repository) }
+    let(:api_url) { 'https://hipchat.example.com/v2/room/123456/notification?auth_token=verySecret' }
+    let(:project_name) { project.full_name.gsub(/\s/, '') }
+    let(:token) { 'verySecret' }
+    let(:server_url) { 'https://hipchat.example.com'}
+    let(:push_sample_data) do
+      Gitlab::DataBuilder::Push.build_sample(project, user)
+    end
+
+    before do
+      allow(hipchat).to receive_messages(
+        project_id: project.id,
+        project: project,
+        room: 123456,
+        server: server_url,
+        token: token
+      )
+      WebMock.stub_request(:post, api_url)
+    end
+
+    it 'tests and return errors' do
+      allow(hipchat).to receive(:execute).and_raise(StandardError, 'no such room')
+      result = hipchat.test(push_sample_data)
+
+      expect(result[:success]).to be_falsey
+      expect(result[:result].to_s).to eq('no such room')
+    end
+
+    it 'uses v1 if version is provided' do
+      allow(hipchat).to receive(:api_version).and_return('v1')
+      expect(HipChat::Client).to receive(:new).with(
+        token,
+        api_version: 'v1',
+        server_url: server_url
+      ).and_return(double(:hipchat_service).as_null_object)
+      hipchat.execute(push_sample_data)
+    end
+
+    it 'uses v2 as the version when nothing is provided' do
+      allow(hipchat).to receive(:api_version).and_return('')
+      expect(HipChat::Client).to receive(:new).with(
+        token,
+        api_version: 'v2',
+        server_url: server_url
+      ).and_return(double(:hipchat_service).as_null_object)
+      hipchat.execute(push_sample_data)
+    end
+
+    context 'push events' do
+      it "calls Hipchat API for push events" do
+        hipchat.execute(push_sample_data)
+
+        expect(WebMock).to have_requested(:post, api_url).once
+      end
+
+      it "creates a push message" do
+        message = hipchat.send(:create_push_message, push_sample_data)
+
+        push_sample_data[:object_attributes]
+        branch = push_sample_data[:ref].gsub('refs/heads/', '')
+        expect(message).to include("#{user.name} pushed to branch " \
+            "<a href=\"#{project.web_url}/commits/#{branch}\">#{branch}</a> of " \
+            "<a href=\"#{project.web_url}\">#{project_name}</a>")
+      end
+    end
+
+    context 'tag_push events' do
+      let(:push_sample_data) do
+        Gitlab::DataBuilder::Push.build(
+          project,
+          user,
+          Gitlab::Git::BLANK_SHA,
+          '1' * 40,
+          'refs/tags/test',
+          [])
+      end
+
+      it "calls Hipchat API for tag push events" do
+        hipchat.execute(push_sample_data)
+
+        expect(WebMock).to have_requested(:post, api_url).once
+      end
+
+      it "creates a tag push message" do
+        message = hipchat.send(:create_push_message, push_sample_data)
+
+        push_sample_data[:object_attributes]
+        expect(message).to eq("#{user.name} pushed new tag " \
+            "<a href=\"#{project.web_url}/commits/test\">test</a> to " \
+            "<a href=\"#{project.web_url}\">#{project_name}</a>\n")
+      end
+    end
+
+    context 'issue events' do
+      let(:issue) { create(:issue, title: 'Awesome issue', description: '**please** fix') }
+      let(:issue_service) { Issues::CreateService.new(project, user) }
+      let(:issues_sample_data) { issue_service.hook_data(issue, 'open') }
+
+      it "calls Hipchat API for issue events" do
+        hipchat.execute(issues_sample_data)
+
+        expect(WebMock).to have_requested(:post, api_url).once
+      end
+
+      it "creates an issue message" do
+        message = hipchat.send(:create_issue_message, issues_sample_data)
+
+        obj_attr = issues_sample_data[:object_attributes]
+        expect(message).to eq("#{user.name} opened " \
+            "<a href=\"#{obj_attr[:url]}\">issue ##{obj_attr["iid"]}</a> in " \
+            "<a href=\"#{project.web_url}\">#{project_name}</a>: " \
+            "<b>Awesome issue</b>" \
+            "<pre><strong>please</strong> fix</pre>")
+      end
+    end
+
+    context 'merge request events' do
+      let(:merge_request) { create(:merge_request, description: '**please** fix', title: 'Awesome merge request', target_project: project, source_project: project) }
+      let(:merge_service) { MergeRequests::CreateService.new(project, user) }
+      let(:merge_sample_data) { merge_service.hook_data(merge_request, 'open') }
+
+      it "calls Hipchat API for merge requests events" do
+        hipchat.execute(merge_sample_data)
+
+        expect(WebMock).to have_requested(:post, api_url).once
+      end
+
+      it "creates a merge request message" do
+        message = hipchat.send(:create_merge_request_message,
+                               merge_sample_data)
+
+        obj_attr = merge_sample_data[:object_attributes]
+        expect(message).to eq("#{user.name} opened " \
+            "<a href=\"#{obj_attr[:url]}\">merge request !#{obj_attr["iid"]}</a> in " \
+            "<a href=\"#{project.web_url}\">#{project_name}</a>: " \
+            "<b>Awesome merge request</b>" \
+            "<pre><strong>please</strong> fix</pre>")
+      end
+    end
+
+    context "Note events" do
+      let(:user) { create(:user) }
+      let(:project) { create(:project, :repository, creator: user) }
+
+      context 'when commit comment event triggered' do
+        let(:commit_note) do
+          create(:note_on_commit, author: user, project: project,
+                                  commit_id: project.repository.commit.id,
+                                  note: 'a comment on a commit')
+        end
+
+        it "calls Hipchat API for commit comment events" do
+          data = Gitlab::DataBuilder::Note.build(commit_note, user)
+          hipchat.execute(data)
+
+          expect(WebMock).to have_requested(:post, api_url).once
+
+          message = hipchat.send(:create_message, data)
+
+          obj_attr = data[:object_attributes]
+          commit_id = Commit.truncate_sha(data[:commit][:id])
+          title = hipchat.send(:format_title, data[:commit][:message])
+
+          expect(message).to eq("#{user.name} commented on " \
+              "<a href=\"#{obj_attr[:url]}\">commit #{commit_id}</a> in " \
+              "<a href=\"#{project.web_url}\">#{project_name}</a>: " \
+              "#{title}" \
+              "<pre>a comment on a commit</pre>")
+        end
+      end
+
+      context 'when merge request comment event triggered' do
+        let(:merge_request) do
+          create(:merge_request, source_project: project,
+                                 target_project: project)
+        end
+
+        let(:merge_request_note) do
+          create(:note_on_merge_request, noteable: merge_request,
+                                         project: project,
+                                         note: "merge request **note**")
+        end
+
+        it "calls Hipchat API for merge request comment events" do
+          data = Gitlab::DataBuilder::Note.build(merge_request_note, user)
+          hipchat.execute(data)
+
+          expect(WebMock).to have_requested(:post, api_url).once
+
+          message = hipchat.send(:create_message, data)
+
+          obj_attr = data[:object_attributes]
+          merge_id = data[:merge_request]['iid']
+          title = data[:merge_request]['title']
+
+          expect(message).to eq("#{user.name} commented on " \
+              "<a href=\"#{obj_attr[:url]}\">merge request !#{merge_id}</a> in " \
+              "<a href=\"#{project.web_url}\">#{project_name}</a>: " \
+              "<b>#{title}</b>" \
+              "<pre>merge request <strong>note</strong></pre>")
+        end
+      end
+
+      context 'when issue comment event triggered' do
+        let(:issue) { create(:issue, project: project) }
+        let(:issue_note) do
+          create(:note_on_issue, noteable: issue, project: project,
+                                 note: "issue **note**")
+        end
+
+        it "calls Hipchat API for issue comment events" do
+          data = Gitlab::DataBuilder::Note.build(issue_note, user)
+          hipchat.execute(data)
+
+          message = hipchat.send(:create_message, data)
+
+          obj_attr = data[:object_attributes]
+          issue_id = data[:issue]['iid']
+          title = data[:issue]['title']
+
+          expect(message).to eq("#{user.name} commented on " \
+              "<a href=\"#{obj_attr[:url]}\">issue ##{issue_id}</a> in " \
+              "<a href=\"#{project.web_url}\">#{project_name}</a>: " \
+              "<b>#{title}</b>" \
+              "<pre>issue <strong>note</strong></pre>")
+        end
+
+        context 'with confidential issue' do
+          before do
+            issue.update!(confidential: true)
+          end
+
+          it 'calls Hipchat API with issue comment' do
+            data = Gitlab::DataBuilder::Note.build(issue_note, user)
+            hipchat.execute(data)
+
+            message = hipchat.send(:create_message, data)
+
+            expect(message).to include("<pre>issue <strong>note</strong></pre>")
+          end
+        end
+      end
+
+      context 'when snippet comment event triggered' do
+        let(:snippet) { create(:project_snippet, project: project) }
+        let(:snippet_note) do
+          create(:note_on_project_snippet, noteable: snippet,
+                                           project: project,
+                                           note: "snippet note")
+        end
+
+        it "calls Hipchat API for snippet comment events" do
+          data = Gitlab::DataBuilder::Note.build(snippet_note, user)
+          hipchat.execute(data)
+
+          expect(WebMock).to have_requested(:post, api_url).once
+
+          message = hipchat.send(:create_message, data)
+
+          obj_attr = data[:object_attributes]
+          snippet_id = data[:snippet]['id']
+          title = data[:snippet]['title']
+
+          expect(message).to eq("#{user.name} commented on " \
+              "<a href=\"#{obj_attr[:url]}\">snippet ##{snippet_id}</a> in " \
+              "<a href=\"#{project.web_url}\">#{project_name}</a>: " \
+              "<b>#{title}</b>" \
+              "<pre>snippet note</pre>")
+        end
+      end
+    end
+
+    context 'pipeline events' do
+      let(:pipeline) { create(:ci_empty_pipeline, user: create(:user)) }
+      let(:data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
+
+      context 'for failed' do
+        before do
+          pipeline.drop
+        end
+
+        it "calls Hipchat API" do
+          hipchat.execute(data)
+
+          expect(WebMock).to have_requested(:post, api_url).once
+        end
+
+        it "creates a build message" do
+          message = hipchat.__send__(:create_pipeline_message, data)
+
+          project_url = project.web_url
+          project_name = project.full_name.gsub(/\s/, '')
+          pipeline_attributes = data[:object_attributes]
+          ref = pipeline_attributes[:ref]
+          ref_type = pipeline_attributes[:tag] ? 'tag' : 'branch'
+          duration = pipeline_attributes[:duration]
+          user_name = data[:user][:name]
+
+          expect(message).to eq("<a href=\"#{project_url}\">#{project_name}</a>: " \
+            "Pipeline <a href=\"#{project_url}/pipelines/#{pipeline.id}\">##{pipeline.id}</a> " \
+            "of <a href=\"#{project_url}/commits/#{ref}\">#{ref}</a> #{ref_type} " \
+            "by #{user_name} failed in #{duration} second(s)")
+        end
+      end
+
+      context 'for succeeded' do
+        before do
+          pipeline.succeed
+        end
+
+        it "calls Hipchat API" do
+          hipchat.notify_only_broken_pipelines = false
+          hipchat.execute(data)
+          expect(WebMock).to have_requested(:post, api_url).once
+        end
+
+        it "notifies only broken" do
+          hipchat.notify_only_broken_pipelines = true
+          hipchat.execute(data)
+          expect(WebMock).not_to have_requested(:post, api_url).once
+        end
+      end
+    end
+
+    context "#message_options" do
+      it "is set to the defaults" do
+        expect(hipchat.__send__(:message_options)).to eq({ notify: false, color: 'yellow' })
+      end
+
+      it "sets notify to true" do
+        allow(hipchat).to receive(:notify).and_return('1')
+
+        expect(hipchat.__send__(:message_options)).to eq({ notify: true, color: 'yellow' })
+      end
+
+      it "sets the color" do
+        allow(hipchat).to receive(:color).and_return('red')
+
+        expect(hipchat.__send__(:message_options)).to eq({ notify: false, color: 'red' })
+      end
+
+      context 'with a successful build' do
+        it 'uses the green color' do
+          data = { object_kind: 'pipeline',
+                   object_attributes: { status: 'success' } }
+
+          expect(hipchat.__send__(:message_options, data)).to eq({ notify: false, color: 'green' })
+        end
+      end
+
+      context 'with a failed build' do
+        it 'uses the red color' do
+          data = { object_kind: 'pipeline',
+                   object_attributes: { status: 'failed' } }
+
+          expect(hipchat.__send__(:message_options, data)).to eq({ notify: false, color: 'red' })
+        end
+      end
+    end
+  end
+
+  context 'with UrlBlocker' do
+    let(:user)    { create(:user) }
+    let(:project) { create(:project, :repository) }
+    let(:hipchat) { described_class.new(project: project) }
+    let(:push_sample_data) { Gitlab::DataBuilder::Push.build_sample(project, user) }
+
+    describe '#execute' do
+      before do
+        hipchat.server = 'http://localhost:9123'
+      end
+
+      it 'raises UrlBlocker for localhost' do
+        expect(Gitlab::UrlBlocker).to receive(:validate!).and_call_original
+        expect { hipchat.execute(push_sample_data) }.to raise_error(Gitlab::HTTP::BlockedUrlError)
+      end
+    end
+  end
+end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 7f8d2ff91fd5d811fe2bea716fbe80ebf3e56285..9f6a0b53281e413f1504825a4eb0d78d16f66326 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -44,6 +44,7 @@
     it { is_expected.to have_one(:pipelines_email_service) }
     it { is_expected.to have_one(:irker_service) }
     it { is_expected.to have_one(:pivotaltracker_service) }
+    it { is_expected.to have_one(:hipchat_service) }
     it { is_expected.to have_one(:flowdock_service) }
     it { is_expected.to have_one(:assembla_service) }
     it { is_expected.to have_one(:slack_slash_commands_service) }
diff --git a/vendor/licenses.csv b/vendor/licenses.csv
index de6e32cb9986c955a06038f110a3a3f63ee77fd5..0c52cb5a94733c191eff514135984df9c11cb02d 100644
--- a/vendor/licenses.csv
+++ b/vendor/licenses.csv
@@ -520,6 +520,7 @@ hashie-forbidden_attributes,0.1.1,MIT
 he,1.1.1,MIT
 health_check,2.6.0,MIT
 highlight.js,9.13.1,New BSD
+hipchat,1.5.2,MIT
 hmac-drbg,1.0.1,MIT
 hoopy,0.1.4,MIT
 html-pipeline,2.8.4,MIT