diff --git a/.rubocop_todo/rspec/be_eq.yml b/.rubocop_todo/rspec/be_eq.yml
index 7cd0920d3f8028acc178631c1065396781dea4f7..a7a667947ce03c42db686fc266c682b40696771e 100644
--- a/.rubocop_todo/rspec/be_eq.yml
+++ b/.rubocop_todo/rspec/be_eq.yml
@@ -92,7 +92,6 @@ RSpec/BeEq:
     - 'ee/spec/lib/arkose/verify_response_spec.rb'
     - 'ee/spec/lib/bulk_imports/groups/pipelines/epics_pipeline_spec.rb'
     - 'ee/spec/lib/bulk_imports/groups/pipelines/iterations_cadences_pipeline_spec.rb'
-    - 'ee/spec/lib/code_suggestions/completions_model_details_spec.rb'
     - 'ee/spec/lib/code_suggestions/tasks/base_spec.rb'
     - 'ee/spec/lib/code_suggestions/tasks/code_completion_spec.rb'
     - 'ee/spec/lib/code_suggestions/tasks/code_generation_spec.rb'
diff --git a/ee/app/models/concerns/ai/user_authorizable.rb b/ee/app/models/concerns/ai/user_authorizable.rb
index cd09509b11b06ddb24474a8564208cfde561e1a7..c4ec64fd079a6a36e0e0b3097a5e61b9984831f3 100644
--- a/ee/app/models/concerns/ai/user_authorizable.rb
+++ b/ee/app/models/concerns/ai/user_authorizable.rb
@@ -134,6 +134,12 @@ def allowed_to_use?(ai_feature, service_name: nil, licensed_feature: :ai_feature
         end
       end
 
+      def allowed_by_namespace_ids(*args, **kwargs)
+        allowed_to_use?(*args, **kwargs) { |namespace_ids| return namespace_ids } # rubocop:disable Cop/AvoidReturnFromBlocks -- Early return if namespace ids are yielded
+
+        []
+      end
+
       private
 
       def licensed_to_use_in_com?(maturity)
diff --git a/ee/lib/api/code_suggestions.rb b/ee/lib/api/code_suggestions.rb
index 2745c70c7748b336676596b65209b39af44695d0..c3c6f034254ca48655691bc085c98cf655e82d17 100644
--- a/ee/lib/api/code_suggestions.rb
+++ b/ee/lib/api/code_suggestions.rb
@@ -30,8 +30,10 @@ def model_gateway_headers(headers, service)
         ).merge(saas_headers).transform_values { |v| Array(v) }
       end
 
-      def connector_public_headers
-        Gitlab::CloudConnector.ai_headers(current_user)
+      def connector_public_headers(service_name)
+        namespace_ids = current_user.allowed_by_namespace_ids(service_name)
+
+        Gitlab::CloudConnector.ai_headers(current_user, namespace_ids: namespace_ids)
           .merge(saas_headers)
           .merge('X-Gitlab-Authentication-Type' => 'oidc')
       end
@@ -181,7 +183,7 @@ def file_too_large_with_origin_header!
             # https://gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist/-/issues/429
             token: token[:token],
             expires_at: token[:expires_at],
-            headers: connector_public_headers
+            headers: connector_public_headers(completion_model_details.feature_name)
           }.tap do |a|
             a[:model_details] = details_hash unless details_hash.blank?
           end
diff --git a/ee/lib/code_suggestions/completions_model_details.rb b/ee/lib/code_suggestions/completions_model_details.rb
index 28ebd3098180e7f51f3e2783c5641d6ff2a53253..103e0e2bee412b038005916fa1e984ac95ce1dfe 100644
--- a/ee/lib/code_suggestions/completions_model_details.rb
+++ b/ee/lib/code_suggestions/completions_model_details.rb
@@ -1,11 +1,11 @@
 # frozen_string_literal: true
 
 module CodeSuggestions
