diff --git a/ee/lib/api/ai/duo_workflows/workflows.rb b/ee/lib/api/ai/duo_workflows/workflows.rb
index e2fd52f37eb4c2f3364fb710d7a8c3f3881f28f1..719c8bda3fa5bc3e2a74eb48bdf4e38ddad4bd46 100644
--- a/ee/lib/api/ai/duo_workflows/workflows.rb
+++ b/ee/lib/api/ai/duo_workflows/workflows.rb
@@ -11,8 +11,6 @@ class Workflows < ::API::Base
 
         before { authenticate! }
 
-        allow_access_with_scope :ai_workflows
-
         helpers do
           def find_workflow!(id)
             workflow = ::Ai::DuoWorkflows::Workflow.for_user_with_id!(current_user.id, id)
@@ -21,10 +19,6 @@ def find_workflow!(id)
             forbidden!
           end
 
-          def find_event!(workflow, id)
-            workflow.events.find(id)
-          end
-
           def authorize_run_workflows!(project)
             return if can?(current_user, :duo_workflow, project)
 
@@ -57,15 +51,6 @@ def create_workflow_params
             declared_params(include_missing: false)
           end
 
-          def render_response(response)
-            if response.success?
-              status :ok
-              response.payload
-            else
-              render_api_error!(response.message, response.reason)
-            end
-          end
-
           params :workflow_params do
             requires :project_id, type: String, desc: 'The ID or path of the workflow project',
               documentation: { example: '1' }
@@ -148,99 +133,6 @@ def render_response(response)
 
                 present workflow, with: ::API::Entities::Ai::DuoWorkflows::Workflow
               end
-
-              desc 'Updates the workflow status' do
-                success code: 200
-              end
-              params do
-                requires :id, type: Integer, desc: 'The ID of the workflow', documentation: { example: 1 }
-                requires :status_event, type: String, desc: 'The status event',
-                  documentation: { example: 'finish' }
-              end
-              patch '/:id' do
-                workflow = find_workflow!(params[:id])
-                forbidden! unless current_user.can?(:update_duo_workflow, workflow)
-
-                service = ::Ai::DuoWorkflows::UpdateWorkflowStatusService.new(
-                  workflow: workflow,
-                  status_event: params[:status_event],
-                  current_user: current_user
-                )
-
-                render_response(service.execute)
-              end
-
-              params do
-                requires :id, type: Integer, desc: 'The ID of the workflow'
-                requires :thread_ts, type: String, desc: 'The thread ts'
-                optional :parent_ts, type: String, desc: 'The parent ts'
-                requires :checkpoint, type: Hash, desc: "Checkpoint content"
-                requires :metadata, type: Hash, desc: "Checkpoint metadata"
-              end
-              post '/:id/checkpoints' do
-                workflow = find_workflow!(params[:id])
-                checkpoint_params = declared_params(include_missing: false).except(:id)
-                service = ::Ai::DuoWorkflows::CreateCheckpointService.new(project: workflow.project,
-                  workflow: workflow, params: checkpoint_params)
-                result = service.execute
-
-                bad_request!(result[:message]) if result[:status] == :error
-
-                present result[:checkpoint], with: ::API::Entities::Ai::DuoWorkflows::Checkpoint
-              end
-
-              get '/:id/checkpoints' do
-                workflow = find_workflow!(params[:id])
-                checkpoints = workflow.checkpoints.order(thread_ts: :desc) # rubocop:disable CodeReuse/ActiveRecord -- adding scope for order is no clearer
-                present paginate(checkpoints), with: ::API::Entities::Ai::DuoWorkflows::Checkpoint
-              end
-
-              params do
-                requires :id, type: Integer, desc: 'The ID of the workflow'
-                requires :event_type, type: String, values: %w[response message pause stop resume],
-                  desc: 'The type of event'
-                requires :message, type: String, desc: "Message from the human"
-              end
-              post '/:id/events' do
-                workflow = find_workflow!(params[:id])
-                event_params = declared_params(include_missing: false).except(:id)
-                service = ::Ai::DuoWorkflows::CreateEventService.new(
-                  project: workflow.project,
-                  workflow: workflow,
-                  params: event_params.merge(event_status: :queued)
-                )
-                result = service.execute
-
-                bad_request!(result[:message]) if result[:status] == :error
-
-                present result[:event], with: ::API::Entities::Ai::DuoWorkflows::Event
-              end
-
-              get '/:id/events' do
-                workflow = find_workflow!(params[:id])
-                events = workflow.events.queued
-                present paginate(events), with: ::API::Entities::Ai::DuoWorkflows::Event
-              end
-
-              params do
-                requires :id, type: Integer, desc: 'The ID of the workflow'
-                requires :event_id, type: Integer, desc: 'The ID of the event'
-                requires :event_status, type: String, values: %w[queued delivered], desc: 'The status of the event'
-              end
-              put '/:id/events/:event_id' do
-                workflow = find_workflow!(params[:id])
-                event = find_event!(workflow, params[:event_id])
-                event_params = declared_params(include_missing: false).except(:id, :event_id)
-                service = ::Ai::DuoWorkflows::UpdateEventService.new(
-                  event: event,
-                  params: event_params
-                )
-                result = service.execute
-
-                bad_request!(result[:message]) if result[:status] == :error
-
-                present result[:event], with: ::API::Entities::Ai::DuoWorkflows::Event
-              end
             end
           end
         end
