diff --git a/lms/djangoapps/discussion_api/tests/test_views.py b/lms/djangoapps/discussion_api/tests/test_views.py
index 4a7f56ebef9fd23079a10179f6153dbbc3af8d02..5695b6b38998c0b1490505e1777b3efc8501f5d0 100644
--- a/lms/djangoapps/discussion_api/tests/test_views.py
+++ b/lms/djangoapps/discussion_api/tests/test_views.py
@@ -15,6 +15,7 @@ from nose.plugins.attrib import attr
 from pytz import UTC
 from rest_framework.parsers import JSONParser
 from rest_framework.test import APIClient
+from six import text_type
 
 from common.test.utils import disable_signal
 from discussion_api import api
@@ -27,7 +28,9 @@ from discussion_api.tests.utils import (
 )
 from django_comment_client.tests.utils import ForumsEnableMixin
 from openedx.core.djangoapps.user_api.accounts.image_helpers import get_profile_image_storage
-from student.tests.factories import CourseEnrollmentFactory, UserFactory
+from openedx.core.lib.token_utils import JwtBuilder
+from student.models import get_retired_username_by_username
+from student.tests.factories import CourseEnrollmentFactory, UserFactory, SuperuserFactory
 from util.testing import PatchMediaTypeMixin, UrlResetMixin
 from xmodule.modulestore import ModuleStoreEnum
 from xmodule.modulestore.django import modulestore