-  class CompletionsModelDetails
+  class CompletionsModelDetails < ModelDetails
     FEATURE_SETTING_NAME = 'code_completions'
 
     def initialize(current_user:)
-      @current_user = current_user
+      super(current_user: current_user, feature_setting_name: FEATURE_SETTING_NAME)
     end
 
     def current_model
@@ -21,14 +21,8 @@ def current_model
       {}
     end
 
-    def feature_disabled?
-      !!feature_setting&.disabled?
-    end
-
     private
 
-    attr_reader :current_user
-
     def codestral_model_details
       {
         model_provider: CodeSuggestions::Prompts::CodeCompletion::VertexCodestral::MODEL_PROVIDER,
@@ -43,10 +37,6 @@ def fireworks_qwen_2_5_model_details
       }
     end
 
-    def self_hosted?
-      feature_setting&.self_hosted?
-    end
-
     def use_codestral_for_code_completions?
       Feature.enabled?(:use_codestral_for_code_completions, current_user, type: :beta)
     end
@@ -54,9 +44,5 @@ def use_codestral_for_code_completions?
     def use_fireworks_qwen_for_code_completions?
       Feature.enabled?(:fireworks_qwen_code_completion, current_user, type: :beta)
     end
-
-    def feature_setting
-      @feature_setting ||= ::Ai::FeatureSetting.find_by_feature(FEATURE_SETTING_NAME)
-    end
   end
 end
diff --git a/ee/lib/code_suggestions/model_details.rb b/ee/lib/code_suggestions/model_details.rb
new file mode 100644
index 0000000000000000000000000000000000000000..748f18661df6804df0ae36845bf74e5c2d91569c
--- /dev/null
+++ b/ee/lib/code_suggestions/model_details.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module CodeSuggestions
+  class ModelDetails
+    def initialize(current_user:, feature_setting_name:)
+      @current_user = current_user
+      @feature_setting_name = feature_setting_name
+    end
+
+    def feature_setting
+      @feature_setting ||= ::Ai::FeatureSetting.find_by_feature(feature_setting_name)
+    end
+
+    def base_url
+      feature_setting&.base_url || Gitlab::AiGateway.url
+    end
+
+    def feature_name
+      if self_hosted?
+        :self_hosted_models
+      else
+        :code_suggestions
+      end
+    end
+
+    def feature_disabled?
+      # In case the code suggestions feature is being used via self-hosted models,
+      # it can also be disabled completely. In such cases, this check
+      # can be used to prevent exposing the feature via UI/API.
+      !!feature_setting&.disabled?
+    end
+
+    def self_hosted?
+      feature_setting&.self_hosted?
+    end
+
+    private
+
+    attr_reader :current_user, :feature_setting_name
+  end
+end
diff --git a/ee/lib/code_suggestions/tasks/base.rb b/ee/lib/code_suggestions/tasks/base.rb
index cc16bd2245cbadced099f4de32a4c876e24b71e2..d7b7df777249d9df4e184408ea14649d854a7b6d 100644
--- a/ee/lib/code_suggestions/tasks/base.rb
+++ b/ee/lib/code_suggestions/tasks/base.rb
@@ -5,36 +5,15 @@ module Tasks
     class Base
       AI_GATEWAY_CONTENT_SIZE = 100_000
 
+      delegate :base_url, :self_hosted?, :feature_setting, :feature_name, :feature_disabled?, to: :model_details
+
       def initialize(params: {}, unsafe_passthrough_params: {}, current_user: nil)
-        @feature_setting = ::Ai::FeatureSetting.find_by_feature(feature_setting_name)
+        @model_details = ModelDetails.new(current_user: current_user, feature_setting_name: feature_setting_name)
         @params = params
         @unsafe_passthrough_params = unsafe_passthrough_params
         @current_user = current_user
       end
 