diff --git a/ee/lib/api/ai/duo_workflows/workflows_internal.rb b/ee/lib/api/ai/duo_workflows/workflows_internal.rb
new file mode 100644
index 0000000000000000000000000000000000000000..94dd13889aa186eb29b0820f154295f58af9cbb1
--- /dev/null
+++ b/ee/lib/api/ai/duo_workflows/workflows_internal.rb
@@ -0,0 +1,150 @@
+# frozen_string_literal: true
+
+module API
+  module Ai
+    module DuoWorkflows
+      # This API is intended to be consumed by a running Duo Workflow using
+      # the ai_workflows scope token. These are requests coming from Duo Workflow
+      # Service and Duo Workflow Executor. We should not add any more requests to
+      # this API than needed by those 2 components. Otherwise add to
+      # `API::Ai::DuoWorkflows::Workflows`.
+      class WorkflowsInternal < ::API::Base
+        include PaginationParams
+        include APIGuard
+
+        allow_access_with_scope :ai_workflows
+
+        feature_category :duo_workflow
+
+        before { authenticate! }
+
+        helpers do
+          def find_workflow!(id)
+            workflow = ::Ai::DuoWorkflows::Workflow.for_user_with_id!(current_user.id, id)
+            return workflow if current_user.can?(:read_duo_workflow, workflow)
+
+            forbidden!
+          end
+
+          def find_event!(workflow, id)
+            workflow.events.find(id)
+          end
+
+          def render_response(response)
+            if response.success?
+              status :ok
+              response.payload
+            else
+              render_api_error!(response.message, response.reason)
+            end
+          end
+        end
+
+        namespace :ai do
+          namespace :duo_workflows do
+            namespace :workflows do
+              namespace '/:id' do
+                desc 'Updates the workflow status' do
+                  success code: 200
+                end
+                params do
+                  requires :id, type: Integer, desc: 'The ID of the workflow', documentation: { example: 1 }
+                  requires :status_event, type: String, desc: 'The status event',
+                    documentation: { example: 'finish' }
+                end
+                patch do
+                  workflow = find_workflow!(params[:id])
+                  forbidden! unless current_user.can?(:update_duo_workflow, workflow)
+
+                  service = ::Ai::DuoWorkflows::UpdateWorkflowStatusService.new(
+                    workflow: workflow,
+                    status_event: params[:status_event],
+                    current_user: current_user
+                  )
+
+                  render_response(service.execute)
+                end
+
+                namespace :checkpoints do
+                  params do
+                    requires :id, type: Integer, desc: 'The ID of the workflow'
+                    requires :thread_ts, type: String, desc: 'The thread ts'
+                    optional :parent_ts, type: String, desc: 'The parent ts'
+                    requires :checkpoint, type: Hash, desc: "Checkpoint content"
+                    requires :metadata, type: Hash, desc: "Checkpoint metadata"
+                  end
+                  post do
+                    workflow = find_workflow!(params[:id])
+                    checkpoint_params = declared_params(include_missing: false).except(:id)
+                    service = ::Ai::DuoWorkflows::CreateCheckpointService.new(project: workflow.project,
+                      workflow: workflow, params: checkpoint_params)
+                    result = service.execute
+
+                    bad_request!(result[:message]) if result[:status] == :error
+
+                    present result[:checkpoint], with: ::API::Entities::Ai::DuoWorkflows::Checkpoint
+                  end
+
+                  get do
+                    workflow = find_workflow!(params[:id])
+                    checkpoints = workflow.checkpoints.order(thread_ts: :desc) # rubocop:disable CodeReuse/ActiveRecord -- adding scope for order is no clearer
+                    present paginate(checkpoints), with: ::API::Entities::Ai::DuoWorkflows::Checkpoint
+                  end
+                end
+
+                namespace :events do
+                  params do
+                    requires :id, type: Integer, desc: 'The ID of the workflow'
+                    requires :event_type, type: String, values: %w[response message pause stop resume],
+                      desc: 'The type of event'
+                    requires :message, type: String, desc: "Message from the human"
+                  end
+                  post do
+                    workflow = find_workflow!(params[:id])
+                    event_params = declared_params(include_missing: false).except(:id)
+                    service = ::Ai::DuoWorkflows::CreateEventService.new(
+                      project: workflow.project,
+                      workflow: workflow,
+                      params: event_params.merge(event_status: :queued)
+                    )
+                    result = service.execute
+
+                    bad_request!(result[:message]) if result[:status] == :error
+
+                    present result[:event], with: ::API::Entities::Ai::DuoWorkflows::Event
+                  end
+
+                  get do
+                    workflow = find_workflow!(params[:id])
+                    events = workflow.events.queued
+                    present paginate(events), with: ::API::Entities::Ai::DuoWorkflows::Event
+                  end
+
+                  params do
+                    requires :id, type: Integer, desc: 'The ID of the workflow'
+                    requires :event_id, type: Integer, desc: 'The ID of the event'
+                    requires :event_status, type: String, values: %w[queued delivered], desc: 'The status of the event'
+                  end
+                  put '/:event_id' do
+                    workflow = find_workflow!(params[:id])
+                    event = find_event!(workflow, params[:event_id])
+                    event_params = declared_params(include_missing: false).except(:id, :event_id)
+                    service = ::Ai::DuoWorkflows::UpdateEventService.new(
+                      event: event,
+                      params: event_params
+                    )
+                    result = service.execute
+
+                    bad_request!(result[:message]) if result[:status] == :error
+
+                    present result[:event], with: ::API::Entities::Ai::DuoWorkflows::Event
+                  end
+                end
+              end
+            end
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/ee/lib/ee/api/api.rb b/ee/lib/ee/api/api.rb
index fd071c5d586b3a5db035cd13788cb86b76712c5f..4bfd8d2cfcb1149e036e1f15c576e5a35e1b794f 100644
--- a/ee/lib/ee/api/api.rb
+++ b/ee/lib/ee/api/api.rb
@@ -71,6 +71,7 @@ module API
         mount ::API::GroupServiceAccounts
         mount ::API::Ai::Llm::GitCommand
         mount ::API::Ai::DuoWorkflows::Workflows
