diff --git a/.rubocop_todo/qa/fabricate_usage.yml b/.rubocop_todo/qa/fabricate_usage.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4560204b84c9e08d41700375d4253ee91247f191
--- /dev/null
+++ b/.rubocop_todo/qa/fabricate_usage.yml
@@ -0,0 +1,36 @@
+---
+QA/FabricateUsage:
+  Include:
+    - 'qa/qa/specs/**/*_spec.rb'
+  Details: grace period
+  Exclude:
+    - 'qa/qa/specs/features/api/3_create/repository/default_branch_name_setting_spec.rb'
+    - 'qa/qa/specs/features/ee/api/7_configure/kubernetes/kubernetes_agent_spec.rb'
+    - 'qa/qa/specs/features/ee/browser_ui/10_govern/create_merge_request_with_secure_spec.rb'
+    - 'qa/qa/specs/features/ee/browser_ui/10_govern/dismissed_vulnerabilities_in_security_widget_spec.rb'
+    - 'qa/qa/specs/features/ee/browser_ui/10_govern/export_vulnerability_report_spec.rb'
+    - 'qa/qa/specs/features/ee/browser_ui/10_govern/fix_vulnerability_workflow_spec.rb'
+    - 'qa/qa/specs/features/ee/browser_ui/10_govern/group/group_audit_event_streaming_spec.rb'
+    - 'qa/qa/specs/features/ee/browser_ui/10_govern/project/project_audit_logs_spec.rb'
+    - 'qa/qa/specs/features/ee/browser_ui/10_govern/project_security_dashboard_spec.rb'
+    - 'qa/qa/specs/features/ee/browser_ui/10_govern/scan_result_policy_vulnerabilities_spec.rb'
+    - 'qa/qa/specs/features/ee/browser_ui/10_govern/security_reports_spec.rb'
+    - 'qa/qa/specs/features/ee/browser_ui/10_govern/vulnerabilities_jira_integration_spec.rb'
+    - 'qa/qa/specs/features/ee/browser_ui/10_govern/vulnerability_management_spec.rb'
+    - 'qa/qa/specs/features/ee/browser_ui/10_govern/vulnerability_security_training_spec.rb'
+    - 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/purchase/purchase_ci_spec.rb'
+    - 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/purchase/purchase_storage_spec.rb'
+    - 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/purchase/upgrade_group_spec.rb'
+    - 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/saas_user_limit_experience_spec.rb'
+    - 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/utilization/free_namespace_storage_spec.rb'
+    - 'qa/qa/specs/features/ee/browser_ui/12_systems/geo/attachment_replication_spec.rb'
+    - 'qa/qa/specs/features/ee/browser_ui/12_systems/geo/database_delete_replication_spec.rb'
+    - 'qa/qa/specs/features/ee/browser_ui/12_systems/geo/geo_replication_ci_job_log_artifacts_spec.rb'
+    - 'qa/qa/specs/features/ee/browser_ui/12_systems/geo/geo_replication_maven_package_spec.rb'
+    - 'qa/qa/specs/features/ee/browser_ui/12_systems/geo/geo_replication_npm_registry_spec.rb'
+    - 'qa/qa/specs/features/ee/browser_ui/12_systems/geo/http_push_spec.rb'
+    - 'qa/qa/specs/features/ee/browser_ui/12_systems/geo/http_push_to_secondary_spec.rb'
+    - 'qa/qa/specs/features/ee/browser_ui/12_systems/geo/rename_replication_spec.rb'
+    - 'qa/qa/specs/features/ee/browser_ui/12_systems/geo/ssh_push_spec.rb'
+    - 'qa/qa/specs/features/ee/browser_ui/12_systems/geo/ssh_push_to_secondary_spec.rb'
+    - 'qa/qa/specs/features/ee/browser_ui/3_create/remote_development/workspace_actions_spec.rb'
diff --git a/rubocop/cop/qa/fabricate_usage.rb b/rubocop/cop/qa/fabricate_usage.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1787df0e7dfe0781ee68e729b6da3dbc8bf67ac2
--- /dev/null
+++ b/rubocop/cop/qa/fabricate_usage.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+require_relative '../../qa_helpers'
+
+module RuboCop
+  module Cop
+    module QA
+      # This cop checks for the usages of API fabrications and suggests using
+      # the corresponding QA factories instead.
+      # @example
+      #   Good:
+      #     create(:project)
+      #   Bad:
+      #     Resource::Project.fabricate_via_api!
+      # @example
+      #   Good:
+      #     create(:group)
+      #   Bad:
+      #     Resource::Group.fabricate_via_api!
+      # @example
+      #   Good:
+      #     create(:user, username: 'username', password: 'password')
+      #   Bad:
+      #     Resource::User.fabricate_via_api! do |user|
+      #       user.username = 'username'
+      #       user.password = 'password'
+      #     end
+      class FabricateUsage < RuboCop::Cop::Base
+        MESSAGE = "Prefer create(:%{factory}[, ...]) here."
+        RESOURCES_TO_CHECK = {
+          'Resource::Project' => :project,
+          'Resource::Group' => :group,
+          'Resource::Issue' => :issue,
+          'Resource::User' => :user,
+          'Resource::Pipeline' => :pipeline,
+          'Resource::Job' => :job,
+          'Resource::File' => :file,
+          'Resource::GroupAccessToken' => :group_access_token,
+          'Resource::ProjectAccessToken' => :project_access_token,
+          'Resource::GroupLabel' => :group_label,
+          'Resource::ProjectLabel' => :project_label,
+          'Resource::GroupRunner' => :group_runner,
+          'Resource::ProjectRunner' => :project_runner,
+          'Resource::GroupMilestone' => :group_milestone,
+          'Resource::ProjectMilestone' => :project_milestone,
+          'Resource::Snippet' => :snippet,
+          'Resource::ProjectSnippet' => :project_snippet
+        }.freeze
+
+        RESTRICT_ON_SEND = %i[fabricate_via_api!].freeze
+
+        def_node_matcher :const_receiver, <<~PATTERN
+          (send $const ...)
+        PATTERN
+
+        def on_send(node)
+          factory = RESOURCES_TO_CHECK[const_receiver(node)&.const_name]
+          return unless factory
+
+          add_offense(node, message: format(MESSAGE, factory: factory))
+        end
+      end
+    end
+  end
+end
diff --git a/spec/rubocop/cop/qa/fabricate_usage_spec.rb b/spec/rubocop/cop/qa/fabricate_usage_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7c4b42b91e0e7dae7eb4e241aeb607d80b988846
--- /dev/null
+++ b/spec/rubocop/cop/qa/fabricate_usage_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'rubocop_spec_helper'
+
+require_relative '../../../../rubocop/cop/qa/fabricate_usage'
+
+RSpec.describe RuboCop::Cop::QA::FabricateUsage, feature_category: :quality_management do
+  let(:source_file) { 'qa/qa/specs/spec.rb' }
+
+  it 'registers an offense when using fabricate_via_api! for a valid resource' do
+    expect_offense(<<~RUBY)
+      Resource::Project.fabricate_via_api! do |project|
+      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer create(:project[, ...]) here.
+        project.name = 'test'
+      end
+    RUBY
+  end
+
+  it 'registers an offense for groups' do
+    expect_offense(<<~RUBY)
+      Resource::Group.fabricate_via_api! do |group|
+      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer create(:group[, ...]) here.
+        group.path = 'test'
+      end
+    RUBY
+  end
+
+  it 'does not register an offense when using fabricate_via_api! for an unenforced resource' do
+    expect_no_offenses(<<~RUBY)
+      Resource::Invalid.fabricate_via_api! do |project|
+        project.name = 'test'
+      end
+    RUBY
+  end
+end