-      def base_url
-        feature_setting&.base_url || Gitlab::AiGateway.url
-      end
-
-      def self_hosted?
-        feature_setting&.self_hosted?
-      end
-
-      def feature_disabled?
-        # In case the code suggestions feature is being used via self-hosted models,
-        # it can also be disabled completely. In such cases, this check
-        # can be used to prevent exposing the feature via UI/API.
-        !!feature_setting&.disabled?
-      end
-
-      def feature_name
-        if self_hosted?
-          :self_hosted_models
-        else
-          :code_suggestions
-        end
-      end
-
       def endpoint
         # TODO: After their migration to AIGW, both generations and
         # completions will use the same `/completions` endpoint in v3.
@@ -56,7 +35,7 @@ def body
 
       private
 
-      attr_reader :params, :unsafe_passthrough_params, :feature_setting, :current_user
+      attr_reader :params, :unsafe_passthrough_params, :model_details, :current_user
 
       def endpoint_name
         raise NotImplementedError
diff --git a/ee/lib/gitlab/ai_gateway.rb b/ee/lib/gitlab/ai_gateway.rb
index f299270502062662d83225f30d0994c76502deb5..d994d29cc105e9426f6691a2c0ff3db401dc0e47 100644
--- a/ee/lib/gitlab/ai_gateway.rb
+++ b/ee/lib/gitlab/ai_gateway.rb
@@ -50,9 +50,7 @@ def self.enabled_feature_flags
     end
 
     def self.headers(user:, service:, agent: nil, lsp_version: nil)
-      allowed_by_namespace_ids = []
-
-      user&.allowed_to_use?(service.name) { |namespace_ids| allowed_by_namespace_ids = namespace_ids }
+      allowed_by_namespace_ids = user&.allowed_by_namespace_ids(service.name) || []
 
       {
         'X-Gitlab-Authentication-Type' => 'oidc',
diff --git a/ee/lib/gitlab/cloud_connector.rb b/ee/lib/gitlab/cloud_connector.rb
index 5ae48bd51733e0015c74a1f4102d10d83ea60eee..79bf389302cecd21123d86ca00fd7bb61a91de1a 100644
--- a/ee/lib/gitlab/cloud_connector.rb
+++ b/ee/lib/gitlab/cloud_connector.rb
@@ -33,7 +33,8 @@ def ai_headers(user, namespace_ids: [])
         namespace_ids: namespace_ids
       )
       headers(user).merge(
-        'X-Gitlab-Duo-Seat-Count' => effective_seat_count.to_s
+        'X-Gitlab-Duo-Seat-Count' => effective_seat_count.to_s,
+        'X-Gitlab-Feature-Enabled-By-Namespace-Ids' => namespace_ids.join(',')
       )
     end
 
diff --git a/ee/spec/lib/code_suggestions/completions_model_details_spec.rb b/ee/spec/lib/code_suggestions/completions_model_details_spec.rb
index 1d2f9f267581eb9c5b8899c9450c50bd9dab3de5..1820a99136b6f4e3e95b3f43bbf6cb611e48c613 100644
--- a/ee/spec/lib/code_suggestions/completions_model_details_spec.rb
+++ b/ee/spec/lib/code_suggestions/completions_model_details_spec.rb
@@ -64,22 +64,4 @@
       end
     end
   end
-
-  describe '#feature_disabled?' do
-    subject(:feature_disabled?) { completions_model_details.feature_disabled? }
-
-    it 'returns false' do
-      expect(feature_disabled?).to eq(false)
-    end
-
-    context 'when code_completions is self-hosted, but set to disabled' do
-      let_it_be(:feature_setting) do
-        create(:ai_feature_setting, provider: :disabled, feature: :code_completions)
-      end
-
-      it 'returns true' do
-        expect(feature_disabled?).to eq(true)
-      end
-    end
-  end
 end