@@ -77,7 +80,7 @@ class DiscussionAPIViewTestMixin(ForumsEnableMixin, CommentsServiceMockMixin, Ur
         """
         cs_thread = make_minimal_cs_thread({
             "id": "test_thread",
-            "course_id": unicode(self.course.id),
+            "course_id": text_type(self.course.id),
             "commentable_id": "test_topic",
             "username": self.user.username,
             "user_id": str(self.user.id),
@@ -95,7 +98,7 @@ class DiscussionAPIViewTestMixin(ForumsEnableMixin, CommentsServiceMockMixin, Ur
         """
         cs_comment = make_minimal_cs_comment({
             "id": "test_comment",
-            "course_id": unicode(self.course.id),
+            "course_id": text_type(self.course.id),
             "thread_id": "test_thread",
             "username": self.user.username,
             "user_id": str(self.user.id),
@@ -125,7 +128,7 @@ class CourseViewTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
     """Tests for CourseView"""
     def setUp(self):
         super(CourseViewTest, self).setUp()
-        self.url = reverse("discussion_course", kwargs={"course_id": unicode(self.course.id)})
+        self.url = reverse("discussion_course", kwargs={"course_id": text_type(self.course.id)})
 
     def test_404(self):
         response = self.client.get(
@@ -143,7 +146,7 @@ class CourseViewTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
             response,
             200,
             {
-                "id": unicode(self.course.id),
+                "id": text_type(self.course.id),
                 "blackouts": [],
                 "thread_list_url": "http://testserver/api/discussion/v1/threads/?course_id=x%2Fy%2Fz",
                 "following_thread_list_url": (
@@ -154,6 +157,84 @@ class CourseViewTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
         )
 
 
+@httpretty.activate
+@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
+class RetireViewTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
+    """Tests for CourseView"""
+    def setUp(self):
+        super(RetireViewTest, self).setUp()
+        self.superuser = SuperuserFactory()
+        self.retired_username = get_retired_username_by_username(self.user.username)
+        self.url = reverse(
+            "retire_discussion_user",
+            kwargs={"username": text_type(self.user.username)}
+        )
+
+    def assert_response_correct(self, response, expected_status, expected_content):
+        """
+        Assert that the response has the given status code and content
+        """
+        self.assertEqual(response.status_code, expected_status)
+
+        if expected_content:
+            self.assertEqual(text_type(response.content), expected_content)
+
+    def build_jwt_headers(self, user):
+        """
+        Helper function for creating headers for the JWT authentication.
+        """
+        token = JwtBuilder(user).build_token([])
+        headers = {'HTTP_AUTHORIZATION': 'JWT ' + token}
+        return headers
+
+    def test_basic(self):
+        """
+        Check successful retirement case
+        """
+        self.register_get_user_retire_response(self.user)
+        headers = self.build_jwt_headers(self.superuser)
+        response = self.client.post(self.url, {'retired_username': self.retired_username}, **headers)
+        self.assert_response_correct(response, 204, "")
+
+    def test_bad_hash(self):
+        """
+        Check that we fail on a hash mismatch with an appropriate error
+        """
+        headers = self.build_jwt_headers(self.superuser)
+        response = self.client.post(self.url, {'retired_username': "this will never match"}, **headers)
+        self.assert_response_correct(response, 500, '"Mismatched hashed_username, bad salt?"')
+
+    def test_downstream_forums_error(self):
+        """
+        Check that we bubble up errors from the comments service
+        """
+        self.register_get_user_retire_response(self.user, status=500, body="Server error")
+        headers = self.build_jwt_headers(self.superuser)
+        response = self.client.post(self.url, {'retired_username': self.retired_username}, **headers)
+        self.assert_response_correct(response, 500, '"Server error"')
+
+    def test_nonexistent_user(self):
+        """
+        Check that we handle unknown users appropriately
+        """
+        nonexistent_username = "nonexistent user"
+        self.url = reverse(
+            "retire_discussion_user",
+            kwargs={"username": nonexistent_username}
+        )
+        self.retired_username = get_retired_username_by_username(nonexistent_username)
+
+        headers = self.build_jwt_headers(self.superuser)
+        response = self.client.post(self.url, {'retired_username': self.retired_username}, **headers)
+        self.assert_response_correct(response, 404, None)
+
+    def test_not_authenticated(self):
+        """
+        Override the parent implementation of this, we auth differently
+        """
+        pass
+
+
 @ddt.ddt
 @mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
 class CourseTopicsViewTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
@@ -162,7 +243,7 @@ class CourseTopicsViewTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
     """
     def setUp(self):
         super(CourseTopicsViewTest, self).setUp()
-        self.url = reverse("course_topics", kwargs={"course_id": unicode(self.course.id)})
+        self.url = reverse("course_topics", kwargs={"course_id": text_type(self.course.id)})
 
     def create_course(self, modules_count, module_store, topics):
         """
@@ -177,7 +258,7 @@ class CourseTopicsViewTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
             discussion_topics=topics
         )
         CourseEnrollmentFactory.create(user=self.user, course_id=course.id)
-        course_url = reverse("course_topics", kwargs={"course_id": unicode(course.id)})
+        course_url = reverse("course_topics", kwargs={"course_id": text_type(course.id)})
         # add some discussion xblocks
         for i in range(modules_count):
             ItemFactory.create(
@@ -325,7 +406,7 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
         """
         thread = make_minimal_cs_thread({
             "id": "test_thread",
-            "course_id": unicode(self.course.id),
+            "course_id": text_type(self.course.id),
             "commentable_id": "test_topic",
             "user_id": str(self.user.id),
             "username": self.user.username,
@@ -350,7 +431,7 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
         )
 
     def test_404(self):
-        response = self.client.get(self.url, {"course_id": unicode("non/existent/course")})
+        response = self.client.get(self.url, {"course_id": text_type("non/existent/course")})
         self.assert_response_correct(
             response,
             404,
@@ -373,7 +454,7 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
             "editable_fields": ["abuse_flagged", "following", "read", "voted"],
         })]
         self.register_get_threads_response(source_threads, page=1, num_pages=2)
-        response = self.client.get(self.url, {"course_id": unicode(self.course.id), "following": ""})
+        response = self.client.get(self.url, {"course_id": text_type(self.course.id), "following": ""})
         expected_response = make_paginated_api_response(
             results=expected_threads,
             count=1,
@@ -388,8 +469,8 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
             expected_response
         )
         self.assert_last_query_params({
-            "user_id": [unicode(self.user.id)],
-            "course_id": [unicode(self.course.id)],
+            "user_id": [text_type(self.user.id)],
+            "course_id": [text_type(self.course.id)],
             "sort_key": ["activity"],
             "page": ["1"],
             "per_page": ["10"],
@@ -403,13 +484,13 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
         self.client.get(
             self.url,
             {
-                "course_id": unicode(self.course.id),
+                "course_id": text_type(self.course.id),
                 "view": query,
             }
         )
         self.assert_last_query_params({
-            "user_id": [unicode(self.user.id)],
-            "course_id": [unicode(self.course.id)],
+            "user_id": [text_type(self.user.id)],
+            "course_id": [text_type(self.course.id)],
             "sort_key": ["activity"],
             "page": ["1"],
             "per_page": ["10"],
@@ -421,7 +502,7 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
         self.register_get_threads_response([], page=1, num_pages=1)
         response = self.client.get(
             self.url,
-            {"course_id": unicode(self.course.id), "page": "18", "page_size": "4"}
+            {"course_id": text_type(self.course.id), "page": "18", "page_size": "4"}
         )
         self.assert_response_correct(
             response,
@@ -429,8 +510,8 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
             {"developer_message": "Page not found (No results on this page)."}
         )
         self.assert_last_query_params({
-            "user_id": [unicode(self.user.id)],
-            "course_id": [unicode(self.course.id)],
+            "user_id": [text_type(self.user.id)],
+            "course_id": [text_type(self.course.id)],
             "sort_key": ["activity"],
             "page": ["18"],
             "per_page": ["4"],
@@ -441,7 +522,7 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
         self.register_get_threads_search_response([], None, num_pages=0)
         response = self.client.get(
             self.url,
-            {"course_id": unicode(self.course.id), "text_search": "test search string"}
+            {"course_id": text_type(self.course.id), "text_search": "test search string"}
         )
 
         expected_response = make_paginated_api_response(
@@ -454,8 +535,8 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
             expected_response
         )
         self.assert_last_query_params({
-            "user_id": [unicode(self.user.id)],
-            "course_id": [unicode(self.course.id)],
+            "user_id": [text_type(self.user.id)],
+            "course_id": [text_type(self.course.id)],
             "sort_key": ["activity"],
             "page": ["1"],
             "per_page": ["10"],
@@ -469,7 +550,7 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
         response = self.client.get(
             self.url,
             {
-                "course_id": unicode(self.course.id),
+                "course_id": text_type(self.course.id),
                 "following": following,
             }
         )
@@ -493,7 +574,7 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
         response = self.client.get(
             self.url,
             {
-                "course_id": unicode(self.course.id),
+                "course_id": text_type(self.course.id),
                 "following": following,
             }
         )
@@ -509,7 +590,7 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
         response = self.client.get(
             self.url,
             {
-                "course_id": unicode(self.course.id),
+                "course_id": text_type(self.course.id),
                 "following": "invalid-boolean",
             }
         )
@@ -541,13 +622,13 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
         self.client.get(
             self.url,
             {
-                "course_id": unicode(self.course.id),
+                "course_id": text_type(self.course.id),
                 "order_by": http_query,
             }
         )
         self.assert_last_query_params({
-            "user_id": [unicode(self.user.id)],
-            "course_id": [unicode(self.course.id)],
+            "user_id": [text_type(self.user.id)],
+            "course_id": [text_type(self.course.id)],
             "page": ["1"],
             "per_page": ["10"],
             "sort_key": [cc_query],
@@ -564,13 +645,13 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
         self.client.get(
             self.url,
             {
-                "course_id": unicode(self.course.id),
+                "course_id": text_type(self.course.id),
                 "order_direction": "desc",
             }
         )
         self.assert_last_query_params({
-            "user_id": [unicode(self.user.id)],
-            "course_id": [unicode(self.course.id)],
+            "user_id": [text_type(self.user.id)],
+            "course_id": [text_type(self.course.id)],
             "sort_key": ["activity"],
             "page": ["1"],
             "per_page": ["10"],
@@ -583,7 +664,7 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
         self.register_get_user_response(self.user)
         self.register_get_threads_search_response([], None, num_pages=0)
         response = self.client.get(self.url, {
-            "course_id": unicode(self.course.id),
+            "course_id": text_type(self.course.id),
             "text_search": "test search string",
             "topic_id": "topic1, topic2",
         })
@@ -616,7 +697,7 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
 
         response = self.client.get(
             self.url,
-            {"course_id": unicode(self.course.id), "requested_fields": "profile_image"},
+            {"course_id": text_type(self.course.id), "requested_fields": "profile_image"},
         )
         self.assertEqual(response.status_code, 200)
         response_threads = json.loads(response.content)['results']
@@ -641,7 +722,7 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
 
         response = self.client.get(
             self.url,
-            {"course_id": unicode(self.course.id), "requested_fields": "profile_image"},
+            {"course_id": text_type(self.course.id), "requested_fields": "profile_image"},
         )
         self.assertEqual(response.status_code, 200)
         response_thread = json.loads(response.content)['results'][0]
@@ -667,7 +748,7 @@ class ThreadViewSetCreateTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
         })
         self.register_post_thread_response(cs_thread)
         request_data = {
-            "course_id": unicode(self.course.id),
+            "course_id": text_type(self.course.id),
             "topic_id": "test_topic",
             "type": "discussion",
             "title": "Test Title",
@@ -684,7 +765,7 @@ class ThreadViewSetCreateTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
         self.assertEqual(
             httpretty.last_request().parsed_body,
             {
-                "course_id": [unicode(self.course.id)],
+                "course_id": [text_type(self.course.id)],
                 "commentable_id": ["test_topic"],
                 "thread_type": ["discussion"],
                 "title": ["Test Title"],
@@ -755,7 +836,7 @@ class ThreadViewSetPartialUpdateTest(DiscussionAPIViewTestMixin, ModuleStoreTest
         self.assertEqual(
             httpretty.last_request().parsed_body,
             {
-                "course_id": [unicode(self.course.id)],
+                "course_id": [text_type(self.course.id)],
                 "commentable_id": ["test_topic"],
                 "thread_type": ["discussion"],
                 "title": ["Test Title"],
@@ -885,7 +966,7 @@ class ThreadViewSetDeleteTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
         self.register_get_user_response(self.user)
         cs_thread = make_minimal_cs_thread({
             "id": self.thread_id,
-            "course_id": unicode(self.course.id),
+            "course_id": text_type(self.course.id),
             "username": self.user.username,
             "user_id": str(self.user.id),
         })
@@ -943,7 +1024,7 @@ class CommentViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pr
         already in overrides.
         """
         overrides = overrides.copy() if overrides else {}
-        overrides.setdefault("course_id", unicode(self.course.id))
+        overrides.setdefault("course_id", text_type(self.course.id))
         return make_minimal_cs_thread(overrides)
 
     def expected_response_comment(self, overrides=None):
@@ -1006,7 +1087,7 @@ class CommentViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pr
         })]
         self.register_get_thread_response({
             "id": self.thread_id,
-            "course_id": unicode(self.course.id),
+            "course_id": text_type(self.course.id),
             "thread_type": "discussion",
             "children": source_comments,
             "resp_total": 100,
@@ -1043,7 +1124,7 @@ class CommentViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pr
         self.register_get_user_response(self.user)
         self.register_get_thread_response(make_minimal_cs_thread({
             "id": self.thread_id,
-            "course_id": unicode(self.course.id),
+            "course_id": text_type(self.course.id),
             "thread_type": "discussion",
             "resp_total": 10,
         }))
@@ -1152,7 +1233,7 @@ class CommentViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pr
         })
         thread = self.make_minimal_cs_thread({
             "id": self.thread_id,
-            "course_id": unicode(self.course.id),
+            "course_id": text_type(self.course.id),
             "thread_type": "discussion",
             "children": [response_1, response_2],
             "resp_total": 2,
@@ -1187,7 +1268,7 @@ class CommentViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pr
         source_comments = [self.create_source_comment()]
         self.register_get_thread_response({
             "id": self.thread_id,
-            "course_id": unicode(self.course.id),
+            "course_id": text_type(self.course.id),
             "thread_type": "discussion",
             "children": source_comments,
             "resp_total": 100,
@@ -1296,7 +1377,7 @@ class CommentViewSetDeleteTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
         self.register_get_user_response(self.user)
         cs_thread = make_minimal_cs_thread({
             "id": "test_thread",
-            "course_id": unicode(self.course.id),
+            "course_id": text_type(self.course.id),
         })
         self.register_get_thread_response(cs_thread)
         cs_comment = make_minimal_cs_comment({
@@ -1376,7 +1457,7 @@ class CommentViewSetCreateTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
         self.assertEqual(
             httpretty.last_request().parsed_body,
             {
-                "course_id": [unicode(self.course.id)],
+                "course_id": [text_type(self.course.id)],
                 "body": ["Test body"],
                 "user_id": [str(self.user.id)],
             }
@@ -1475,7 +1556,7 @@ class CommentViewSetPartialUpdateTest(DiscussionAPIViewTestMixin, ModuleStoreTes
             httpretty.last_request().parsed_body,
             {
                 "body": ["Edited body"],
-                "course_id": [unicode(self.course.id)],
+                "course_id": [text_type(self.course.id)],
                 "user_id": [str(self.user.id)],
                 "anonymous": ["False"],
                 "anonymous_to_peers": ["False"],
@@ -1543,7 +1624,7 @@ class ThreadViewSetRetrieveTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase,
         self.register_get_user_response(self.user)
         cs_thread = make_minimal_cs_thread({
             "id": self.thread_id,
-            "course_id": unicode(self.course.id),
+            "course_id": text_type(self.course.id),
             "commentable_id": "test_topic",
             "username": self.user.username,
             "user_id": str(self.user.id),
@@ -1568,7 +1649,7 @@ class ThreadViewSetRetrieveTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase,
         self.register_get_user_response(self.user)
         cs_thread = make_minimal_cs_thread({
             "id": self.thread_id,
-            "course_id": unicode(self.course.id),
+            "course_id": text_type(self.course.id),
             "username": self.user.username,
             "user_id": str(self.user.id),
         })
@@ -1598,7 +1679,7 @@ class CommentViewSetRetrieveTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase
         return make_minimal_cs_comment({
             "id": comment_id,
             "parent_id": parent_id,
-            "course_id": unicode(self.course.id),
+            "course_id": text_type(self.course.id),
             "thread_id": self.thread_id,
             "thread_type": "discussion",
             "username": self.user.username,
@@ -1615,7 +1696,7 @@ class CommentViewSetRetrieveTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase
         cs_comment = self.make_comment_data(self.comment_id, None, [cs_comment_child])
         cs_thread = make_minimal_cs_thread({
             "id": self.thread_id,
-            "course_id": unicode(self.course.id),
+            "course_id": text_type(self.course.id),
             "children": [cs_comment],
         })
         self.register_get_thread_response(cs_thread)
@@ -1663,7 +1744,7 @@ class CommentViewSetRetrieveTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase
         cs_comment = self.make_comment_data(self.comment_id, None, [cs_comment_child])
         cs_thread = make_minimal_cs_thread({
             "id": self.thread_id,
-            "course_id": unicode(self.course.id),
+            "course_id": text_type(self.course.id),
             "children": [cs_comment],
         })
         self.register_get_thread_response(cs_thread)
@@ -1687,7 +1768,7 @@ class CommentViewSetRetrieveTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase
         cs_comment = self.make_comment_data(self.comment_id, None, [cs_comment_child])
         cs_thread = make_minimal_cs_thread({
             'id': self.thread_id,
-            'course_id': unicode(self.course.id),
+            'course_id': text_type(self.course.id),
             'children': [cs_comment],
         })
         self.register_get_thread_response(cs_thread)
diff --git a/lms/djangoapps/discussion_api/tests/utils.py b/lms/djangoapps/discussion_api/tests/utils.py
index 2112295911bb48bdc18b1a5a19ca170b1c1f6f17..6a0b43764f0ef7e87c91cbf1ef1cc26e0b69d14b 100644
--- a/lms/djangoapps/discussion_api/tests/utils.py
+++ b/lms/djangoapps/discussion_api/tests/utils.py
@@ -216,6 +216,16 @@ class CommentsServiceMockMixin(object):
             status=200
         )
 
+    def register_get_user_retire_response(self, user, status=200, body=""):
+        """Register a mock response for GET on the CS user retirement endpoint"""
+        assert httpretty.is_enabled(), 'httpretty must be enabled to mock calls.'
+        httpretty.register_uri(
+            httpretty.POST,
+            "http://localhost:4567/api/v1/users/{id}/retire".format(id=user.id),
+            body=body,
+            status=status
+        )
+
     def register_subscribed_threads_response(self, user, threads, page, num_pages):
         """Register a mock response for GET on the CS user instance endpoint"""
         assert httpretty.is_enabled(), 'httpretty must be enabled to mock calls.'
diff --git a/lms/djangoapps/discussion_api/urls.py b/lms/djangoapps/discussion_api/urls.py
index 4d884582ba8d4ad93816d63ebdf2fad349ca0590..898dfdc921a3cd38a48bba1c28c8cbc8d6949746 100644
--- a/lms/djangoapps/discussion_api/urls.py
+++ b/lms/djangoapps/discussion_api/urls.py
@@ -5,7 +5,7 @@ from django.conf import settings
 from django.conf.urls import include, url
 from rest_framework.routers import SimpleRouter
 
-from discussion_api.views import CommentViewSet, CourseTopicsView, CourseView, ThreadViewSet
+from discussion_api.views import CommentViewSet, CourseTopicsView, CourseView, ThreadViewSet, RetireUserView
 
 ROUTER = SimpleRouter()
 ROUTER.register("threads", ThreadViewSet, base_name="thread")
@@ -17,6 +17,7 @@ urlpatterns = [
         CourseView.as_view(),
         name="discussion_course"
     ),
+    url(r"^v1/users/{}".format(settings.USERNAME_PATTERN), RetireUserView.as_view(), name="retire_discussion_user"),
     url(
         r"^v1/course_topics/{}".format(settings.COURSE_ID_PATTERN),
         CourseTopicsView.as_view(),
diff --git a/lms/djangoapps/discussion_api/views.py b/lms/djangoapps/discussion_api/views.py
index fcf89087d720cb08e8f0067876375c3498b00be9..a264064c5e5b0b84a960327e258ee726488a5269 100644
--- a/lms/djangoapps/discussion_api/views.py
+++ b/lms/djangoapps/discussion_api/views.py
@@ -2,13 +2,19 @@
 Discussion API views
 """
 from django.core.exceptions import ValidationError
+from django.contrib.auth import get_user_model
+from edx_rest_framework_extensions.authentication import JwtAuthentication
 from opaque_keys.edx.keys import CourseKey
+from rest_framework import permissions
+from rest_framework import status
 from rest_framework.exceptions import UnsupportedMediaType
 from rest_framework.parsers import JSONParser
 from rest_framework.response import Response
 from rest_framework.views import APIView
 from rest_framework.viewsets import ViewSet
+from six import text_type
 
+from lms.lib import comment_client
 from discussion_api.api import (
     create_comment,
     create_thread,
@@ -26,6 +32,8 @@ from discussion_api.api import (
 from discussion_api.forms import CommentGetForm, CommentListGetForm, ThreadListGetForm
 from openedx.core.lib.api.parsers import MergePatchParser
 from openedx.core.lib.api.view_utils import DeveloperErrorViewMixin, view_auth_classes
+from openedx.core.djangoapps.user_api.accounts.permissions import CanRetireUser
+from student.models import get_potentially_retired_user_by_username_and_hash
 from xmodule.modulestore.django import modulestore
 
 
@@ -512,3 +520,48 @@ class CommentViewSet(DeveloperErrorViewMixin, ViewSet):
         if request.content_type != MergePatchParser.media_type:
             raise UnsupportedMediaType(request.content_type)
         return Response(update_comment(request, comment_id, request.data))
+
+
+class RetireUserView(APIView):
+    """
+    **Use Cases**
+
+        A superuser or the user with the settings.RETIREMENT_SERVICE_WORKER_USERNAME
+        can "retire" the user's data from the comments service, which will remove
+        personal information and blank all posts / comments the user has made.
+
+    **Example Requests**:
+        POST /api/discussion/v1/retire_user/
+        {
+            "retired_username": "old_user_name"
+        }
+
+    **Example Response**:
+        Empty string
+    """
+
+    authentication_classes = (JwtAuthentication,)
+    permission_classes = (permissions.IsAuthenticated, CanRetireUser)
+
+    def post(self, request, username):
+        """
+        Implements the retirement endpoint.
+        """
+        user_model = get_user_model()
+        retired_username = request.data['retired_username']
+
+        try:
+            user = get_potentially_retired_user_by_username_and_hash(username, retired_username)
+            cc_user = comment_client.User.from_django_user(user)
+
+            # We can't count on the LMS username being un-retired at this point,
+            # so we pass the old username as a parameter to describe which
+            # user to retire. This will either succeed or throw an error which
+            # should be good to raise from here.
+            cc_user.retire(username)
+        except user_model.DoesNotExist:
+            return Response(status=status.HTTP_404_NOT_FOUND)
+        except Exception as exc:  # pylint: disable=broad-except
+            return Response(text_type(exc), status=status.HTTP_500_INTERNAL_SERVER_ERROR)
+
+        return Response(status=status.HTTP_204_NO_CONTENT)
diff --git a/lms/lib/comment_client/user.py b/lms/lib/comment_client/user.py
index 238061c330ad679b484be91fb3d09eb6afdadb88..c58092128f685c062fc60afa718ba20caaca493f 100644
--- a/lms/lib/comment_client/user.py
+++ b/lms/lib/comment_client/user.py
@@ -2,9 +2,7 @@
 from six import text_type
 
 import settings
-
 import models
-
 import utils
 
 
@@ -169,6 +167,19 @@ class User(models.Model):
                 raise
         self._update_from_response(response)
 
+    def retire(self, retired_username):
+        url = _url_for_retire(self.id)
+        params = {'retired_username': retired_username}
+
+        utils.perform_request(
+            'post',
+            url,
+            params,
+            raw=True,
+            metric_action='user.retire',
+            metric_tags=self._metric_tags
+        )
+
 
 def _url_for_vote_comment(comment_id):
     return "{prefix}/comments/{comment_id}/votes".format(prefix=settings.PREFIX, comment_id=comment_id)
@@ -195,3 +206,10 @@ def _url_for_read(user_id):
     Returns cs_comments_service url endpoint to mark thread as read for given user_id
     """
     return "{prefix}/users/{user_id}/read".format(prefix=settings.PREFIX, user_id=user_id)
+
+
+def _url_for_retire(user_id):
+    """
+    Returns cs_comments_service url endpoint to retire a user (remove all post content, etc.)
+    """
+    return "{prefix}/users/{user_id}/retire".format(prefix=settings.PREFIX, user_id=user_id)