diff --git a/opentech/apply/activity/messaging.py b/opentech/apply/activity/messaging.py
index c52bd74b3261d8ed533ce2142dfbd86d1fac6d31..1d973043d39e8ea6971d83616d6dab685c90e9da 100644
--- a/opentech/apply/activity/messaging.py
+++ b/opentech/apply/activity/messaging.py
@@ -4,9 +4,10 @@ import requests
 
 from django.conf import settings
 from django.contrib import messages
-from django.core.mail import send_mail
 from django.template.loader import render_to_string
 
+from .tasks import send_mail, update_message_status
+
 
 def link_to(target, request):
     return request.scheme + '://' + request.get_host() + target.get_absolute_url()
@@ -69,13 +70,13 @@ class AdapterBase:
             return
 
         for recipient in self.recipients(message_type, **kwargs):
+            message_log = self.create_log(message, recipient, event)
             if settings.SEND_MESSAGES or self.always_send:
-                status = self.send_message(message, recipient=recipient, **kwargs)
+                status = self.send_message(message, recipient=recipient, message_log=message_log, **kwargs)
             else:
                 status = 'Message not sent as SEND_MESSAGES==FALSE'
 
-            if status:
-                self.log_message(message, recipient, event, status)
+            message_log.update_status(status)
 
             if not settings.SEND_MESSAGES:
                 if recipient:
@@ -84,14 +85,13 @@ class AdapterBase:
                     message = '{}: {}'.format(self.adapter_type, message)
                 messages.add_message(kwargs['request'], messages.INFO, message)
 
-    def log_message(self, message, recipient, event, status):
-        from.models import Message
-        Message.objects.create(
+    def create_log(self, message, recipient, event):
+        from .models import Message
+        return Message.objects.create(
             type=self.adapter_type,
             content=message,
             recipient=recipient,
             event=event,
-            status=status,
         )
 
     def send_message(self, message, **kwargs):
@@ -253,18 +253,18 @@ class EmailAdapter(AdapterBase):
 
     def send_message(self, message, submission, subject, recipient, **kwargs):
         try:
-            emails_sent = send_mail(
-                subject,
-                message,
-                submission.page.specific.from_address,
-                [recipient],
-                fail_silently=False,
+            emails_sent = send_mail.apply_async(
+                (
+                    subject,
+                    message,
+                    submission.page.specific.from_address,
+                    [recipient],
+                ),
+                link=update_message_status.s(kwargs['message_log'].id),
             )
         except Exception as e:
             return 'Error: ' + str(e)
 
-        return 'Emails sent: ' + str(emails_sent)
-
 
 class MessengerBackend:
     def __init__(self, *adpaters):
diff --git a/opentech/apply/activity/models.py b/opentech/apply/activity/models.py
index 7970cf9eb5fe928833f5bf5a38eb4da0466c5227..caa8cd54a7b77cb12a21a350269db671e79a5545 100644
--- a/opentech/apply/activity/models.py
+++ b/opentech/apply/activity/models.py
@@ -1,5 +1,7 @@
 from django.conf import settings
 from django.db import models
+from django.db.models import Case, When, Value
+from django.db.models.functions import Concat
 
 from .messaging import MESSAGES
 
@@ -121,3 +123,11 @@ class Message(models.Model):
     recipient = models.CharField(max_length=250)
     event = models.ForeignKey(Event, on_delete=models.CASCADE)
     status = models.TextField()
+
+    def update_status(self, status):
+        if status:
+            self.status = Case(
+                When(status='', then=Value(status)),
+                default=Concat('status', Value('<br />' + status))
+            )
+            self.save()
diff --git a/opentech/apply/activity/tasks.py b/opentech/apply/activity/tasks.py
new file mode 100644
index 0000000000000000000000000000000000000000..53a16f845d12d775132b063ebb4bd3c5b2879f42
--- /dev/null
+++ b/opentech/apply/activity/tasks.py
@@ -0,0 +1,28 @@
+from celery import Celery
+
+from django.conf import settings
+from django.core.mail import send_mail as dj_send_mail
+
+app = Celery('tasks')
+
+app.config_from_object(settings, namespace='CELERY', force=True)
+
+
+@app.task
+def send_mail(*args, **kwargs):
+    try:
+        emails_sent = dj_send_mail(
+            *args,
+            fail_silently=False,
+            **kwargs,
+        )
+    except Exception as e:
+        return 'Error: ' + str(e)
+
+    return 'Emails sent: ' + str(emails_sent)
+
+
+@app.task
+def update_message_status(status, message):
+    from .models import Message
+    Message.objects.get(id=message).update_status(status)
diff --git a/opentech/apply/activity/tests/test_tasks.py b/opentech/apply/activity/tests/test_tasks.py
new file mode 100644
index 0000000000000000000000000000000000000000..b9c73a965b073f08c166c91b8f88dbb64e589186
--- /dev/null
+++ b/opentech/apply/activity/tests/test_tasks.py
@@ -0,0 +1,13 @@
+from unittest.mock import patch
+
+from django.test import TestCase
+
+from ..tasks import send_mail
+
+
+class TestSendEmail(TestCase):
+    @patch('opentech.apply.activity.tasks.dj_send_mail')
+    def test_args_passed_to_django(self, dj_send_mail):
+        args = ['subject', 'message', 'from', ['to@to.com']]
+        send_mail(*args)
+        dj_send_mail.assert_called_once_with(*args, fail_silently=False)
diff --git a/opentech/settings/base.py b/opentech/settings/base.py
index c4da25f92ef4553be9dbb4358242a49b94d39b46..6d4b5579fe4f8396baa34b8e2abfc1d5775d8e29 100644
--- a/opentech/settings/base.py
+++ b/opentech/settings/base.py
@@ -356,3 +356,10 @@ HIJACK_DECORATOR = 'opentech.apply.users.decorators.superuser_decorator'
 SEND_MESSAGES = env.get('SEND_MESSAGES', 'false').lower() == 'true'
 SLACK_DESTINATION_URL = env.get('SLACK_DESTINATION_URL', None)
 SLACK_DESTINATION_ROOM = env.get('SLACK_DESTINATION_ROOM', None)
+
+
+# Celery config
+if 'REDIS_URL' in env:
+    CELERY_BROKER_URL = env.get('REDIS_URL')
+else:
+    CELERY_TASK_ALWAYS_EAGER = True
diff --git a/requirements.txt b/requirements.txt
index beb571c642a3e18eba3c76d4ec83563eaed9a745..0ea9b0b33fa436dc7538579fffaa11c5336c4588 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -12,6 +12,8 @@ stellar==0.4.3
 django-tinymce4-lite==1.7.0
 uwsgidecorators==1.1.0
 django-hijack==2.1.9
+django-anymail==3.0
+celery==4.2.1
 
 factory_boy==2.9.2
 # wagtail_factories - waiting on merge and release form master branch