+        mount ::API::Ai::DuoWorkflows::WorkflowsInternal
         mount ::API::CodeSuggestions
         mount ::API::Chat
         mount ::API::DuoCodeReview
diff --git a/ee/spec/requests/api/ai/duo_workflows/workflows_internal_spec.rb b/ee/spec/requests/api/ai/duo_workflows/workflows_internal_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..84caa07704df710ff188cb05696dfca09d6ad09d
--- /dev/null
+++ b/ee/spec/requests/api/ai/duo_workflows/workflows_internal_spec.rb
@@ -0,0 +1,280 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe API::Ai::DuoWorkflows::WorkflowsInternal, feature_category: :duo_workflow do
+  include HttpBasicAuthHelpers
+
+  let_it_be(:group) { create(:group) }
+  let_it_be(:project) { create(:project, :repository, group: group) }
+  let_it_be(:user) { create(:user, maintainer_of: project) }
+  let_it_be(:workflow) { create(:duo_workflows_workflow, user: user, project: project) }
+  let_it_be(:duo_workflow_service_url) { 'duo-workflow-service.example.com:50052' }
+  let_it_be(:oauth_token) { create(:oauth_access_token, user: user, scopes: [:ai_workflows]) }
+
+  before do
+    allow(::Gitlab::Llm::StageCheck).to receive(:available?).with(project, :duo_workflow).and_return(true)
+  end
+
+  describe 'POST /ai/duo_workflows/workflows/:id/checkpoints' do
+    let(:current_time) { Time.current }
+    let(:thread_ts) { current_time.to_s }
+    let(:later_thread_ts) { (current_time + 1.second).to_s }
+    let(:parent_ts) { (current_time - 1.second).to_s }
+    let(:checkpoint) { { key: 'value' } }
+    let(:metadata) { { key: 'value' } }
+    let(:params) { { thread_ts: thread_ts, checkpoint: checkpoint, parent_ts: parent_ts, metadata: metadata } }
+    let(:path) { "/ai/duo_workflows/workflows/#{workflow.id}/checkpoints" }
+
+    it 'allows creating multiple checkpoints for a workflow' do
+      expect do
+        post api(path, user), params: params
+        expect(response).to have_gitlab_http_status(:created)
+
+        post api(path, user), params: params.merge(thread_ts: later_thread_ts, parent_ts: thread_ts)
+        expect(response).to have_gitlab_http_status(:created)
+      end.to change { workflow.reload.checkpoints.count }.by(2)
+
+      expect(json_response['id']).to eq(Ai::DuoWorkflows::Checkpoint.last.id)
+    end
+
+    context 'when authenticated with a token that has the ai_workflows scope' do
+      it 'is successful' do
+        post api(path, oauth_access_token: oauth_token),
+          params: params.merge(thread_ts: later_thread_ts, parent_ts: thread_ts)
+
+        expect(response).to have_gitlab_http_status(:created)
+      end
+    end
+
+    it 'fails if the thread_ts is an empty string' do
+      post api(path, user), params: params.merge(thread_ts: '')
+      expect(response).to have_gitlab_http_status(:bad_request)
+      expect(json_response['message']).to include("can't be blank")
+    end
+  end
+
+  describe 'GET /ai/duo_workflows/workflows/:id/checkpoints' do
+    let(:path) { "/ai/duo_workflows/workflows/#{workflow.id}/checkpoints" }
+
+    it 'returns the checkpoints in descending order of thread_ts' do
+      checkpoint1 = create(:duo_workflows_checkpoint, workflow: workflow)
+      checkpoint2 = create(:duo_workflows_checkpoint, workflow: workflow)
+      workflow.checkpoints << checkpoint1
+      workflow.checkpoints << checkpoint2
+
+      get api(path, user)
+      expect(response).to have_gitlab_http_status(:ok)
+      expect(json_response.pluck('id')).to eq([checkpoint2.id, checkpoint1.id])
+      expect(json_response.pluck('thread_ts')).to eq([checkpoint2.thread_ts, checkpoint1.thread_ts])
+      expect(json_response.pluck('parent_ts')).to eq([checkpoint2.parent_ts, checkpoint1.parent_ts])
+      expect(json_response[0]).to have_key('checkpoint')
+      expect(json_response[0]).to have_key('metadata')
+    end
+  end
+
+  describe 'POST /ai/duo_workflows/workflows/:id/events' do
+    let(:path) { "/ai/duo_workflows/workflows/#{workflow.id}/events" }
+    let(:params) do
+      {
+        event_type: 'message',
+        message: 'Hello, World!'
+      }
+    end
+
+    context 'when success' do
+      it 'creates a new event' do
+        expect do
+          post api(path, user), params: params
+          expect(response).to have_gitlab_http_status(:created)
+        end.to change { workflow.events.count }.by(1)
+        expect(json_response['id']).to eq(Ai::DuoWorkflows::Event.last.id)
+        expect(json_response['event_type']).to eq('message')
+        expect(json_response['message']).to eq('Hello, World!')
+        expect(json_response['event_status']).to eq('queued')
+      end
+
+      context 'when authenticated with a token that has the ai_workflows scope' do
+        it 'is successful' do
+          expect do
+            post api(path, oauth_access_token: oauth_token), params: params
+            expect(response).to have_gitlab_http_status(:created)
+          end.to change { workflow.events.count }.by(1)
+        end
+      end
+    end
+
+    context 'when required parameters are missing' do
+      it 'returns bad request when event_type is missing' do
+        post api(path, user), params: params.except(:event_type)
+        expect(response).to have_gitlab_http_status(:bad_request)
+        expect(json_response['error']).to include("event_type is missing")
+      end
+
+      it 'returns bad request when message is missing' do
+        post api(path, user), params: params.except(:message)
+        expect(response).to have_gitlab_http_status(:bad_request)
+        expect(json_response['error']).to include("message is missing")
+      end
+    end
+
+    context 'when invalid event_type is provided' do
+      it 'returns bad request' do
+        post api(path, user), params: params.merge(event_type: 'invalid_event_type')
+        expect(response).to have_gitlab_http_status(:bad_request)
+        expect(json_response['error']).to include("event_type does not have a valid value")
+      end
+    end
+
+    context 'with a workflow belonging to a different user' do
+      let(:workflow) { create(:duo_workflows_workflow) }
+
+      it 'returns 404' do
+        post api(path, user), params: params
+        expect(response).to have_gitlab_http_status(:not_found)
+      end
+    end
+  end
+
+  describe 'GET /ai/duo_workflows/workflows/:id/events' do
+    let(:path) { "/ai/duo_workflows/workflows/#{workflow.id}/events" }
+
+    it 'returns queued events for the workflow' do
+      event1 = create(:duo_workflows_event, workflow: workflow, event_status: :queued)
+      event2 = create(:duo_workflows_event, workflow: workflow, event_status: :queued)
+
+      get api(path, user)
+      expect(response).to have_gitlab_http_status(:ok)
+      expect(json_response.size).to eq(2)
+      # rubocop:disable Rails/Pluck -- json_response is an array of hashes, we can't use pluck
+      expect(json_response.map { |e| e['id'] }).to contain_exactly(event1.id, event2.id)
+      expect(json_response.map { |e| e['event_status'] }).to all(eq('queued'))
+      # rubocop:enable Rails/Pluck
+    end
+
+    it 'returns empty array if no queued events' do
+      create(:duo_workflows_event, workflow: workflow, event_status: :delivered)
+
+      get api(path, user)
+      expect(response).to have_gitlab_http_status(:ok)
+      expect(json_response).to be_empty
+    end
+
+    context 'with a workflow belonging to a different user' do
+      let(:workflow) { create(:duo_workflows_workflow) }
+
+      it 'returns 404' do
+        get api(path, user)
+        expect(response).to have_gitlab_http_status(:not_found)
+      end
+    end
+  end
+
+  describe 'PUT /ai/duo_workflows/workflows/:id/events/:event_id' do
+    let(:event) { create(:duo_workflows_event, workflow: workflow, event_status: :queued) }
+    let(:path) { "/ai/duo_workflows/workflows/#{workflow.id}/events/#{event.id}" }
+    let(:params) { { event_status: 'delivered' } }
+
+    context 'when success' do
+      it 'updates the event status' do
+        put api(path, user), params: params
+        expect(response).to have_gitlab_http_status(:ok)
+        expect(json_response['id']).to eq(event.id)
+        expect(json_response['event_status']).to eq('delivered')
+        expect(event.reload.event_status).to eq('delivered')
+      end
+
+      context 'when authenticated with a token that has the ai_workflows scope' do
+        it 'is successful' do
+          put api(path, oauth_access_token: oauth_token), params: params
+          expect(response).to have_gitlab_http_status(:ok)
+        end
+      end
+    end
+
+    context 'when invalid event_status is provided' do
+      it 'returns bad request' do
+        put api(path, user), params: { event_status: 'InvalidStatus' }
+        expect(response).to have_gitlab_http_status(:bad_request)
+        expect(json_response['error']).to include("event_status does not have a valid value")
+      end
+    end
+
+    context 'when the event does not exist' do
+      let(:path) { "/ai/duo_workflows/workflows/#{workflow.id}/events/0" }
+
+      it 'returns 404' do
+        put api(path, user), params: params
+        expect(response).to have_gitlab_http_status(:not_found)
+      end
+    end
+
+    context 'with a workflow belonging to a different user' do
+      let(:workflow) { create(:duo_workflows_workflow) }
+
+      it 'returns 404' do
+        put api(path, user), params: params
+        expect(response).to have_gitlab_http_status(:not_found)
+      end
+    end
+
+    context 'with an event belonging to a different workflow' do
+      let(:other_workflow) { create(:duo_workflows_workflow, user: user, project: project) }
+      let(:event) { create(:duo_workflows_event, workflow: other_workflow) }
+
+      it 'returns 404' do
+        put api(path, user), params: params
+        expect(response).to have_gitlab_http_status(:not_found)
+      end
+    end
+  end
+
+  describe 'PATCH /ai/duo_workflows/workflows/:id' do
+    let(:path) { "/ai/duo_workflows/workflows/#{workflow.id}" }
+
+    context 'when update workflow status service returns error' do
+      before do
+        allow_next_instance_of(::Ai::DuoWorkflows::UpdateWorkflowStatusService) do |service|
+          allow(service).to receive(:execute).and_return(ServiceResponse.error(reason: :bad_request,
+            message: 'Cannot update workflow status'))
+        end
+      end
+
+      it 'returns http error status and error message' do
+        patch api(path, user), params: { status_event: "finish" }
+
+        expect(response).to have_gitlab_http_status(:bad_request)
+        expect(json_response['message']).to eq('Cannot update workflow status')
+      end
+    end
+
+    context 'when update workflow status service returns success' do
+      before do
+        allow_next_instance_of(::Ai::DuoWorkflows::UpdateWorkflowStatusService) do |service|
+          allow(service).to receive(:execute).and_return(ServiceResponse.success(payload: { workflow: workflow },
+            message: 'Workflow status updated'))
+        end
+      end
+
+      it 'returns http status ok' do
+        patch api(path, user), params: { status_event: "finish" }
+
+        expect(response).to have_gitlab_http_status(:ok)
+        expect(json_response['workflow']['id']).to eq(workflow.id)
+      end
+    end
+
+    context 'when duo_features_enabled settings is turned off' do
+      before do
+        workflow.project.project_setting.update!(duo_features_enabled: false)
+        workflow.project.reload
+      end
+
+      it 'returns forbidden' do
+        patch api(path, user), params: { status_event: "finish" }
+
+        expect(response).to have_gitlab_http_status(:forbidden)
+      end
+    end
+  end
+end
diff --git a/ee/spec/requests/api/ai/duo_workflows/workflows_spec.rb b/ee/spec/requests/api/ai/duo_workflows/workflows_spec.rb
index 26d02a5c9e3527a34cf76d2588ebb906896d4c1e..a54bf1da1973864fdae33a77feede54946ee622b 100644
--- a/ee/spec/requests/api/ai/duo_workflows/workflows_spec.rb
+++ b/ee/spec/requests/api/ai/duo_workflows/workflows_spec.rb
@@ -10,7 +10,7 @@
   let_it_be(:user) { create(:user, maintainer_of: project) }
   let_it_be(:workflow) { create(:duo_workflows_workflow, user: user, project: project) }
   let_it_be(:duo_workflow_service_url) { 'duo-workflow-service.example.com:50052' }
