diff --git a/hc/api/models.py b/hc/api/models.py
index bb528f38..4b69ad85 100644
--- a/hc/api/models.py
+++ b/hc/api/models.py
@@ -35,7 +35,8 @@ CHANNEL_KINDS = (("email", "Email"),
                  ("pushbullet", "Pushbullet"),
                  ("opsgenie", "OpsGenie"),
                  ("victorops", "VictorOps"),
-                 ("discord", "Discord"))
+                 ("discord", "Discord"),
+                 ("telegram", "Telegram"))
 
 PO_PRIORITIES = {
     -2: "lowest",
@@ -262,6 +263,8 @@ class Channel(models.Model):
             return transports.OpsGenie(self)
         elif self.kind == "discord":
             return transports.Discord(self)
+        elif self.kind == "telegram":
+            return transports.Telegram(self)
         else:
             raise NotImplementedError("Unknown channel kind: %s" % self.kind)
 
@@ -348,6 +351,24 @@ class Channel(models.Model):
         doc = json.loads(self.value)
         return doc["webhook"]["id"]
 
+    @property
+    def telegram_id(self):
+        assert self.kind == "telegram"
+        doc = json.loads(self.value)
+        return doc.get("id")
+
+    @property
+    def telegram_type(self):
+        assert self.kind == "telegram"
+        doc = json.loads(self.value)
+        return doc.get("type")
+
+    @property
+    def telegram_name(self):
+        assert self.kind == "telegram"
+        doc = json.loads(self.value)
+        return doc.get("name")
+
     def latest_notification(self):
         return Notification.objects.filter(channel=self).latest()
 
diff --git a/hc/api/tests/test_notify.py b/hc/api/tests/test_notify.py
index 9509f46a..3e9c9c87 100644
--- a/hc/api/tests/test_notify.py
+++ b/hc/api/tests/test_notify.py
@@ -1,7 +1,6 @@
 import json
 
 from django.core import mail
-from django.test import override_settings
 from hc.api.models import Channel, Check, Notification
 from hc.test import BaseTestCase
 from mock import patch
@@ -282,3 +281,17 @@ class NotifyTestCase(BaseTestCase):
         _, kwargs = mock_post.call_args
         self.assertEqual(kwargs["json"]["type"], "note")
         self.assertEqual(kwargs["headers"]["Access-Token"], "fake-token")
+
+    @patch("hc.api.transports.requests.request")
+    def test_telegram(self, mock_post):
+        v = json.dumps({"id": 123})
+        self._setup_data("telegram", v)
+        mock_post.return_value.status_code = 200
+
+        self.channel.notify(self.check)
+        assert Notification.objects.count() == 1
+
+        args, kwargs = mock_post.call_args
+        payload = kwargs["json"]
+        self.assertEqual(payload["chat_id"], 123)
+        self.assertTrue("The check" in payload["text"])
diff --git a/hc/api/transports.py b/hc/api/transports.py
index 0612b845..b32ef980 100644
--- a/hc/api/transports.py
+++ b/hc/api/transports.py
@@ -61,7 +61,8 @@ class Email(Transport):
 
 class HttpTransport(Transport):
 
-    def _request(self, method, url, **kwargs):
+    @classmethod
+    def _request(cls, method, url, **kwargs):
         try:
             options = dict(kwargs)
             if "headers" not in options:
@@ -79,19 +80,21 @@ class HttpTransport(Transport):
         except requests.exceptions.ConnectionError:
             return "Connection failed"
 
-    def get(self, url):
+    @classmethod
+    def get(cls, url):
         # Make 3 attempts--
         for x in range(0, 3):
-            error = self._request("get", url)
+            error = cls._request("get", url)
             if error is None:
                 break
 
         return error
 
-    def post(self, url, **kwargs):
+    @classmethod
+    def post(cls, url, **kwargs):
         # Make 3 attempts--
         for x in range(0, 3):
-            error = self._request("post", url, **kwargs)
+            error = cls._request("post", url, **kwargs)
             if error is None:
                 break
 
@@ -277,3 +280,19 @@ class Discord(HttpTransport):
         payload = json.loads(text)
         url = self.channel.discord_webhook_url + "/slack"
         return self.post(url, json=payload)
+
+
+class Telegram(HttpTransport):
+    SM = "https://api.telegram.org/bot%s/sendMessage" % settings.TELEGRAM_TOKEN
+
+    @classmethod
+    def send(cls, chat_id, text):
+        return cls.post(cls.SM, json={
+            "chat_id": chat_id,
+            "text": text,
+            "parse_mode": "html"
+        })
+
+    def notify(self, check):
+        text = tmpl("telegram_message.html", check=check)
+        return self.send(self.channel.telegram_id, text)
diff --git a/hc/front/schemas.py b/hc/front/schemas.py
new file mode 100644
index 00000000..61b0a1c5
--- /dev/null
+++ b/hc/front/schemas.py
@@ -0,0 +1,23 @@
+telegram_callback = {
+    "type": "object",
+    "properties": {
+        "message": {
+            "type": "object",
+            "properties": {
+                "chat": {
+                    "type": "object",
+                    "properties": {
+                        "id": {"type": "number"},
+                        "type": {"enum": ["group", "private"]},
+                        "title": {"type": "string"},
+                        "username": {"type": "string"}
+                    },
+                    "required": ["id", "type"]
+                },
+                "text": {"type": "string"}
+            },
+            "required": ["chat", "text"]
+        }
+    },
+    "required": ["message"]
+}
diff --git a/hc/front/templatetags/hc_extras.py b/hc/front/templatetags/hc_extras.py
index 4a61d181..14a54bc5 100644
--- a/hc/front/templatetags/hc_extras.py
+++ b/hc/front/templatetags/hc_extras.py
@@ -12,11 +12,6 @@ def hc_duration(td):
     return format_duration(td)
 
 
-@register.simple_tag
-def settings_value(name):
-    return getattr(settings, name, "")
-
-
 @register.simple_tag
 def site_name():
     return settings.SITE_NAME
diff --git a/hc/front/tests/test_add_telegram.py b/hc/front/tests/test_add_telegram.py
new file mode 100644
index 00000000..dd79529b
--- /dev/null
+++ b/hc/front/tests/test_add_telegram.py
@@ -0,0 +1,76 @@
+import json
+
+from django.core import signing
+from hc.api.models import Channel
+from hc.test import BaseTestCase
+from mock import patch
+
+
+class AddTelegramTestCase(BaseTestCase):
+    url = "/integrations/add_telegram/"
+
+    def test_instructions_work(self):
+        self.client.login(username="alice@example.org", password="password")
+        r = self.client.get(self.url)
+        self.assertContains(r, "start@HealthchecksBot")
+
+    def test_it_shows_confirmation(self):
+        payload = signing.dumps((123, "group", "My Group"))
+
+        self.client.login(username="alice@example.org", password="password")
+        r = self.client.get(self.url + "?" + payload)
+        self.assertContains(r, "My Group")
+
+    def test_it_works(self):
+        payload = signing.dumps((123, "group", "My Group"))
+
+        self.client.login(username="alice@example.org", password="password")
+        r = self.client.post(self.url + "?" + payload, {})
+        self.assertRedirects(r, "/integrations/")
+
+        c = Channel.objects.get()
+        self.assertEqual(c.kind, "telegram")
+        self.assertEqual(c.telegram_id, 123)
+        self.assertEqual(c.telegram_type, "group")
+        self.assertEqual(c.telegram_name, "My Group")
+
+    @patch("hc.api.transports.requests.request")
+    def test_it_sends_invite(self, mock_get):
+        data = {
+            "message": {
+                "chat": {
+                    "id": 123,
+                    "title": "My Group",
+                    "type": "group"
+                },
+                "text": "/start"
+            }
+        }
+        r = self.client.post("/integrations/telegram/bot/", json.dumps(data),
+                             content_type="application/json")
+
+        self.assertEqual(r.status_code, 200)
+        self.assertTrue(mock_get.called)
+
+    @patch("hc.api.transports.requests.request")
+    def test_bot_handles_bad_message(self, mock_get):
+        samples = ["", "{}"]
+
+        # text is missing
+        samples.append(json.dumps({
+            "message": {"chat": {"id": 123, "type": "group"}}
+        }))
+
+        # bad type
+        samples.append(json.dumps({
+            "message": {
+                "chat": {"id": 123, "type": "invalid"},
+                "text": "/start"
+            }
+        }))
+
+        for sample in samples:
+            r = self.client.post("/integrations/telegram/bot/", sample,
+                                 content_type="application/json")
+
+            self.assertEqual(r.status_code, 400)
diff --git a/hc/front/urls.py b/hc/front/urls.py
index 609a266b..a6ce7df1 100644
--- a/hc/front/urls.py
+++ b/hc/front/urls.py
@@ -24,6 +24,8 @@ channel_urls = [
     url(r'^add_pushover/$', views.add_pushover, name="hc-add-pushover"),
     url(r'^add_opsgenie/$', views.add_opsgenie, name="hc-add-opsgenie"),
     url(r'^add_victorops/$', views.add_victorops, name="hc-add-victorops"),
+    url(r'^telegram/bot/$', views.telegram_bot),
+    url(r'^add_telegram/$', views.add_telegram, name="hc-add-telegram"),
     url(r'^([\w-]+)/checks/$', views.channel_checks, name="hc-channel-checks"),
     url(r'^([\w-]+)/remove/$', views.remove_channel, name="hc-remove-channel"),
     url(r'^([\w-]+)/verify/([\w-]+)/$', views.verify_email,
diff --git a/hc/front/views.py b/hc/front/views.py
index 59368afe..1b2a428f 100644
--- a/hc/front/views.py
+++ b/hc/front/views.py
@@ -1,29 +1,36 @@
 from collections import Counter
-from croniter import croniter
 from datetime import datetime, timedelta as td
 from itertools import tee
+import json
 
-import requests
+from croniter import croniter
 from django.conf import settings
 from django.contrib import messages
 from django.contrib.auth.decorators import login_required
+from django.core import signing
 from django.db.models import Count
-from django.http import Http404, HttpResponseBadRequest, HttpResponseForbidden
+from django.http import (Http404, HttpResponse, HttpResponseBadRequest,
+                         HttpResponseForbidden)
 from django.shortcuts import get_object_or_404, redirect, render
+from django.template.loader import render_to_string
 from django.urls import reverse
 from django.utils import timezone
 from django.utils.crypto import get_random_string
+from django.utils.six.moves.urllib.parse import urlencode
 from django.views.decorators.csrf import csrf_exempt
 from django.views.decorators.http import require_POST
-from django.utils.six.moves.urllib.parse import urlencode
 from hc.api.decorators import uuid_or_400
 from hc.api.models import (DEFAULT_GRACE, DEFAULT_TIMEOUT, Channel, Check,
                            Ping, Notification)
+from hc.api.transports import Telegram
 from hc.front.forms import (AddWebhookForm, NameTagsForm,
                             TimeoutForm, AddUrlForm, AddPdForm, AddEmailForm,
                             AddOpsGenieForm, CronForm)
+from hc.front.schemas import telegram_callback
+from hc.lib import jsonschema
 from pytz import all_timezones
 from pytz.exceptions import UnknownTimeZoneError
+import requests
 
 
 # from itertools recipes:
@@ -341,7 +348,8 @@ def channels(request):
         "num_checks": num_checks,
         "enable_pushbullet": settings.PUSHBULLET_CLIENT_ID is not None,
         "enable_pushover": settings.PUSHOVER_API_TOKEN is not None,
-        "enable_discord": settings.DISCORD_CLIENT_ID is not None
+        "enable_discord": settings.DISCORD_CLIENT_ID is not None,
+        "enable_telegram": settings.TELEGRAM_TOKEN is not None
     }
     return render(request, "front/channels.html", ctx)
 
@@ -747,6 +755,60 @@ def add_victorops(request):
     return render(request, "integrations/add_victorops.html", ctx)
 
 
+@csrf_exempt
+@require_POST
+def telegram_bot(request):
+    try:
+        doc = json.loads(request.body.decode("utf-8"))
+        jsonschema.validate(doc, telegram_callback)
+    except json.decoder.JSONDecodeError:
+        return HttpResponseBadRequest()
+    except jsonschema.ValidationError:
+        return HttpResponseBadRequest()
+
+    if "/start" not in doc["message"]["text"]:
+        return HttpResponse()
+
+    chat = doc["message"]["chat"]
+    name = max(chat.get("title", ""), chat.get("username", ""))
+
+    invite = render_to_string("integrations/telegram_invite.html", {
+        "qs": signing.dumps((chat["id"], chat["type"], name))
+    })
+
+    Telegram.send(chat["id"], invite)
+    return HttpResponse()
+
+
+@login_required
+def add_telegram(request):
+    chat_id, chat_type, chat_name = None, None, None
+    qs = request.META["QUERY_STRING"]
+    if qs:
+        chat_id, chat_type, chat_name = signing.loads(qs, max_age=600)
+
+    if request.method == "POST":
+        channel = Channel(user=request.team.user, kind="telegram")
+        channel.value = json.dumps({
+            "id": chat_id,
+            "type": chat_type,
+            "name": chat_name
+        })
+        channel.save()
+
+        channel.assign_all_checks()
+        messages.success(request, "The Telegram integration has been added!")
+        return redirect("hc-channels")
+
+    ctx = {
+        "chat_id": chat_id,
+        "chat_type": chat_type,
+        "chat_name": chat_name
+    }
+
+    return render(request, "integrations/add_telegram.html", ctx)
+
+
 def privacy(request):
     return render(request, "front/privacy.html", {})
 
diff --git a/hc/settings.py b/hc/settings.py
index 94bfae5a..9bd00a4b 100644
--- a/hc/settings.py
+++ b/hc/settings.py
@@ -153,6 +153,9 @@ PUSHOVER_EMERGENCY_EXPIRATION = 86400
 PUSHBULLET_CLIENT_ID = None
 PUSHBULLET_CLIENT_SECRET = None
 
+# Telegram integration -- override in local_settings.py
+TELEGRAM_TOKEN = None
+
 if os.path.exists(os.path.join(BASE_DIR, "hc/local_settings.py")):
     from .local_settings import *
 else:
diff --git a/static/img/integrations/setup_telegram_1.png b/static/img/integrations/setup_telegram_1.png
new file mode 100644
index 00000000..ac73d922
Binary files /dev/null and b/static/img/integrations/setup_telegram_1.png differ
diff --git a/static/img/integrations/setup_telegram_2.png b/static/img/integrations/setup_telegram_2.png
new file mode 100644
index 00000000..d9cff510
Binary files /dev/null and b/static/img/integrations/setup_telegram_2.png differ
diff --git a/static/img/integrations/setup_telegram_3.png b/static/img/integrations/setup_telegram_3.png
new file mode 100644
index 00000000..f334d4b9
Binary files /dev/null and b/static/img/integrations/setup_telegram_3.png differ
diff --git a/static/img/integrations/telegram.png b/static/img/integrations/telegram.png
new file mode 100644
index 00000000..617d04c3
Binary files /dev/null and b/static/img/integrations/telegram.png differ
diff --git a/static/img/integrations/victorops.png b/static/img/integrations/victorops.png
index a219c15e..461204fa 100644
Binary files a/static/img/integrations/victorops.png and b/static/img/integrations/victorops.png differ
diff --git a/templates/front/channels.html b/templates/front/channels.html
index 2656b4d5..dd1c0425 100644
--- a/templates/front/channels.html
+++ b/templates/front/channels.html
@@ -82,6 +82,13 @@
                     {{ ch.value }}
                 {% elif ch.kind == "discord" %}
                     {{ ch.discord_webhook_id }}
+                {% elif ch.kind == "telegram" %}
+                    {% if ch.telegram_type == "group" %}
+                    <span class="preposition">chat</span>
+                    {% elif ch.telegram_type == "private" %}
+                    <span class="preposition">user</span>
+                    {% endif %}
+                    {{ ch.telegram_name }}
                 {% else %}
                     {{ ch.value }}
                 {% endif %}
@@ -223,6 +230,17 @@
             <a href="{% url 'hc-add-discord' %}" class="btn btn-primary">Add Integration</a>
         </li>
         {% endif %}
+        {% if enable_telegram %}
+        <li>
+            <img src="{% static 'img/integrations/telegram.png' %}"
+                 class="icon" alt="Telegram icon" />
+
+            <h2>Telegram</h2>
+            <p>A messaging app with a focus on speed and security.</p>
+
+            <a href="{% url 'hc-add-telegram' %}" class="btn btn-primary">Add Integration</a>
+        </li>
+        {% endif %}
         <li class="link-to-github">
             <img src="{% static 'img/integrations/missing.png' %}"
                  class="icon" alt="Suggest New Integration" />
diff --git a/templates/integrations/add_telegram.html b/templates/integrations/add_telegram.html
new file mode 100644
index 00000000..00317d62
--- /dev/null
+++ b/templates/integrations/add_telegram.html
@@ -0,0 +1,104 @@
+{% extends "base.html" %}
+{% load compress humanize staticfiles hc_extras %}
+
+{% block title %}Notification Channels - {% site_name %}{% endblock %}
+
+
+{% block content %}
+<div class="row">
+<div class="col-sm-12">
+    <h1>Telegram</h1>
+
+    {% if chat_id %}
+    <div class="jumbotron">
+        <p>
+            When a check goes <strong>up</strong> or <strong>down</strong>,
+            healthchecks.io will send notifications to
+            {% if chat_type == "private" %}
+                a Telegram user
+            {% else %}
+                a Telegram chat
+            {% endif %}
+            named <strong>{{ chat_name }}</strong>. Sound good?
+        </p>
+
+        <form method="post" class="text-center">
+            {% csrf_token %}
+
+            <button type="submit" class="btn btn-default">
+                <img class="ai-icon" src="{% static 'img/integrations/telegram.png' %}" alt="Discord" />
+                Yes, connect Telegram
+            </button>
+        </form>
+    </div>
+    {% else %}
+    <p>If your team uses <a href="https://telegram.org/">Telegram</a>,
+    you can set up {% site_name %} to post status updates directly to an
+    appropriate Telegram chat or user.</p>
+
+    <h2>Setup Guide</h2>
+    <div class="row ai-step">
+        <div class="col-sm-6">
+            <span class="step-no">1</span>
+            <p>
+                From your Telegram client, invite
+                <strong>HealthchecksBot</strong> to a group. It will get added
+                as a member with no access to group messages.
+            </p>
+            <p>
+                Alternatively, if you want notifications sent to yourself
+                directly, start a conversation with
+                <strong>HealthchecksBot</strong>.
+            </p>
+        </div>
+        <div class="col-sm-6">
+            <img
+                class="ai-guide-screenshot"
+                alt="Screenshot"
+                src="{% static 'img/integrations/setup_telegram_1.png' %}">
+        </div>
+    </div>
+    <div class="row ai-step">
+        <div class="col-sm-6">
+            <span class="step-no">2</span>
+            <p>Type <strong><code>/start</code></strong> command.
+               If there are multiple bots in the group, type
+               <strong><code>/start@HealthchecksBot</code></strong> instead.
+           </p>
+            <p>The bot will respond with a confirmation link.</p>
+        </div>
+        <div class="col-sm-6">
+            <img
+                class="ai-guide-screenshot"
+                alt="Screenshot"
+                src="{% static 'img/integrations/setup_telegram_2.png' %}">
+        </div>    </div>
+
+    <div class="row ai-step">
+        <div class="col-sm-6">
+            <span class="step-no">3</span>
+            <p>Click or tap on the confirmation link, and
+            {% site_name %} will open in a browser window asking you to
+             confirm the new integration.</p>
+            <p>Confirm the integration, and it's done!</p>
+        </div>
+        <div class="col-sm-6">
+            <img
+                class="ai-guide-screenshot"
+                alt="Screenshot"
+                src="{% static 'img/integrations/setup_telegram_3.png' %}">
+        </div>
+    </div>
+
+
+    {% endif %}
+</div>
+</div>
+{% endblock %}
+
+{% block scripts %}
+{% compress js %}
+<script src="{% static 'js/jquery-2.1.4.min.js' %}"></script>
+<script src="{% static 'js/bootstrap.min.js' %}"></script>
+{% endcompress %}
+{% endblock %}
diff --git a/templates/integrations/telegram_invite.html b/templates/integrations/telegram_invite.html
new file mode 100644
index 00000000..74da28cf
--- /dev/null
+++ b/templates/integrations/telegram_invite.html
@@ -0,0 +1,5 @@
+{% load hc_extras %}
+
+Please open this link to complete the {% site_name %} integration:
+
+{% site_root %}{% url "hc-add-telegram" %}?{{ qs }}
diff --git a/templates/integrations/telegram_message.html b/templates/integrations/telegram_message.html
new file mode 100644
index 00000000..74f787ec
--- /dev/null
+++ b/templates/integrations/telegram_message.html
@@ -0,0 +1,6 @@
+{% load humanize %}
+{% if check.status == "down" %}
+The check "{{ check.name_then_code }}" is <b>DOWN</b>. Last ping was {{ check.last_ping|naturaltime }}.
+{% else %}
+The check "{{ check.name_then_code }}" received a ping and is now <b>UP</b>.
+{% endif %}