diff --git a/ee/spec/lib/code_suggestions/model_details_spec.rb b/ee/spec/lib/code_suggestions/model_details_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4ab754b45f06867279a4207d4d2f0a27523d026a
--- /dev/null
+++ b/ee/spec/lib/code_suggestions/model_details_spec.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe CodeSuggestions::ModelDetails, feature_category: :code_suggestions do
+  let_it_be(:feature_setting_name) { 'code_completions' }
+  let(:user) { create(:user) }
+  let(:completions_model_details) do
+    described_class.new(current_user: user, feature_setting_name: feature_setting_name)
+  end
+
+  describe '#feature_disabled?' do
+    subject(:feature_disabled?) { completions_model_details.feature_disabled? }
+
+    it 'returns false' do
+      expect(feature_disabled?).to be(false)
+    end
+
+    context 'when the feature is self-hosted, but set to disabled' do
+      let_it_be(:feature_setting) do
+        create(:ai_feature_setting, provider: :disabled, feature: feature_setting_name)
+      end
+
+      it 'returns true' do
+        expect(feature_disabled?).to be(true)
+      end
+    end
+  end
+
+  describe '#base_url' do
+    include_context 'when loading 1_settings initializer'
+
+    # Reload settings to ensure a consistent state
+    # for Settings.cloud_connector base_url
+    # and isolate tests to reduce the risk of flaky tests
+    # due to shared state with other specs
+    before do
+      load_settings
+    end
+
+    it 'returns correct URL' do
+      expect(completions_model_details.base_url).to eql('https://cloud.gitlab.com/ai')
+    end
+
+    context 'when the feature is customized' do
+      let_it_be(:feature_setting) { create(:ai_feature_setting, provider: :vendored) }
+
+      it 'takes the base url from feature settings' do
+        url = "http://localhost:5000"
+        expect(::Gitlab::AiGateway).to receive(:cloud_connector_url).and_return(url)
+
+        expect(completions_model_details.base_url).to eq(url)
+      end
+    end
+  end
+end
diff --git a/ee/spec/lib/code_suggestions/tasks/base_spec.rb b/ee/spec/lib/code_suggestions/tasks/base_spec.rb
index b1944750b29bdf0d4b69404ede3b8db92b544bcc..ed4cd0df5cf8f7795db6baeae3471fb619c9750f 100644
--- a/ee/spec/lib/code_suggestions/tasks/base_spec.rb
+++ b/ee/spec/lib/code_suggestions/tasks/base_spec.rb
@@ -11,33 +11,6 @@ def feature_setting_name
     end
   end
 
-  describe '#base_url' do
-    include_context 'when loading 1_settings initializer'
-
-    # Reload settings to ensure a consistent state
-    # for Settings.cloud_connector base_url
-    # and isolate tests to reduce the risk of flaky tests
-    # due to shared state with other specs
-    before do
-      load_settings
-    end
-
-    it 'returns correct URL' do
-      expect(klass.new.base_url).to eql('https://cloud.gitlab.com/ai')
-    end
-
-    context 'when the feature is customized' do
-      let_it_be(:feature_setting) { create(:ai_feature_setting, provider: :vendored) }
-
-      it 'takes the base url from feature settings' do
-        url = "http://localhost:5000"
-        expect(::Gitlab::AiGateway).to receive(:cloud_connector_url).and_return(url)
-
-        expect(klass.new.base_url).to eq(url)
-      end
-    end
-  end
-
   describe '#endpoint' do
     it 'raies NotImplementedError' do
       expect { klass.new.endpoint }.to raise_error(NotImplementedError)
diff --git a/ee/spec/lib/gitlab/cloud_connector_spec.rb b/ee/spec/lib/gitlab/cloud_connector_spec.rb
index 578ac598a0a9cddaedd92f0c2be4c0a9606ca17d..2017505b48f0da7d852768c36e6c22a527127205 100644
--- a/ee/spec/lib/gitlab/cloud_connector_spec.rb
+++ b/ee/spec/lib/gitlab/cloud_connector_spec.rb
@@ -50,7 +50,10 @@
 
   describe '.ai_headers' do
     let(:expected_headers) do