-  let_it_be(:oauth_token) { create(:oauth_access_token, user: user, scopes: [:ai_workflows]) }
+  let_it_be(:ai_workflows_oauth_token) { create(:oauth_access_token, user: user, scopes: [:ai_workflows]) }
 
   before do
     allow(::Gitlab::Llm::StageCheck).to receive(:available?).with(project, :duo_workflow).and_return(true)
@@ -30,10 +30,10 @@
       end
 
       context 'when authenticated with a token that has the ai_workflows scope' do
-        it 'is successful' do
-          post api(path, oauth_access_token: oauth_token), params: params
+        it 'is forbidden' do
+          post api(path, oauth_access_token: ai_workflows_oauth_token), params: params
 
-          expect(response).to have_gitlab_http_status(:created)
+          expect(response).to have_gitlab_http_status(:forbidden)
         end
       end
 
@@ -95,10 +95,10 @@
     end
 
     context 'when authenticated with a token that has the ai_workflows scope' do
-      it 'is successful' do
-        get api(path, oauth_access_token: oauth_token)
+      it 'is forbidden' do
+        get api(path, oauth_access_token: ai_workflows_oauth_token)
 
-        expect(response).to have_gitlab_http_status(:ok)
+        expect(response).to have_gitlab_http_status(:forbidden)
       end
     end
 
