diff --git a/hypha/apply/activity/messaging.py b/hypha/apply/activity/messaging.py
index 07d56b18c26a36e4fae32d564a002514b118a4d0..944532c958d333b4c6fa9f495278155764469b39 100644
--- a/hypha/apply/activity/messaging.py
+++ b/hypha/apply/activity/messaging.py
@@ -2,7 +2,6 @@ import json
 import logging
 from collections import defaultdict
 
-import requests
 from django.conf import settings
 from django.contrib import messages
 from django.contrib.auth import get_user_model
@@ -10,6 +9,7 @@ from django.db import models
 from django.template.loader import render_to_string
 from django.utils import timezone
 from django.utils.translation import gettext as _
+from django_slack import slack_message
 
 from hypha.apply.projects.models.payment import (
     APPROVED_BY_FINANCE_1,
@@ -467,7 +467,7 @@ class SlackAdapter(AdapterBase):
 
     def __init__(self):
         super().__init__()
-        self.destination = settings.SLACK_DESTINATION_URL
+        self.destination = settings.SLACK_ENDPOINT_URL
         self.target_room = settings.SLACK_DESTINATION_ROOM
         self.comments_room = settings.SLACK_DESTINATION_ROOM_COMMENTS
         self.comments_type = settings.SLACK_TYPE_COMMENTS
@@ -716,23 +716,26 @@ class SlackAdapter(AdapterBase):
     def send_message(self, message, recipient, source, **kwargs):
         target_rooms = self.slack_channels(source, **kwargs)
 
-        if not self.destination or not any(target_rooms):
+        if not any(target_rooms) or not settings.SLACK_TOKEN:
             errors = list()
-            if not self.destination:
-                errors.append('Destination URL')
             if not target_rooms:
                 errors.append('Room ID')
+            if not settings.SLACK_TOKEN:
+                errors.append('Slack Token')
             return 'Missing configuration: {}'.format(', '.join(errors))
 
         message = ' '.join([recipient, message]).strip()
 
         data = {
-            "room": target_rooms,
             "message": message,
         }
-        response = requests.post(self.destination, json=data)
-
-        return str(response.status_code) + ': ' + response.content.decode()
+        for room in target_rooms:
+            try:
+                slack_message('messages/slack_message.html', data, channel=room)
+            except Exception as e:
+                logger.exception(e)
+                return '400: Bad Request'
+        return '200: OK'
 
 
 class EmailAdapter(AdapterBase):
diff --git a/hypha/apply/activity/templates/messages/slack_message.html b/hypha/apply/activity/templates/messages/slack_message.html
new file mode 100644
index 0000000000000000000000000000000000000000..1974748d10599f1f3950bac5009d9f22736b1ab2
--- /dev/null
+++ b/hypha/apply/activity/templates/messages/slack_message.html
@@ -0,0 +1,6 @@
+{% extends django_slack %}
+<!--Template required for django-slack. We can customize it for channels, endpoint_url, etc as per the requirements.-->
+
+{% block text %}
+{{ message|safe }}
+{% endblock %}
diff --git a/hypha/apply/activity/tests/test_messaging.py b/hypha/apply/activity/tests/test_messaging.py
index bd716ec6256dc611edab3c147bb88b170a403055..e6f90c0c6a1029cb41bd185e4ae514586a8ea1c1 100644
--- a/hypha/apply/activity/tests/test_messaging.py
+++ b/hypha/apply/activity/tests/test_messaging.py
@@ -7,6 +7,7 @@ import responses
 from django.contrib.messages import get_messages
 from django.core import mail
 from django.test import TestCase, override_settings
+from django_slack.utils import get_backend
 
 from hypha.apply.funds.tests.factories import (
     ApplicationSubmissionFactory,
@@ -330,99 +331,101 @@ class TestActivityAdapter(TestCase):
 class TestSlackAdapter(AdapterMixin, TestCase):
     source_factory = ApplicationSubmissionFactory
 
+    backend = 'django_slack.backends.TestBackend'
     target_url = 'https://my-slack-backend.com/incoming/my-very-secret-key'
     target_room = '#<ROOM ID>'
+    token = 'fake-token'
 
     @override_settings(
-        SLACK_DESTINATION_URL=target_url,
+        SLACK_ENDPOINT_URL=target_url,
         SLACK_DESTINATION_ROOM=None,
+        SLACK_BACKEND=backend,
+        SLACK_TOKEN=token,
     )
-    @responses.activate
     def test_cant_send_with_no_room(self):
+        error_message = "Missing configuration: Room ID"
         adapter = SlackAdapter()
         submission = ApplicationSubmissionFactory()
-        adapter.send_message('my message', '', source=submission)
-        self.assertEqual(len(responses.calls), 0)
+        messages = adapter.send_message('my message', '', source=submission)
+        self.assertEqual(messages, error_message)
 
     @override_settings(
-        SLACK_DESTINATION_URL=None,
+        SLACK_ENDPOINT_URL=target_url,
         SLACK_DESTINATION_ROOM=target_room,
+        SLACK_BACKEND=backend,
+        SLACK_TOKEN=None,
     )
-    @responses.activate
-    def test_cant_send_with_no_url(self):
+    def test_cant_send_with_no_token(self):
+        error_message = "Missing configuration: Slack Token"
         adapter = SlackAdapter()
         submission = ApplicationSubmissionFactory()
-        adapter.send_message('my message', '', source=submission)
-        self.assertEqual(len(responses.calls), 0)
+        messages = adapter.send_message('my message', '', source=submission)
+        self.assertEqual(messages, error_message)
 
     @override_settings(
-        SLACK_DESTINATION_URL=target_url,
+        SLACK_ENDPOINT_URL=target_url,
         SLACK_DESTINATION_ROOM=target_room,
+        SLACK_BACKEND=backend,
+        SLACK_TOKEN=token,
     )
-    @responses.activate
     def test_correct_payload(self):
-        responses.add(responses.POST, self.target_url, status=200, body='OK')
+        backend = get_backend()
+        backend.reset_messages()
         submission = ApplicationSubmissionFactory()
         adapter = SlackAdapter()
         message = 'my message'
         adapter.send_message(message, '', source=submission)
-        self.assertEqual(len(responses.calls), 1)
-        self.assertDictEqual(
-            json.loads(responses.calls[0].request.body),
-            {
-                'room': [self.target_room],
-                'message': message,
-            }
-        )
+        messages = backend.retrieve_messages()
+        self.assertEqual(len(messages), 1)
+        message_payload = json.loads(messages[0]['payload'])
+        self.assertEqual(message_payload['text'], message)
 
     @override_settings(
-        SLACK_DESTINATION_URL=target_url,
+        SLACK_ENDPOINT_URL=target_url,
         SLACK_DESTINATION_ROOM=target_room,
+        SLACK_BACKEND=backend,
+        SLACK_TOKEN=token,
     )
-    @responses.activate
     def test_fund_custom_slack_channel(self):
+        backend = get_backend()
+        backend.reset_messages()
         responses.add(responses.POST, self.target_url, status=200, body='OK')
         submission = ApplicationSubmissionFactory(round__parent__slack_channel='dummy')
         adapter = SlackAdapter()
         message = 'my message'
         adapter.send_message(message, '', source=submission)
-        self.assertEqual(len(responses.calls), 1)
-        self.assertDictEqual(
-            json.loads(responses.calls[0].request.body),
-            {
-                'room': ['#dummy'],
-                'message': message,
-            }
-        )
+        messages = backend.retrieve_messages()
+        self.assertEqual(len(messages), 1)
+        message_payload = json.loads(messages[0]['payload'])
+        self.assertEqual(message_payload['text'], message)
+        self.assertEqual(message_payload['channel'], '#dummy')
 
     @override_settings(
-        SLACK_DESTINATION_URL=target_url,
+        SLACK_ENDPOINT_URL=target_url,
         SLACK_DESTINATION_ROOM=target_room,
+        SLACK_BACKEND=backend,
+        SLACK_TOKEN=token,
     )
-    @responses.activate
     def test_fund_multiple_custom_slack_channel(self):
-        responses.add(responses.POST, self.target_url, status=200, body='OK')
+        backend = get_backend()
+        backend.reset_messages()
         submission = ApplicationSubmissionFactory(round__parent__slack_channel='dummy1, dummy2')
         adapter = SlackAdapter()
         message = 'my message'
         adapter.send_message(message, '', source=submission)
-        self.assertEqual(len(responses.calls), 1)
-        self.assertDictEqual(
-            json.loads(responses.calls[0].request.body),
-            {
-                'room': ['#dummy1', '#dummy2'],
-                'message': message,
-            }
-        )
+        messages = backend.retrieve_messages()
+        self.assertEqual(len(messages), 2)
+        for index, sent_message in enumerate(messages):
+            message_payload = json.loads(sent_message['payload'])
+            self.assertEqual(message_payload['text'], message)
+            self.assertEqual(message_payload['channel'], '#dummy' + str(index + 1))
 
-    @responses.activate
     def test_gets_lead_if_slack_set(self):
         adapter = SlackAdapter()
         submission = ApplicationSubmissionFactory()
         recipients = adapter.recipients(MESSAGES.COMMENT, source=submission, related=None)
         self.assertTrue(submission.lead.slack in recipients[0])
 
-    @responses.activate
     def test_gets_blank_if_slack_not_set(self):
         adapter = SlackAdapter()
         submission = ApplicationSubmissionFactory(lead__slack='')
@@ -430,13 +433,12 @@ class TestSlackAdapter(AdapterMixin, TestCase):
         self.assertTrue(submission.lead.slack in recipients[0])
 
     @override_settings(
-        SLACK_DESTINATION_URL=target_url,
+        SLACK_ENDPOINT_URL=target_url,
         SLACK_DESTINATION_ROOM=target_room,
+        SLACK_BACKEND=backend,
+        SLACK_TOKEN=token,
     )
-    @responses.activate
     def test_message_with_good_response(self):
-        responses.add(responses.POST, self.target_url, status=200, body='OK')
-
         self.adapter = SlackAdapter()
         self.adapter_process(MESSAGES.NEW_SUBMISSION)
         self.assertEqual(Message.objects.count(), 1)
@@ -445,19 +447,21 @@ class TestSlackAdapter(AdapterMixin, TestCase):
         self.assertEqual(sent_message.status, '200: OK')
 
     @override_settings(
-        SLACK_DESTINATION_URL=target_url,
+        SLACK_ENDPOINT_URL=target_url,
         SLACK_DESTINATION_ROOM=target_room,
+        SLACK_BACKEND=backend,
+        SLACK_TOKEN=token,
     )
-    @responses.activate
-    def test_message_with_bad_response(self):
-        responses.add(responses.POST, self.target_url, status=400, body='Bad Request')
-
-        self.adapter = SlackAdapter()
-        self.adapter_process(MESSAGES.NEW_SUBMISSION)
-        self.assertEqual(Message.objects.count(), 1)
-        sent_message = Message.objects.first()
-        self.assertEqual(sent_message.content[0:10], self.adapter.messages[MESSAGES.NEW_SUBMISSION][0:10])
-        self.assertEqual(sent_message.status, '400: Bad Request')
+    def test_400_bad_request(self):
+        backend = get_backend()
+        backend.reset_messages()
+        submission = ApplicationSubmissionFactory()
+        adapter = SlackAdapter()
+        message = ''
+        message_status = adapter.send_message(message, '', source=submission)
+        messages = backend.retrieve_messages()
+        self.assertEqual(len(messages), 0)
+        self.assertEqual(message_status, '400: Bad Request')
 
 
 @override_settings(SEND_MESSAGES=True)
@@ -592,8 +596,10 @@ class TestAdaptersForProject(AdapterMixin, TestCase):
     activity = ActivityAdapter
     source_factory = ProjectFactory
     # Slack
+    backend = 'django_slack.backends.TestBackend'
     target_url = 'https://my-slack-backend.com/incoming/my-very-secret-key'
     target_room = '#<ROOM ID>'
+    token = 'fake-token'
 
     def test_activity_lead_change(self):
         old_lead = UserFactory()
@@ -635,12 +641,14 @@ class TestAdaptersForProject(AdapterMixin, TestCase):
         self.assertEqual(project.submission, activity.related_object)
 
     @override_settings(
-        SLACK_DESTINATION_URL=target_url,
+        SLACK_ENDPOINT_URL=target_url,
         SLACK_DESTINATION_ROOM=target_room,
+        SLACK_BACKEND=backend,
+        SLACK_TOKEN=token,
     )
-    @responses.activate
     def test_slack_created(self):
-        responses.add(responses.POST, self.target_url, status=200, body='OK')
+        backend = get_backend()
+        backend.reset_messages()
         project = self.source_factory()
         user = UserFactory()
         self.adapter_process(
@@ -650,18 +658,21 @@ class TestAdaptersForProject(AdapterMixin, TestCase):
             source=project,
             related=project.submission,
         )
-        self.assertEqual(len(responses.calls), 1)
-        data = json.loads(responses.calls[0].request.body)
-        self.assertIn(str(user), data['message'])
-        self.assertIn(str(project), data['message'])
+        messages = backend.retrieve_messages()
+        self.assertEqual(len(messages), 1)
+        message_payload = json.loads(messages[0]['payload'])
+        self.assertIn(str(user), message_payload['text'])
+        self.assertIn(str(project), message_payload['text'])
 
     @override_settings(
-        SLACK_DESTINATION_URL=target_url,
+        SLACK_ENDPOINT_URL=target_url,
         SLACK_DESTINATION_ROOM=target_room,
+        SLACK_BACKEND=backend,
+        SLACK_TOKEN=token,
     )
-    @responses.activate
     def test_slack_lead_change(self):
-        responses.add(responses.POST, self.target_url, status=200, body='OK')
+        backend = get_backend()
+        backend.reset_messages()
         project = self.source_factory()
         user = UserFactory()
         self.adapter_process(
@@ -671,19 +682,21 @@ class TestAdaptersForProject(AdapterMixin, TestCase):
             source=project,
             related=project.submission,
         )
-        self.assertEqual(len(responses.calls), 1)
-        data = json.loads(responses.calls[0].request.body)
-        self.assertIn(str(user), data['message'])
-        self.assertIn(str(project), data['message'])
+        messages = backend.retrieve_messages()
+        self.assertEqual(len(messages), 1)
+        message_payload = json.loads(messages[0]['payload'])
+        self.assertIn(str(user), message_payload['text'])
+        self.assertIn(str(project), message_payload['text'])
 
     @override_settings(
-        SLACK_DESTINATION_URL=target_url,
+        SLACK_ENDPOINT_URL=target_url,
         SLACK_DESTINATION_ROOM=target_room,
+        SLACK_BACKEND=backend,
+        SLACK_TOKEN=token,
     )
-    @responses.activate
     def test_slack_applicant_update_invoice(self):
-        responses.add(responses.POST, self.target_url, status=200, body='OK')
-
+        backend = get_backend()
+        backend.reset_messages()
         project = self.source_factory()
         invoice = InvoiceFactory(project=project)
         applicant = ApplicantFactory()
@@ -695,21 +708,22 @@ class TestAdaptersForProject(AdapterMixin, TestCase):
             source=project,
             related=invoice,
         )
+        messages = backend.retrieve_messages()
 
-        self.assertEqual(len(responses.calls), 1)
-
-        data = json.loads(responses.calls[0].request.body)
-        self.assertIn(str(applicant), data['message'])
-        self.assertIn(str(project), data['message'])
+        self.assertEqual(len(messages), 1)
+        message_payload = json.loads(messages[0]['payload'])
+        self.assertIn(str(applicant), message_payload['text'])
+        self.assertIn(str(project), message_payload['text'])
 
     @override_settings(
-        SLACK_DESTINATION_URL=target_url,
+        SLACK_ENDPOINT_URL=target_url,
         SLACK_DESTINATION_ROOM=target_room,
+        SLACK_BACKEND=backend,
+        SLACK_TOKEN=token,
     )
-    @responses.activate
     def test_slack_staff_update_invoice(self):
-        responses.add(responses.POST, self.target_url, status=200, body='OK')
-
+        backend = get_backend()
+        backend.reset_messages()
         project = self.source_factory()
         invoice = InvoiceFactory(project=project)
         staff = StaffFactory()
@@ -721,8 +735,8 @@ class TestAdaptersForProject(AdapterMixin, TestCase):
             source=project,
             related=invoice,
         )
