diff --git a/hypha/apply/funds/models/applications.py b/hypha/apply/funds/models/applications.py
index 19f7846b30e4a48417ea8eb580764d40cf6db01a..b3d5ca4290767d345c2988c18e14b856edf2c625 100644
--- a/hypha/apply/funds/models/applications.py
+++ b/hypha/apply/funds/models/applications.py
@@ -19,6 +19,7 @@ from django.db.models import (
 from django.db.models.functions import Coalesce, Left, Length
 from django.http import Http404
 from django.shortcuts import render
+from django.template.response import TemplateResponse
 from django.utils.functional import cached_property
 from django.utils.safestring import mark_safe
 from django.utils.translation import gettext_lazy as _
@@ -356,10 +357,11 @@ class RoundBase(WorkflowStreamForm, SubmittableStreamForm):  # type: ignore
             # Overriding serve method to pass submission id to get_form method
             copy_open_submission = request.GET.get('open_call_submission')
             if request.method == 'POST':
+                draft = request.POST.get('draft', False)
                 form = self.get_form(request.POST, request.FILES, page=self, user=request.user)
 
                 if form.is_valid():
-                    form_submission = self.process_form_submission(form)
+                    form_submission = self.process_form_submission(form, draft=draft)
                     return self.render_landing_page(request, form_submission, *args, **kwargs)
             else:
                 form = self.get_form(page=self, user=request.user, submission_id=copy_open_submission)
@@ -441,6 +443,24 @@ class LabBase(EmailForm, WorkflowStreamForm, SubmittableStreamForm):  # type: ig
     def open_round(self):
         return self.live
 
+    def serve(self, request, *args, **kwargs):
+        if request.method == 'POST':
+            form = self.get_form(request.POST, request.FILES, page=self, user=request.user)
+            draft = request.POST.get('draft', False)
+            if form.is_valid():
+                form_submission = SubmittableStreamForm.process_form_submission(self, form, draft=draft)
+                return self.render_landing_page(request, form_submission, *args, **kwargs)
+        else:
+            form = self.get_form(page=self, user=request.user)
+
+        context = self.get_context(request)
+        context['form'] = form
+        return TemplateResponse(
+            request,
+            self.get_template(request),
+            context
+        )
+
 
 class RoundsAndLabsQueryset(PageQuerySet):
     def new(self):
diff --git a/hypha/apply/funds/models/submissions.py b/hypha/apply/funds/models/submissions.py
index 741293d6e432a457824e9a862815536ff965da05..f13ae29f76522ca729a8f6f55bbe325f366f61d1 100644
--- a/hypha/apply/funds/models/submissions.py
+++ b/hypha/apply/funds/models/submissions.py
@@ -45,6 +45,7 @@ from ..blocks import NAMED_BLOCKS, ApplicationCustomFormFieldsBlock
 from ..workflow import (
     COMMUNITY_REVIEW_PHASES,
     DETERMINATION_RESPONSE_PHASES,
+    DRAFT_STATE,
     INITIAL_STATE,
     PHASES,
     PHASES_MAPPING,
@@ -160,6 +161,9 @@ class ApplicationSubmissionQueryset(JSONOrderable):
             Sum('value'),
         )
 
+    def exclude_draft(self):
+        return self.exclude(status=DRAFT_STATE)
+
     def with_latest_update(self):
         activities = self.model.activities.rel.model
         latest_activity = activities.objects.filter(submission=OuterRef('id')).select_related('user')
@@ -786,13 +790,21 @@ def log_status_update(sender, **kwargs):
     notify = kwargs['method_kwargs'].get('notify', True)
 
     if request and notify:
-        messenger(
-            MESSAGES.TRANSITION,
-            user=by,
-            request=request,
-            source=instance,
-            related=old_phase,
-        )
+        if kwargs['source'] == DRAFT_STATE:
+            messenger(
+                MESSAGES.NEW_SUBMISSION,
+                request=request,
+                user=by,
+                source=instance,
+            )
+        else:
+            messenger(
+                MESSAGES.TRANSITION,
+                user=by,
+                request=request,
+                source=instance,
+                related=old_phase,
+            )
 
         if instance.status in review_statuses:
             messenger(
diff --git a/hypha/apply/funds/models/utils.py b/hypha/apply/funds/models/utils.py
index caa8194a2d7205fd4d212abed754e20cdc19b389..1c5de3edc8b781d920e6da62a79858a408000ccc 100644
--- a/hypha/apply/funds/models/utils.py
+++ b/hypha/apply/funds/models/utils.py
@@ -18,7 +18,7 @@ from hypha.apply.users.groups import (
     STAFF_GROUP_NAME,
 )
 
-from ..workflow import WORKFLOWS
+from ..workflow import DRAFT_STATE, WORKFLOWS
 
 REVIEW_GROUPS = [
     STAFF_GROUP_NAME,
@@ -65,14 +65,22 @@ class SubmittableStreamForm(AbstractStreamForm):
     def get_submission_class(self):
         return self.submission_class
 
-    def process_form_submission(self, form):
+    def process_form_submission(self, form, draft=False):
         if not form.user.is_authenticated:
             form.user = None
-        return self.get_submission_class().objects.create(
-            form_data=form.cleaned_data,
-            form_fields=self.get_defined_fields(),
-            **self.get_submit_meta_data(user=form.user),
-        )
+        if draft:
+            return self.get_submission_class().objects.create(
+                form_data=form.cleaned_data,
+                form_fields=self.get_defined_fields(),
+                **self.get_submit_meta_data(user=form.user),
+                status=DRAFT_STATE,
+            )
+        else:
+            return self.get_submission_class().objects.create(
+                form_data=form.cleaned_data,
+                form_fields=self.get_defined_fields(),
+                **self.get_submit_meta_data(user=form.user),
+            )
 
     def get_submit_meta_data(self, **kwargs):
         return kwargs
@@ -95,13 +103,14 @@ class WorkflowStreamForm(WorkflowHelpers, AbstractStreamForm):  # type: ignore
     def render_landing_page(self, request, form_submission=None, *args, **kwargs):
         # We only reach this page after creation of a new submission
         # Hook in to notify about new applications
-        messenger(
-            MESSAGES.NEW_SUBMISSION,
-            request=request,
-            user=form_submission.user,
-            source=form_submission,
-        )
-        return super().render_landing_page(request, form_submission=None, *args, **kwargs)
+        if not form_submission.status == DRAFT_STATE:
+            messenger(
+                MESSAGES.NEW_SUBMISSION,
+                request=request,
+                user=form_submission.user,
+                source=form_submission,
+            )
+        return super().render_landing_page(request, form_submission, *args, **kwargs)
 
     content_panels = AbstractStreamForm.content_panels + [
         FieldPanel('workflow_name'),
diff --git a/hypha/apply/funds/templates/funds/application_base.html b/hypha/apply/funds/templates/funds/application_base.html
index 86739048690ee4565fe789532bde602d51c921ac..32b43e12a1c729138a5c19ae1004092111e6fbff 100644
--- a/hypha/apply/funds/templates/funds/application_base.html
+++ b/hypha/apply/funds/templates/funds/application_base.html
@@ -54,6 +54,7 @@
                 {% endif %}
             {% endfor %}
             <button class="link link--button-secondary" type="submit" disabled>Submit for review</button>
+            <button class="link link--button-tertiary" type="submit" name="draft" value="Save Draft" formnovalidate>Save Draft</button>
         </form>
         <p class="wrapper--error message-no-js js-hidden">You must have Javascript enabled to use this form.</p>
     {% endif %}
diff --git a/hypha/apply/funds/templates/funds/application_base_landing.html b/hypha/apply/funds/templates/funds/application_base_landing.html
index 2d8ac2c8dd483023a3dc0baf63cb30a2100f14f9..b1d5080fabaabec350895f4264a896515c1b85d7 100644
--- a/hypha/apply/funds/templates/funds/application_base_landing.html
+++ b/hypha/apply/funds/templates/funds/application_base_landing.html
@@ -2,9 +2,17 @@
 {% block header_modifier %}header--light-bg{% endblock %}
 {% block content %}
 <div class="wrapper wrapper--small">
-    <h3>Thank you for your submission to the {{ ORG_LONG_NAME }}.</h3>
+	{% if form_submission.status == 'draft' %}
+		<h3>Your application is saved as a draft.</h3>
+	{% else %}
+    	<h3>Thank you for your submission to the {{ ORG_LONG_NAME }}.</h3>
+	{% endif %}
     <div class="rich-text">
-        <p>An e-mail with more information has been sent to the address you entered.</p>
+    	{% if form_submission.status == 'draft' %}
+    		<p>Please note that it is not submitted for review. You can complete your application by following the log-in details emailed to you.</p>
+    	{% else %}
+        	<p>An e-mail with more information has been sent to the address you entered.</p>
+        {% endif %}
         <p>If you do not receive an e-mail within 15 minutes please check your
 spam folder and contact {{ ORG_EMAIL|urlize }} for further assistance.</p>
         {% with email_context=page.specific %}<p>{{ email_context.confirmation_text_extra|urlize }}</p>{% endwith %}
diff --git a/hypha/apply/funds/tests/test_models.py b/hypha/apply/funds/tests/test_models.py
index a0486ff7f0bb8e331d7cc544d8599c3af0e212ab..fd04152a81638d4866062d2aee506d35867dad33 100644
--- a/hypha/apply/funds/tests/test_models.py
+++ b/hypha/apply/funds/tests/test_models.py
@@ -202,7 +202,7 @@ class TestFormSubmission(TestCase):
         self.round_page = RoundFactory(parent=fund, now=True)
         self.lab_page = LabFactory(lead=self.round_page.lead)
 
-    def submit_form(self, page=None, email=None, name=None, user=AnonymousUser(), ignore_errors=False):
+    def submit_form(self, page=None, email=None, name=None, draft=None, user=AnonymousUser(), ignore_errors=False):
         page = page or self.round_page
 
         fields = page.forms.first().fields
@@ -214,6 +214,8 @@ class TestFormSubmission(TestCase):
                 data[field.id] = self.email if email is None else email
             if isinstance(field.block, FullNameBlock):
                 data[field.id] = self.name if name is None else name
+            if draft:
+                data['draft'] = 'Save Draft'
 
         request = make_request(user, data, method='post', site=self.site)
 
@@ -228,17 +230,31 @@ class TestFormSubmission(TestCase):
             self.assertNotContains(response, 'errors')
         return response
 
+    def test_workflow_and_draft(self):
+        self.submit_form(draft=True)
+        submission = ApplicationSubmission.objects.first()
+        first_phase = list(self.round_page.workflow.keys())[0]
+        self.assertEqual(submission.workflow, self.round_page.workflow)
+        self.assertEqual(submission.status, first_phase)
+
+    def test_workflow_and_draft_lab(self):
+        self.submit_form(page=self.lab_page, draft=True)
+        submission = ApplicationSubmission.objects.first()
+        first_phase = list(self.lab_page.workflow.keys())[0]
+        self.assertEqual(submission.workflow, self.lab_page.workflow)
+        self.assertEqual(submission.status, first_phase)
+
     def test_workflow_and_status_assigned(self):
         self.submit_form()
         submission = ApplicationSubmission.objects.first()
-        first_phase = list(self.round_page.workflow.keys())[0]
+        first_phase = list(self.round_page.workflow.keys())[1]
         self.assertEqual(submission.workflow, self.round_page.workflow)
         self.assertEqual(submission.status, first_phase)
 
     def test_workflow_and_status_assigned_lab(self):
         self.submit_form(page=self.lab_page)
         submission = ApplicationSubmission.objects.first()
-        first_phase = list(self.lab_page.workflow.keys())[0]
+        first_phase = list(self.lab_page.workflow.keys())[1]
         self.assertEqual(submission.workflow, self.lab_page.workflow)
         self.assertEqual(submission.status, first_phase)
 
diff --git a/hypha/apply/funds/tests/test_views.py b/hypha/apply/funds/tests/test_views.py
index 19a51b30aabc8a4cc2aed0869b55f77731755d6e..3c7256f049be6afb349cf50801a8ea6bc693b822 100644
--- a/hypha/apply/funds/tests/test_views.py
+++ b/hypha/apply/funds/tests/test_views.py
@@ -40,7 +40,7 @@ from hypha.apply.utils.testing import make_request
 from hypha.apply.utils.testing.tests import BaseViewTestCase
 
 from ..models import ApplicationRevision, ApplicationSubmission
-from ..views import SubmissionDetailSimplifiedView
+from ..views import SubmissionDetailSimplifiedView, SubmissionDetailView
 from .factories import CustomFormFieldsFactory
 
 
@@ -432,6 +432,29 @@ class TestStaffSubmissionView(BaseSubmissionViewTestCase):
         DeterminationFactory(submission=submission, author=self.user, accepted=True, submitted=False)
         assert_view_determination_not_displayed(submission)
 
+    def test_cant_see_application_draft_status(self):
+        factory = RequestFactory()
+        submission = ApplicationSubmissionFactory(status='draft')
+        ProjectFactory(submission=submission)
+
+        request = factory.get(f'/submission/{submission.pk}')
+        request.user = StaffFactory()
+
+        with self.assertRaises(Http404):
+            SubmissionDetailView.as_view()(request, pk=submission.pk)
+
+    def test_applicant_can_see_application_draft_status(self):
+        factory = RequestFactory()
+        user = ApplicantFactory()
+        submission = ApplicationSubmissionFactory(status='draft', user=user)
+        ProjectFactory(submission=submission)
+
+        request = factory.get(f'/submission/{submission.pk}')
+        request.user = user
+
+        response = SubmissionDetailView.as_view()(request, pk=submission.pk)
+        self.assertEqual(response.status_code, 200)
+
 
 class TestReviewersUpdateView(BaseSubmissionViewTestCase):
     user_factory = StaffFactory
diff --git a/hypha/apply/funds/views.py b/hypha/apply/funds/views.py
index 637dc91d84d573a42a7efac298eedff05bfb6fcd..a7fec42b8d8222f887ebb4f8d7c2752c83427748 100644
--- a/hypha/apply/funds/views.py
+++ b/hypha/apply/funds/views.py
@@ -94,6 +94,7 @@ from .tables import (
     SummarySubmissionsTable,
 )
 from .workflow import (
+    DRAFT_STATE,
     INITIAL_STATE,
     PHASES_MAPPING,
     STAGE_CHANGE_ACTIONS,
@@ -143,7 +144,7 @@ class UpdateReviewersMixin:
             action = None
             if submission.status == INITIAL_STATE:
                 # Automatically transition the application to "Internal review".
-                action = submission.workflow.stepped_phases[1][0].name
+                action = submission.workflow.stepped_phases[2][0].name
             elif submission.status == 'proposal_discussion':
                 # Automatically transition the proposal to "Internal review".
                 action = 'proposal_internal_review'
@@ -186,7 +187,7 @@ class BaseAdminSubmissionsTable(SingleTableMixin, FilterView):
         return new_kwargs
 
     def get_queryset(self):
-        return self.filterset_class._meta.model.objects.current().for_table(self.request.user)
+        return self.filterset_class._meta.model.objects.exclude_draft().current().for_table(self.request.user)
 
     def get_context_data(self, **kwargs):
         search_term = self.request.GET.get('query')
@@ -703,6 +704,8 @@ class AdminSubmissionDetailView(ReviewContextMixin, ActivityContextMixin, Delega
 
     def dispatch(self, request, *args, **kwargs):
         submission = self.get_object()
+        if submission.status == DRAFT_STATE and not request.user == submission.user:
+            raise Http404
         redirect = SubmissionSealedView.should_redirect(request, submission)
         return redirect or super().dispatch(request, *args, **kwargs)
 
@@ -731,6 +734,8 @@ class ReviewerSubmissionDetailView(ReviewContextMixin, ActivityContextMixin, Del
         # Reviewers may sometimes be applicants as well.
         if submission.user == request.user:
             return ApplicantSubmissionDetailView.as_view()(request, *args, **kwargs)
+        if submission.status == DRAFT_STATE:
+            raise Http404
         return super().dispatch(request, *args, **kwargs)
 
 
@@ -751,6 +756,8 @@ class PartnerSubmissionDetailView(ActivityContextMixin, DelegateableView, Detail
         partner_has_access = submission.partners.filter(pk=request.user.pk).exists()
         if not partner_has_access:
             raise PermissionDenied
+        if submission.status == DRAFT_STATE:
+            raise Http404
         return super().dispatch(request, *args, **kwargs)
 
 
@@ -768,6 +775,8 @@ class CommunitySubmissionDetailView(ReviewContextMixin, ActivityContextMixin, De
         # Only allow community reviewers in submission with a community review state.
         if not submission.community_review:
             raise PermissionDenied
+        if submission.status == DRAFT_STATE:
+            raise Http404
         return super().dispatch(request, *args, **kwargs)
 
 
@@ -938,7 +947,7 @@ class ApplicantSubmissionEditView(BaseSubmissionEditView):
                 user=self.request.user,
                 source=self.object,
             )
-        elif revision:
+        elif revision and not self.object.status == DRAFT_STATE:
             messenger(
                 MESSAGES.APPLICANT_EDIT,
                 request=self.request,
@@ -957,7 +966,7 @@ class ApplicantSubmissionEditView(BaseSubmissionEditView):
                 transition.target,
                 self.request.user,
                 request=self.request,
-                notify=not (revision or submitting_proposal),  # Use the other notification
+                notify=not (revision or submitting_proposal) or self.object.status == DRAFT_STATE,  # Use the other notification
             )
 
         return HttpResponseRedirect(self.get_success_url())
diff --git a/hypha/apply/funds/workflow.py b/hypha/apply/funds/workflow.py
index d9888b11af49210096b49662c33b40ad30229f25..849485f0bd33767da09ef49e584fa94073dc2290 100644
--- a/hypha/apply/funds/workflow.py
+++ b/hypha/apply/funds/workflow.py
@@ -190,10 +190,25 @@ Concept = Stage('Concept', False)
 
 Proposal = Stage('Proposal', True)
 
+DRAFT_STATE = 'draft'
 
 INITIAL_STATE = 'in_discussion'
 
 SingleStageDefinition = [
+    {
+        DRAFT_STATE: {
+            'transitions': {
+                INITIAL_STATE: {
+                    'display': 'Submit',
+                    'permissions': {UserPermissions.APPLICANT},
+                    'method': 'create_revision',
+                },
+            },
+            'display': 'Draft',
+            'stage': Request,
+            'permissions': applicant_edit_permissions,
+        }
+    },
     {
         INITIAL_STATE: {
             'transitions': {
@@ -293,6 +308,20 @@ SingleStageDefinition = [
 ]
 
 SingleStageExternalDefinition = [
+    {
+        DRAFT_STATE: {
+            'transitions': {
+                INITIAL_STATE: {
+                    'display': 'Submit',
+                    'permissions': {UserPermissions.APPLICANT},
+                    'method': 'create_revision',
+                },
+            },
+            'display': 'Draft',
+            'stage': RequestExt,
+            'permissions': applicant_edit_permissions,
+        }
+    },
     {
         INITIAL_STATE: {
             'transitions': {
@@ -423,6 +452,20 @@ SingleStageExternalDefinition = [
 
 
 SingleStageCommunityDefinition = [
+    {
+        DRAFT_STATE: {
+            'transitions': {
+                INITIAL_STATE: {
+                    'display': 'Submit',
+                    'permissions': {UserPermissions.APPLICANT},
+                    'method': 'create_revision',
+                },
+            },
+            'display': 'Draft',
+            'stage': RequestCom,
+            'permissions': applicant_edit_permissions,
+        }
+    },
     {
         INITIAL_STATE: {
             'transitions': {
@@ -577,6 +620,20 @@ SingleStageCommunityDefinition = [
 
 
 DoubleStageDefinition = [
+    {
+        DRAFT_STATE: {
+            'transitions': {
+                INITIAL_STATE: {
+                    'display': 'Submit',
+                    'permissions': {UserPermissions.APPLICANT},
+                    'method': 'create_revision',
+                },
+            },
+            'display': 'Draft',
+            'stage': Concept,
+            'permissions': applicant_edit_permissions,
+        }
+    },
     {
         INITIAL_STATE: {
             'transitions': {
diff --git a/hypha/apply/review/views.py b/hypha/apply/review/views.py
index d4ed8a4dbc0d158b6a5559b87ff76482f5fffc0e..06526cf6863c8dcbd92d7ac6399aa517cb7f33f1 100644
--- a/hypha/apply/review/views.py
+++ b/hypha/apply/review/views.py
@@ -187,13 +187,13 @@ def review_workflow_actions(request, submission):
     action = None
     if submission.status == INITIAL_STATE:
         # Automatically transition the application to "Internal review".
-        action = submission_stepped_phases[1][0].name
+        action = submission_stepped_phases[2][0].name
     elif submission.status == 'proposal_discussion':
         # Automatically transition the proposal to "Internal review".
         action = 'proposal_internal_review'
-    elif submission.status == submission_stepped_phases[1][0].name and submission.reviews.count() > 1:
+    elif submission.status == submission_stepped_phases[2][0].name and submission.reviews.count() > 1:
         # Automatically transition the application to "Ready for discussion".
-        action = submission_stepped_phases[2][0].name
+        action = submission_stepped_phases[3][0].name
     elif submission.status == 'ext_external_review' and submission.reviews.by_reviewers().count() > 1:
         # Automatically transition the application to "Ready for discussion".
         action = 'ext_post_external_review_discussion'
diff --git a/hypha/static_src/src/javascript/apply/submission-form-copy.js b/hypha/static_src/src/javascript/apply/submission-form-copy.js
index c18fce9c441e9f56463ef7fe0c297ecbcf9865be..a824fa4a48f8fe74c462f2148e131bf218e5e0a7 100644
--- a/hypha/static_src/src/javascript/apply/submission-form-copy.js
+++ b/hypha/static_src/src/javascript/apply/submission-form-copy.js
@@ -81,7 +81,7 @@
             .attr('title', 'Copies all the questions and user input to the clipboard in plain text.');
         var $application_form = $('.application-form');
         $button.clone().css({'display': 'block', 'margin-left': 'auto'}).insertBefore($application_form);
-        $button.insertAfter($application_form.find('.link--button-secondary').last());
+        $button.css({'margin-left': '20px'}).insertAfter($application_form.find('button').last());
 
         $('.js-clipboard-button').on('click', function (e) {
             e.preventDefault();