@@ -124,219 +124,6 @@
     end
   end
 
-  describe 'POST /ai/duo_workflows/workflows/:id/checkpoints' do
-    let(:current_time) { Time.current }
-    let(:thread_ts) { current_time.to_s }
-    let(:later_thread_ts) { (current_time + 1.second).to_s }
-    let(:parent_ts) { (current_time - 1.second).to_s }
-    let(:checkpoint) { { key: 'value' } }
-    let(:metadata) { { key: 'value' } }
-    let(:params) { { thread_ts: thread_ts, checkpoint: checkpoint, parent_ts: parent_ts, metadata: metadata } }
-    let(:path) { "/ai/duo_workflows/workflows/#{workflow.id}/checkpoints" }
-
-    it 'allows creating multiple checkpoints for a workflow' do
-      expect do
-        post api(path, user), params: params
-        expect(response).to have_gitlab_http_status(:created)
-
-        post api(path, user), params: params.merge(thread_ts: later_thread_ts, parent_ts: thread_ts)
-        expect(response).to have_gitlab_http_status(:created)
-      end.to change { workflow.reload.checkpoints.count }.by(2)
-
-      expect(json_response['id']).to eq(Ai::DuoWorkflows::Checkpoint.last.id)
-    end
-
-    context 'when authenticated with a token that has the ai_workflows scope' do
-      it 'is successful' do
-        post api(path, oauth_access_token: oauth_token),
-          params: params.merge(thread_ts: later_thread_ts, parent_ts: thread_ts)
-
-        expect(response).to have_gitlab_http_status(:created)
-      end
-    end
-
-    it 'fails if the thread_ts is an empty string' do
-      post api(path, user), params: params.merge(thread_ts: '')
-      expect(response).to have_gitlab_http_status(:bad_request)
-      expect(json_response['message']).to include("can't be blank")
-    end
-  end
-
-  describe 'GET /ai/duo_workflows/workflows/:id/checkpoints' do
-    let(:path) { "/ai/duo_workflows/workflows/#{workflow.id}/checkpoints" }
-
-    it 'returns the checkpoints in descending order of thread_ts' do
-      checkpoint1 = create(:duo_workflows_checkpoint, workflow: workflow)
-      checkpoint2 = create(:duo_workflows_checkpoint, workflow: workflow)
-      workflow.checkpoints << checkpoint1
-      workflow.checkpoints << checkpoint2
-
-      get api(path, user)
-      expect(response).to have_gitlab_http_status(:ok)
-      expect(json_response.pluck('id')).to eq([checkpoint2.id, checkpoint1.id])
-      expect(json_response.pluck('thread_ts')).to eq([checkpoint2.thread_ts, checkpoint1.thread_ts])
-      expect(json_response.pluck('parent_ts')).to eq([checkpoint2.parent_ts, checkpoint1.parent_ts])
-      expect(json_response[0]).to have_key('checkpoint')
-      expect(json_response[0]).to have_key('metadata')
-    end
-  end
-
-  describe 'POST /ai/duo_workflows/workflows/:id/events' do
-    let(:path) { "/ai/duo_workflows/workflows/#{workflow.id}/events" }
-    let(:params) do
-      {
-        event_type: 'message',
-        message: 'Hello, World!'
-      }
-    end
-
-    context 'when success' do
-      it 'creates a new event' do
-        expect do
-          post api(path, user), params: params
-          expect(response).to have_gitlab_http_status(:created)
-        end.to change { workflow.events.count }.by(1)
-        expect(json_response['id']).to eq(Ai::DuoWorkflows::Event.last.id)
-        expect(json_response['event_type']).to eq('message')
-        expect(json_response['message']).to eq('Hello, World!')
-        expect(json_response['event_status']).to eq('queued')
-      end
-
-      context 'when authenticated with a token that has the ai_workflows scope' do
-        it 'is successful' do
-          expect do
-            post api(path, oauth_access_token: oauth_token), params: params
-            expect(response).to have_gitlab_http_status(:created)
-          end.to change { workflow.events.count }.by(1)
-        end
-      end
-    end
-
-    context 'when required parameters are missing' do
-      it 'returns bad request when event_type is missing' do
-        post api(path, user), params: params.except(:event_type)
-        expect(response).to have_gitlab_http_status(:bad_request)
-        expect(json_response['error']).to include("event_type is missing")
-      end
-
-      it 'returns bad request when message is missing' do
-        post api(path, user), params: params.except(:message)
-        expect(response).to have_gitlab_http_status(:bad_request)
-        expect(json_response['error']).to include("message is missing")
-      end
-    end
-
-    context 'when invalid event_type is provided' do
-      it 'returns bad request' do
-        post api(path, user), params: params.merge(event_type: 'invalid_event_type')
-        expect(response).to have_gitlab_http_status(:bad_request)
-        expect(json_response['error']).to include("event_type does not have a valid value")
-      end
-    end
-
-    context 'with a workflow belonging to a different user' do
-      let(:workflow) { create(:duo_workflows_workflow) }
-
-      it 'returns 404' do
-        post api(path, user), params: params
-        expect(response).to have_gitlab_http_status(:not_found)
-      end
-    end
-  end
-
-  describe 'GET /ai/duo_workflows/workflows/:id/events' do
-    let(:path) { "/ai/duo_workflows/workflows/#{workflow.id}/events" }
-
-    it 'returns queued events for the workflow' do
-      event1 = create(:duo_workflows_event, workflow: workflow, event_status: :queued)
-      event2 = create(:duo_workflows_event, workflow: workflow, event_status: :queued)
-
-      get api(path, user)
-      expect(response).to have_gitlab_http_status(:ok)
-      expect(json_response.size).to eq(2)
-      # rubocop:disable Rails/Pluck -- json_response is an array of hashes, we can't use pluck
-      expect(json_response.map { |e| e['id'] }).to contain_exactly(event1.id, event2.id)
-      expect(json_response.map { |e| e['event_status'] }).to all(eq('queued'))
-      # rubocop:enable Rails/Pluck
-    end
-
-    it 'returns empty array if no queued events' do
-      create(:duo_workflows_event, workflow: workflow, event_status: :delivered)
-
-      get api(path, user)
-      expect(response).to have_gitlab_http_status(:ok)
-      expect(json_response).to be_empty
-    end
-
-    context 'with a workflow belonging to a different user' do
-      let(:workflow) { create(:duo_workflows_workflow) }
-
-      it 'returns 404' do
-        get api(path, user)
-        expect(response).to have_gitlab_http_status(:not_found)
-      end
-    end
-  end
-
-  describe 'PUT /ai/duo_workflows/workflows/:id/events/:event_id' do
-    let(:event) { create(:duo_workflows_event, workflow: workflow, event_status: :queued) }
-    let(:path) { "/ai/duo_workflows/workflows/#{workflow.id}/events/#{event.id}" }
-    let(:params) { { event_status: 'delivered' } }
-
-    context 'when success' do
-      it 'updates the event status' do
-        put api(path, user), params: params
-        expect(response).to have_gitlab_http_status(:ok)
-        expect(json_response['id']).to eq(event.id)
-        expect(json_response['event_status']).to eq('delivered')
-        expect(event.reload.event_status).to eq('delivered')
-      end
-
-      context 'when authenticated with a token that has the ai_workflows scope' do
-        it 'is successful' do
-          put api(path, oauth_access_token: oauth_token), params: params
-          expect(response).to have_gitlab_http_status(:ok)
-        end
-      end
-    end
-
-    context 'when invalid event_status is provided' do
-      it 'returns bad request' do
-        put api(path, user), params: { event_status: 'InvalidStatus' }
-        expect(response).to have_gitlab_http_status(:bad_request)
-        expect(json_response['error']).to include("event_status does not have a valid value")
-      end
-    end
-
-    context 'when the event does not exist' do
-      let(:path) { "/ai/duo_workflows/workflows/#{workflow.id}/events/0" }
-
-      it 'returns 404' do
-        put api(path, user), params: params
-        expect(response).to have_gitlab_http_status(:not_found)
-      end
-    end
-
-    context 'with a workflow belonging to a different user' do
-      let(:workflow) { create(:duo_workflows_workflow) }
-
-      it 'returns 404' do
-        put api(path, user), params: params
-        expect(response).to have_gitlab_http_status(:not_found)
-      end
-    end
-
-    context 'with an event belonging to a different workflow' do
-      let(:other_workflow) { create(:duo_workflows_workflow, user: user, project: project) }
-      let(:event) { create(:duo_workflows_event, workflow: other_workflow) }
-
-      it 'returns 404' do
-        put api(path, user), params: params
-        expect(response).to have_gitlab_http_status(:not_found)
-      end
-    end
-  end
-
   describe 'POST /ai/duo_workflows/direct_access' do
     let(:path) { '/ai/duo_workflows/direct_access' }
 