-
-        self.assertEqual(len(responses.calls), 1)
+        messages = backend.retrieve_messages()
+        self.assertEqual(len(messages), 1)
 
     @override_settings(SEND_MESSAGES=True)
     def test_email_staff_update_invoice(self):
diff --git a/hypha/apply/utils/notifications.py b/hypha/apply/utils/notifications.py
index 13e403e4b1800de4ea94de5e90580cdcdbaf2e30..8b6930af1cf1e7004055709910b3726915c729aa 100644
--- a/hypha/apply/utils/notifications.py
+++ b/hypha/apply/utils/notifications.py
@@ -1,11 +1,15 @@
-import requests
+import logging
+
 from django.conf import settings
+from django_slack import slack_message
+
+logger = logging.getLogger(__name__)
 
 
 class SlackNotifications():
 
     def __init__(self):
-        self.destination = settings.SLACK_DESTINATION_URL
+        self.destination = settings.SLACK_ENDPOINT_URL
         self.target_room = settings.SLACK_DESTINATION_ROOM
 
     def __call__(self, *args, recipients=None, related=None, **kwargs):
@@ -29,12 +33,14 @@ class SlackNotifications():
         return f'<{link}|{title}>'
 
     def send_message(self, message, request, recipients=None, related=None, **kwargs):