-      super().merge('X-Gitlab-Duo-Seat-Count' => '0')
+      super().merge(
+        'X-Gitlab-Duo-Seat-Count' => '0',
+        'X-Gitlab-Feature-Enabled-By-Namespace-Ids' => namespace_ids.join(',')
+      )
     end
 
     let(:namespace_ids) { [1, 42] }
diff --git a/ee/spec/models/concerns/ai/user_authorizable_spec.rb b/ee/spec/models/concerns/ai/user_authorizable_spec.rb
index dd5e7f1dcdfa135809f4fa732ee9d7738b02e4ad..1c590fcc844bc8dfa28e0e706c3f6f1bbd9871f8 100644
--- a/ee/spec/models/concerns/ai/user_authorizable_spec.rb
+++ b/ee/spec/models/concerns/ai/user_authorizable_spec.rb
@@ -240,6 +240,30 @@
     end
   end
 
+  describe '#allowed_by_namespace_ids' do
+    let(:ai_feature) { :my_feature }
+
+    subject { user.allowed_by_namespace_ids(ai_feature) }
+
+    context "when allowed_to_use? doesn't yield any value" do
+      before do
+        allow(user).to receive(:allowed_to_use?).with(ai_feature)
+      end
+
+      it { is_expected.to eq([]) }
+    end
+
+    context 'when allowed_to_use? yields namespace ids' do
+      let(:namespace_ids) { [1, 2] }
+
+      before do
+        allow(user).to receive(:allowed_to_use?).with(ai_feature).and_yield(namespace_ids)
+      end
+
+      it { is_expected.to eq(namespace_ids) }
+    end
+  end
+
   describe '#any_group_with_ai_available?', :saas, :use_clean_rails_redis_caching do
     using RSpec::Parameterized::TableSyntax
 
diff --git a/ee/spec/requests/api/code_suggestions_spec.rb b/ee/spec/requests/api/code_suggestions_spec.rb
index 9d6dc8c210ac6f5316d6172aadc8e75ecf6a2f3f..6748ddce1a3c45d0ceb0ec1664be6aa4d21c66f5 100644
--- a/ee/spec/requests/api/code_suggestions_spec.rb
+++ b/ee/spec/requests/api/code_suggestions_spec.rb
@@ -16,7 +16,7 @@
     }
   end
 
-  let(:enabled_by_namespace_ids) { [1, 2] }
+  let(:enabled_by_namespace_ids) { [] }
   let(:current_user) { nil }
   let(:headers) { {} }
   let(:access_code_suggestions) { true }
@@ -24,6 +24,9 @@
   let(:global_instance_id) { 'instance-ABC' }
   let(:global_user_id) { 'user-ABC' }
   let(:gitlab_realm) { 'saas' }
+  let(:service_name) { :code_suggestions }
+  let(:service) { instance_double('::CloudConnector::SelfSigned::AvailableServiceData') }
+  let_it_be(:token) { 'generated-jwt' }
 
   before do
     allow(Gitlab).to receive(:com?).and_return(is_saas)
@@ -38,6 +41,12 @@
 
     allow(Gitlab::GlobalAnonymousId).to receive(:user_id).and_return(global_user_id)
     allow(Gitlab::GlobalAnonymousId).to receive(:instance_id).and_return(global_instance_id)
+
+    allow(::CloudConnector::AvailableServices).to receive(:find_by_name).with(service_name).and_return(service)
+    allow(service).to receive_messages(access_token: token, name: service_name)
+    allow(service).to receive_message_chain(:add_on_purchases, :assigned_to_user, :any?).and_return(true)
+    allow(service).to receive_message_chain(:add_on_purchases, :assigned_to_user, :uniq_namespace_ids)
+      .and_return(enabled_by_namespace_ids)
   end
 
   shared_examples 'a response' do |case_name|
