diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index ab5c136fee3844aca7647dffa7e2eea9364f26bc..7136c1b4692b75e5ee5b5834d75ca31bec2c8d72 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -181,10 +181,18 @@ def edit_group_origin_location
 
   def destroy
     Groups::DestroyService.new(@group, current_user).async_execute
+    message = format(_("Group '%{group_name}' is being deleted."), group_name: @group.full_name)
 
-    flash[:toast] = format(_("Group '%{group_name}' is being deleted."), group_name: @group.full_name)
+    respond_to do |format|
+      format.html do
+        flash[:toast] = message
+        redirect_to root_path, status: :found
+      end
 
-    redirect_to root_path, status: :found
+      format.json do
+        render json: { message: message }
+      end
+    end
   end
 
   # rubocop: disable CodeReuse/ActiveRecord
diff --git a/ee/app/controllers/ee/groups_controller.rb b/ee/app/controllers/ee/groups_controller.rb
index 26eeb999615bb3d6476367ecce2d5c5d42c09793..d3d43a64d3f6218f30040415848015a0fee86c27 100644
--- a/ee/app/controllers/ee/groups_controller.rb
+++ b/ee/app/controllers/ee/groups_controller.rb
@@ -49,9 +49,31 @@ def destroy
       result = ::Groups::MarkForDeletionService.new(group, current_user).execute
 
       if result[:status] == :success
-        redirect_to group_path(group), status: :found
+        respond_to do |format|
+          format.html do
+            redirect_to group_path(group), status: :found
+          end
+
+          format.json do
+            render json: {
+              message: format(
+                _("'%{group_name}' has been scheduled for deletion and will be deleted on %{date}."),
+                group_name: group.name,
+                date: permanent_deletion_date_formatted(group, group.marked_for_deletion_on)
+              )
+            }
+          end
+        end
       else
-        redirect_to edit_group_path(group), status: :found, alert: result[:message]
+        respond_to do |format|
+          format.html do
+            redirect_to edit_group_path(group), status: :found, alert: result[:message]
+          end
+
+          format.json do
+            render json: { message: result[:message] }, status: :unprocessable_entity
+          end
+        end
       end
     end
 
@@ -71,9 +93,17 @@ def restore
 
     def check_subscription!
       if group.linked_to_subscription?
-        redirect_to edit_group_path(group),
-          status: :found,
-          alert: _('This group is linked to a subscription')
+        respond_to do |format|
+          format.html do
+            redirect_to edit_group_path(group),
+              status: :found,
+              alert: _('This group is linked to a subscription')
+          end
+
+          format.json do
+            render json: { message: _('This group is linked to a subscription') }, status: :unprocessable_entity
+          end
+        end
       end
     end
 
diff --git a/ee/spec/controllers/ee/groups_controller_spec.rb b/ee/spec/controllers/ee/groups_controller_spec.rb
index bc6270278ced93e56935dd55b5a15782f2299773..cdee780de6088efeffab6096c04d18a4fc3a451f 100644
--- a/ee/spec/controllers/ee/groups_controller_spec.rb
+++ b/ee/spec/controllers/ee/groups_controller_spec.rb
@@ -246,7 +246,10 @@
   end
 
   describe 'DELETE #destroy' do
-    subject { delete :destroy, params: { id: group.to_param } }
+    let(:format) { :html }
+    let(:params) { {} }
+
+    subject { delete :destroy, format: format, params: { id: group.to_param, **params } }
 
     before do
       group.add_owner(user)
@@ -273,10 +276,22 @@
             end
           end
 
-          it 'redirects to group path' do
-            subject
+          context 'for a html request' do
+            it 'redirects to group path' do
+              subject
 
-            expect(response).to redirect_to(group_path(group))
+              expect(response).to redirect_to(group_path(group))
+            end
+          end
+
+          context 'for a json request', :freeze_time do
+            let(:format) { :json }
+
+            it 'returns json with message' do
+              subject
+
+              expect(json_response['message']).to eq("'#{group.name}' has been scheduled for deletion and will be deleted on #{permanent_deletion_date_formatted(group, group.marked_for_deletion_on)}.")
+            end
           end
         end
 
@@ -289,11 +304,23 @@
             expect { subject }.not_to change { group.reload.marked_for_deletion? }.from(false)
           end
 
-          it 'redirects to group edit page' do
-            subject
+          context 'for a html request' do
+            it 'redirects to group edit page' do
+              subject
 
-            expect(response).to redirect_to(edit_group_path(group))
-            expect(flash[:alert]).to include 'error'
+              expect(response).to redirect_to(edit_group_path(group))
+              expect(flash[:alert]).to include 'error'
+            end
+          end
+
+          context 'for a json request' do
+            let(:format) { :json }
+
+            it 'returns json with message' do
+              subject
+
+              expect(json_response['message']).to eq("error")
+            end
           end
         end
 
@@ -303,39 +330,108 @@
           end
 
           context 'when permanently_remove param is set' do
-            it 'deletes the group immediately' do
-              expect(GroupDestroyWorker).to receive(:perform_async)
+            let(:params) { { permanently_remove: true } }
 
-              delete :destroy, params: { id: group.to_param, permanently_remove: true }
+            context 'for a html request' do
+              it 'deletes the group immediately and redirects to root path' do
+                expect(GroupDestroyWorker).to receive(:perform_async)
 