-        if not self.destination or not self.target_room:
+        if not self.destination or not self.target_room or not settings.SLACK_TOKEN:
             errors = list()
             if not self.destination:
                 errors.append('Destination URL')
             if not self.target_room:
                 errors.append('Room ID')
+            if not settings.SLACK_TOKEN:
+                errors.append('Slack Token')
             return 'Missing configuration: {}'.format(', '.join(errors))
 
         slack_users = self.slack_users(recipients) if recipients else ''
@@ -44,12 +50,14 @@ class SlackNotifications():
         message = ' '.join([slack_users, message, slack_link]).strip()
 
         data = {
-            "room": self.target_room,
             "message": message,
         }
-        response = requests.post(self.destination, json=data)
-
-        return str(response.status_code) + ': ' + response.content.decode()
+        try:
+            slack_message('messages/slack_message.html', data, channel=self.target_room)
+            return '200: OK'
+        except Exception as e:
+            logger.exception(e)
+            return '400: Bad Request'
 
 
 slack_notify = SlackNotifications()
diff --git a/hypha/settings/base.py b/hypha/settings/base.py
index 10f87df0d0df4900b15d8b49a12ca971721d0a1d..07e5af2b9dc97fad7fbfc133c753e5d03903e143 100644
--- a/hypha/settings/base.py
+++ b/hypha/settings/base.py
@@ -105,6 +105,7 @@ INSTALLED_APPS = [
     'django_bleach',
     'django_fsm',
     'django_pwned_passwords',
+    'django_slack',
     'django_otp',
     'django_otp.plugins.otp_totp',
     'django_otp.plugins.otp_static',
@@ -524,7 +525,13 @@ if not SEND_MESSAGES:
 
 SEND_READY_FOR_REVIEW = env.bool('SEND_READY_FOR_REVIEW', True)
 
-SLACK_DESTINATION_URL = env.str('SLACK_DESTINATION_URL', None)
+# Django Slack settings
+SLACK_TOKEN = env.str('SLACK_TOKEN', None)
+SLACK_USERNAME = env.str('SLACK_USERNAME', 'Hypha')
+SLACK_BACKEND = 'django_slack.backends.CeleryBackend'  # UrllibBackend can be used for sync
+SLACK_ENDPOINT_URL = env.str('SLACK_ENDPOINT_URL', 'https://slack.com/api/chat.postMessage')
+
+# Slack settings
 SLACK_DESTINATION_ROOM = env.str('SLACK_DESTINATION_ROOM', None)
 SLACK_DESTINATION_ROOM_COMMENTS = env.str('SLACK_DESTINATION_ROOM_COMMENTS', None)
 SLACK_TYPE_COMMENTS = env.list('SLACK_TYPE_COMMENTS', [])
diff --git a/requirements.txt b/requirements.txt
index c66d1540300e2172c40cbf54f95da9a7aad5f82a..bad1dafa298265c3a425c49f12fa95efe39a375b 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -24,6 +24,7 @@ django-redis==5.1.0
 django-referrer-policy==1.0
 django-salesforce==4.0
 django-select2==7.9.0
+django-slack==5.17.7
 django-storages==1.12.3
 django-tables2==2.4.1
 django-tinymce==3.4.0