@@ -164,7 +173,6 @@ def is_even(n: int) ->
     end
 
     let(:file_name) { 'test.py' }
-    let(:service_name) { :code_suggestions }
     let(:additional_params) { {} }
     let(:body) do
       {
@@ -224,19 +232,12 @@ def is_even(n: int) ->
       }
     end
 
-    let(:service) { instance_double('::CloudConnector::SelfSigned::AvailableServiceData') }
-
     subject(:post_api) do
       post api('/code_suggestions/completions', current_user), headers: headers, params: body.to_json
     end
 
     before do
       allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).and_return(0)
-      allow(::CloudConnector::AvailableServices).to receive(:find_by_name).with(service_name).and_return(service)
-      allow(service).to receive_messages(access_token: token, name: service_name)
-      allow(service).to receive_message_chain(:add_on_purchases, :assigned_to_user, :any?).and_return(true)
-      allow(service).to receive_message_chain(:add_on_purchases, :assigned_to_user, :uniq_namespace_ids)
-        .and_return(enabled_by_namespace_ids)
       stub_feature_flags(use_codestral_for_code_completions: false)
       stub_feature_flags(fireworks_qwen_code_completion: false)
     end
@@ -295,7 +296,7 @@ def request
               'X-Gitlab-Host-Name' => [Gitlab.config.gitlab.host],
               'X-Gitlab-Realm' => [gitlab_realm],
               'Authorization' => ["Bearer #{token}"],
-              'X-Gitlab-Feature-Enabled-By-Namespace-Ids' => [enabled_by_namespace_ids.join(',')],
+              'X-Gitlab-Feature-Enabled-By-Namespace-Ids' => [""],
               'Content-Type' => ['application/json'],
               'User-Agent' => ['Super Awesome Browser 43.144.12']
             )
@@ -348,7 +349,7 @@ def request
             'X-Gitlab-Host-Name' => [Gitlab.config.gitlab.host],
             'X-Gitlab-Realm' => [gitlab_realm],
             'Authorization' => ["Bearer #{token}"],
-            'X-Gitlab-Feature-Enabled-By-Namespace-Ids' => [enabled_by_namespace_ids.join(',')],
+            'X-Gitlab-Feature-Enabled-By-Namespace-Ids' => [""],
             'Content-Type' => ['application/json'],
             'User-Agent' => ['Super Awesome Browser 43.144.12']
           )
@@ -373,7 +374,7 @@ def request
             expect(params['Header']).to include({
               'X-Gitlab-Authentication-Type' => ['oidc'],
               'Authorization' => ["Bearer #{token}"],
-              'X-Gitlab-Feature-Enabled-By-Namespace-Ids' => [enabled_by_namespace_ids.join(',')],
+              'X-Gitlab-Feature-Enabled-By-Namespace-Ids' => [""],
               'Content-Type' => ['application/json'],
               'X-Gitlab-Instance-Id' => [global_instance_id],
               'X-Gitlab-Global-User-Id' => [global_user_id],
@@ -970,7 +971,8 @@ def get_user(session):
           'X-Gitlab-Realm' => gitlab_realm,
           'X-Gitlab-Version' => Gitlab.version_info.to_s,
           'X-Gitlab-Authentication-Type' => 'oidc',
-          'X-Gitlab-Duo-Seat-Count' => duo_seat_count
+          'X-Gitlab-Duo-Seat-Count' => duo_seat_count,
+          'X-Gitlab-Feature-Enabled-By-Namespace-Ids' => enabled_by_namespace_ids.join(',')
         }
       end
 
@@ -996,6 +998,7 @@ def request
 
       context 'when user belongs to a namespace with an active code suggestions purchase' do
         let_it_be(:add_on_purchase) { create(:gitlab_subscription_add_on_purchase) }
+        let_it_be(:enabled_by_namespace_ids) { [add_on_purchase.namespace_id] }
         let(:duo_seat_count) { '1' }
 
         let(:headers) do