@@ -439,48 +226,12 @@
       end
 
       context 'when authenticated with a token that has the ai_workflows scope' do
-        it 'succeeds' do
-          post api(path, oauth_access_token: oauth_token)
+        it 'is forbidden' do
+          post api(path, oauth_access_token: ai_workflows_oauth_token)
 
-          expect(response).to have_gitlab_http_status(:created)
-        end
-      end
-    end
-  end
-
-  describe 'PATCH /ai/duo_workflows/workflows/:id' do
-    let(:path) { "/ai/duo_workflows/workflows/#{workflow.id}" }
-
-    context 'when update workflow status service returns error' do
-      before do
-        allow_next_instance_of(::Ai::DuoWorkflows::UpdateWorkflowStatusService) do |service|
-          allow(service).to receive(:execute).and_return(ServiceResponse.error(reason: :bad_request,
-            message: 'Cannot update workflow status'))
-        end
-      end
-
-      it 'returns http error status and error message' do
-        patch api(path, user), params: { status_event: "finish" }
-
-        expect(response).to have_gitlab_http_status(:bad_request)
-        expect(json_response['message']).to eq('Cannot update workflow status')
-      end
-    end
-
-    context 'when update workflow status service returns success' do
-      before do
-        allow_next_instance_of(::Ai::DuoWorkflows::UpdateWorkflowStatusService) do |service|
-          allow(service).to receive(:execute).and_return(ServiceResponse.success(payload: { workflow: workflow },
-            message: 'Workflow status updated'))
+          expect(response).to have_gitlab_http_status(:forbidden)
         end
       end
-
-      it 'returns http status ok' do
-        patch api(path, user), params: { status_event: "finish" }
-
-        expect(response).to have_gitlab_http_status(:ok)
-        expect(json_response['workflow']['id']).to eq(workflow.id)
-      end
     end
   end
 end