diff --git a/app/models/member.rb b/app/models/member.rb
index 25dae51840661514cc928cb6b9440076a440baa2..feb0b66f63c1a2b2a32c85ba6e013a9d395c6063 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -513,7 +513,7 @@ def access_level_inclusion
   end
 
   def send_invite
-    # override in subclass
+    run_after_commit_or_now { notification_service.invite_member(self, @raw_invite_token) }
   end
 
   def send_request
diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb
index 71e5ea6de4850a746c718f14f00029922557f0a0..0e70f9973d6bc7c644545f6286510f0566f13d8b 100644
--- a/app/models/members/group_member.rb
+++ b/app/models/members/group_member.rb
@@ -88,12 +88,6 @@ def refresh_member_authorized_projects
     super
   end
 
-  def send_invite
-    run_after_commit_or_now { notification_service.invite_group_member(self, @raw_invite_token) }
-
-    super
-  end
-
   def post_create_hook
     run_after_commit_or_now { notification_service.new_group_member(self) }
 
diff --git a/app/models/members/project_member.rb b/app/models/members/project_member.rb
index f52fef9e2477ebee8ac113dc75e9d979b86022db..e442fc969d0864aa9c594665facf63663a65158e 100644
--- a/app/models/members/project_member.rb
+++ b/app/models/members/project_member.rb
@@ -116,12 +116,6 @@ def set_member_namespace_id
     self.member_namespace_id = project&.project_namespace_id
   end
 
-  def send_invite
-    run_after_commit_or_now { notification_service.invite_project_member(self, @raw_invite_token) }
-
-    super
-  end
-
   def post_create_hook
     # The creator of a personal project gets added as a `ProjectMember`
     # with `OWNER` access during creation of a personal project,
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index 5099272a2121859cdf636f1d325e1c263889ec04..95d291b9bd63948cf528e4bb4a94578182420445 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -522,11 +522,8 @@ def decline_invite(member)
     ).deliver_later
   end
 
-  # Project invite
-  def invite_project_member(project_member, token)
-    return true unless project_member.notifiable?(:subscription)
-
-    mailer.member_invited_email(project_member.real_source_type, project_member.id, token).deliver_later
+  def invite_member(member, token)
+    mailer.member_invited_email(member.real_source_type, member.id, token).deliver_later
   end
 
   def accept_project_invite(project_member)
@@ -553,11 +550,6 @@ def member_about_to_expire(member)
     mailer.member_about_to_expire_email(member.real_source_type, member.id).deliver_later
   end
 
-  # Group invite
-  def invite_group_member(group_member, token)
-    mailer.member_invited_email(group_member.real_source_type, group_member.id, token).deliver_later
-  end
-
   def invite_member_reminder(group_member, token, reminder_index)
     mailer.member_invited_reminder_email(group_member.real_source_type, group_member.id, token, reminder_index).deliver_later
   end
diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb
index db2ae319bc9017a75305a413dd7a4502d91559ee..32303f766841a0e8f59d3b8ae772a698ccc44d50 100644
--- a/spec/models/member_spec.rb
+++ b/spec/models/member_spec.rb
@@ -735,6 +735,30 @@
     it { is_expected.to respond_to(:user_email) }
   end
 
+  describe 'callbacks' do
+    describe '#send_invite' do
+      context 'with an invited group member' do
+        it 'sends an invite email' do
+          expect_next_instance_of(NotificationService) do |instance|
+            expect(instance).to receive(:invite_member)
+          end
+
+          create(:group_member, :invited)
+        end
+      end
+
+      context 'with an uninvited member' do
+        it 'does not send an invite email' do
+          expect_next_instance_of(NotificationService) do |instance|
+            expect(instance).not_to receive(:invite_member)
+          end
+
+          create(:group_member)
+        end
+      end
+    end
+  end
+
   describe '.valid_email?' do
     it 'is a valid email format' do
       expect(described_class.valid_email?('foo')).to eq(false)
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 40597c30c4a6d5723f7d722614ea6cc49aae714e..0012cc112c1e8695babb7609cfe4b4957e0d39c8 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -3179,6 +3179,22 @@ def commit_to_hash(commit)
     end
   end
 
+  describe '#invite_member' do
+    let_it_be(:group_member) { create(:group_member) }
+
+    subject(:invite_member) { notification.invite_member(group_member, 'token') }
+
+    it 'sends exactly one email' do
+      expect(Notify)
+        .to receive(:member_invited_email).with('Group', group_member.id, 'token').at_least(:once).and_call_original
+
+      invite_member
+
+      expect_delivery_jobs_count(1)
+      expect_enqueud_email('Group', group_member.id, 'token', mail: 'member_invited_email')
+    end
+  end
+
   describe '#new_instance_access_request', :deliver_mails_inline do
     let_it_be(:user) { create(:user, :blocked_pending_approval) }
     let_it_be(:admins) { create_list(:admin, 12, :with_sign_ins) }