-              expect(response).to redirect_to(root_path)
-              expect(flash[:toast]).to include "Group '#{group.name}' is being deleted."
+                subject
+
+                expect(response).to redirect_to(root_path)
+                expect(flash[:toast]).to include "Group '#{group.name}' is being deleted."
+              end
+            end
+
+            context 'for a json request' do
+              let(:format) { :json }
+
+              it 'deletes the group immediately and returns json with message' do
+                expect(GroupDestroyWorker).to receive(:perform_async)
+
+                subject
+
+                expect(json_response['message']).to eq("Group '#{group.name}' is being deleted.")
+              end
             end
           end
 
           context 'when permanently_remove param is not set' do
-            it 'does nothing' do
-              subject
+            context 'for a html request' do
+              it 'redirects to edit path with error' do
+                subject
 
-              expect(response).to redirect_to(edit_group_path(group))
-              expect(flash[:alert]).to include "Group has been already marked for deletion"
+                expect(response).to redirect_to(edit_group_path(group))
+                expect(flash[:alert]).to include "Group has been already marked for deletion"
+              end
+            end
+
+            context 'for a json request' do
+              let(:format) { :json }
+
+              it 'returns json with message' do
+                subject
+
+                expect(json_response['message']).to eq("Group has been already marked for deletion")
+              end
             end
           end
         end
       end
 
-      context 'delayed deletion feature is not available' do
+      context 'delayed deletion feature is not available', :sidekiq_inline do
         before do
           stub_licensed_features(adjourned_deletion_for_projects_and_groups: false)
         end
 
-        it 'immediately schedules a group destroy and redirects to root page with alert about immediate deletion' do
-          Sidekiq::Testing.fake! do
-            expect { subject }.to change { GroupDestroyWorker.jobs.size }.by(1)
+        context 'for a html request' do
+          it 'immediately schedules a group destroy and redirects to root page with alert about immediate deletion' do
+            Sidekiq::Testing.fake! do
+              expect { subject }.to change { GroupDestroyWorker.jobs.size }.by(1)
+            end
+
+            expect(response).to redirect_to(root_path)
+            expect(flash[:toast]).to include "Group '#{group.name}' is being deleted."
+          end
+        end
+
+        context 'for a json request' do
+          let(:format) { :json }
+
+          it 'immediately schedules a group destroy and returns json with message' do
+            Sidekiq::Testing.fake! do
+              expect { subject }.to change { GroupDestroyWorker.jobs.size }.by(1)
+            end
+
+            expect(json_response['message']).to eq("Group '#{group.name}' is being deleted.")
+          end
+        end
+      end
+
+      context 'when group is linked to a subscription', :saas do
+        let_it_be(:group_with_plan) do
+          create(:group_with_plan, plan: :ultimate_plan, owners: user, organization: current_organization)
+        end
+
+        let(:params) { { id: group_with_plan.to_param } }
+
+        context 'for a html request' do
+          it 'redirects to edit page with alert' do
+            subject
+
+            expect(response).to redirect_to(edit_group_path(group_with_plan))
+            expect(flash[:alert]).to eq 'This group is linked to a subscription'
           end
+        end
+
+        context 'for a json request' do
+          let(:format) { :json }
 
-          expect(response).to redirect_to(root_path)
-          expect(flash[:toast]).to include "Group '#{group.name}' is being deleted."
+          it 'returns json with message' do
+            subject
+
+            expect(json_response['message']).to eq('This group is linked to a subscription')
+          end
         end
       end
     end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index b082b5f0a97a228bb37b2c084365bc9a571d0a6b..2ef75e7d417e2b829c4e7dc079b322ad72d5ce32 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -1639,6 +1639,9 @@ msgstr ""
 msgid "%{wildcards_link_start}Wildcards%{wildcards_link_end} such as %{code_tag_start}v*%{code_tag_end} or %{code_tag_start}*-release%{code_tag_end} are supported."
 msgstr ""
 
+msgid "'%{group_name}' has been scheduled for deletion and will be deleted on %{date}."
+msgstr ""
+
 msgid "'%{group_name}' has been scheduled for removal on %{removal_time}."
 msgstr ""
 
diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb
index 7a98c8eae04dd1a4b71b6ba49d7fdf85b19cb0b1..7f4d6bbf89f4b686b7d8419167ed2dc0a6851cf5 100644
--- a/spec/controllers/groups_controller_spec.rb
+++ b/spec/controllers/groups_controller_spec.rb
@@ -545,12 +545,23 @@
         sign_in(user)
       end
 
-      it 'schedules a group destroy and redirects to the root path' do
-        Sidekiq::Testing.fake! do
-          expect { delete :destroy, params: { id: group.to_param } }.to change(GroupDestroyWorker.jobs, :size).by(1)
+      context 'for a html request' do
+        it 'schedules a group destroy and redirects to the root path' do
+          Sidekiq::Testing.fake! do
+            expect { delete :destroy, params: { id: group.to_param } }.to change(GroupDestroyWorker.jobs, :size).by(1)
+          end
+          expect(flash[:toast]).to eq(format(_("Group '%{group_name}' is being deleted."), group_name: group.full_name))
+          expect(response).to redirect_to(root_path)
+        end
+      end
+
+      context 'for a json request' do
+        it 'schedules a group destroy and returns message' do
+          Sidekiq::Testing.fake! do
+            expect { delete :destroy, format: :json, params: { id: group.to_param } }.to change(GroupDestroyWorker.jobs, :size).by(1)
+          end
+          expect(Gitlab::Json.parse(response.body)).to eq({ 'message' => "Group '#{group.full_name}' is being deleted." })
         end
-        expect(flash[:toast]).to eq(format(_("Group '%{group_name}' is being deleted."), group_name: group.full_name))
-        expect(response).to redirect_to(root_path)
       end
     end
   end