diff --git a/scripts/assign_workflow_preseqlab.py b/scripts/assign_workflow_preseqlab.py
index e539d68..5652fbb 100644
--- a/scripts/assign_workflow_preseqlab.py
+++ b/scripts/assign_workflow_preseqlab.py
@@ -1,15 +1,41 @@
 #!/usr/bin/env python
+import platform
+
+import time
+from pyclarity_lims.entities import Step
+from requests import HTTPError
+
 from EPPs.common import StepEPP, get_workflow_stage, find_newest_artifact_originating_from
 
 
 class AssignWorkflowPreSeqLab(StepEPP):
+
+    @staticmethod
+    def _finish_step(step, try_count=1):
+        """
+        This function will try to advance a step three time waiting for a ongoing program to finish.
+        It waits for 5 seconds in between each try
+        """
+        try:
+            step.get(force=True)
+            step.advance()
+        except HTTPError as e:
+            if try_count < 3 and str(e) == '400: Cannot advance a step that has an external program queued, ' \
+                                           'running or pending acknowledgement':
+                # wait for whatever needs to happen to happen
+                time.sleep(5)
+                AssignWorkflowPreSeqLab._finish_step(step, try_count=try_count + 1)
+            else:
+                raise e
+
     def _run(self):
-        artifacts_to_route = set()
+        artifacts_to_route_to_seq_lab = set()
+        artifacts_to_route_to_remove = set()
         for art in self.artifacts:
             sample = art.samples[0]
             if sample.udf.get("Proceed To SeqLab") and not sample.udf.get("2D Barcode"):
                 # checks to see if sample is in plate or fluidX tube
-                artifacts_to_route.add(sample.artifact)
+                artifacts_to_route_to_seq_lab.add(sample.artifact)
 
             elif sample.udf.get("Proceed To SeqLab") and sample.udf.get("2D Barcode"):
                 artifact = find_newest_artifact_originating_from(
@@ -17,12 +43,37 @@ class AssignWorkflowPreSeqLab(StepEPP):
                     process_type="FluidX Transfer From Rack Into Plate EG 1.0 ST",
                     sample_name=sample.name
                 )
-                artifacts_to_route.add(artifact)
+                artifacts_to_route_to_seq_lab.add(artifact)
+
+            else:
+                artifacts_to_route_to_remove.add(sample.artifact)
 
-        if artifacts_to_route:
+        if artifacts_to_route_to_seq_lab:
             # Only route artifacts if there are any
             stage = get_workflow_stage(self.lims, "PreSeqLab EG 6.0", "Sequencing Plate Preparation EG 2.0")
-            self.lims.route_artifacts(list(artifacts_to_route), stage_uri=stage.uri)
+            self.lims.route_artifacts(list(artifacts_to_route_to_seq_lab), stage_uri=stage.uri)
+
+        if artifacts_to_route_to_remove:
+            stage = get_workflow_stage(self.lims, "Remove From Processing EG 1.0 WF", "Remove From Processing EG 1.0 ST")
+            self.lims.route_artifacts(list(artifacts_to_route_to_remove), stage_uri=stage.uri)
+
+            # Create new step with the routed artifacts
+            s = Step.create(self.lims, protocol_step=stage.step, inputs=artifacts_to_route_to_remove,
+                            container_type_name='Tube')
+
+            url = 'https://%s/clarity/work-complete/%s' % (platform.node(), self.process.id.split('-')[1])
+            s.details.udf['Reason for removal from processing:'] = 'Failed QC. See step %s' % url
+            s.details.put()
+
+            # Move from "Record detail" window to the "Next Step"
+            s.advance()
+
+            for next_action in s.actions.next_actions:
+                next_action['action'] = 'complete'
+            s.actions.put()
+
+            # Complete the step
+            self._finish_step(s)
 
 
 if __name__ == "__main__":

Route samples after "Awaiting User Response" to Remove Step
At the end of "Awaiting User Response" artifacts that do not have UDF "Proceed To SeqLab" == true
Should be routed and run through the Remove from workflow step.
this can be done in `assign_workflow_preseqlab.py` or another independent script
diff --git a/tests/test_assign_to_workflow_preseqlab.py b/tests/test_assign_to_workflow_preseqlab.py
index de2ef85..ff58eff 100644
--- a/tests/test_assign_to_workflow_preseqlab.py
+++ b/tests/test_assign_to_workflow_preseqlab.py
@@ -1,3 +1,6 @@
+import pytest
+from requests import HTTPError
+
 from scripts.assign_workflow_preseqlab import AssignWorkflowPreSeqLab
 from tests.test_common import TestEPP, fake_artifact
 from unittest.mock import Mock, patch, PropertyMock
@@ -7,7 +10,8 @@ def fake_all_inputs(unique=False, resolve=False):
     """Return a list of mocked artifacts which contain samples which contain artifacts ... Simple!"""
     return (
         Mock(samples=[Mock(artifact=fake_artifact('a1'), id='s1', udf={'Proceed To SeqLab': True})]),
-        Mock(samples=[Mock(artifact=fake_artifact('a2'), id='s2', udf={'Proceed To SeqLab': True, '2D Barcode': 'fluidX1'})])
+        Mock(samples=[Mock(artifact=fake_artifact('a2'), id='s2', udf={'Proceed To SeqLab': True, '2D Barcode': 'fluidX1'})]),
+        Mock(samples=[Mock(artifact=fake_artifact('a3'), id='s3', udf={'Proceed To SeqLab': False})])
     )
 
 
@@ -16,22 +20,69 @@ class TestAssignWorkflowPreSeqLab(TestEPP):
         self.patched_process = patch.object(
             AssignWorkflowPreSeqLab,
             'process',
-            new_callable=PropertyMock(return_value=Mock(all_inputs=fake_all_inputs))
+            new_callable=PropertyMock(return_value=Mock(id='24-1234', all_inputs=fake_all_inputs))
         )
+        self.workflow_stage = Mock(uri='a_uri')
         self.patched_get_workflow_stage = patch(
             'scripts.assign_workflow_preseqlab.get_workflow_stage',
-            return_value=Mock(uri='a_uri')
+            return_value=self.workflow_stage
         )
         self.patch_find_art = patch(
             'scripts.assign_workflow_preseqlab.find_newest_artifact_originating_from',
             return_value=Mock(id='fx2')
         )
+        self.mocked_step = Mock(details=Mock(udf={}), actions=Mock(next_actions=[{}]))
+        self.patch_Step_create = patch('scripts.assign_workflow_preseqlab.Step.create', return_value=self.mocked_step)
         self.epp = AssignWorkflowPreSeqLab(self.default_argv)
 
     def test_assign(self):
-        with self.patched_get_workflow_stage as pws, self.patched_lims, self.patched_process, self.patch_find_art:
+        with self.patched_get_workflow_stage as pws, self.patched_lims, self.patched_process, self.patch_find_art, \
+             self.patch_Step_create as psc:
             self.epp._run()
 
-            pws.assert_called_with(self.epp.lims, 'PreSeqLab EG 6.0', 'Sequencing Plate Preparation EG 2.0')
-            assert sorted([a.id for a in self.epp.lims.route_artifacts.call_args[0][0]]) == ['a1', 'fx2']
-            assert self.epp.lims.route_artifacts.call_args[1] == {'stage_uri': 'a_uri'}
+            pws.assert_any_call(self.epp.lims, 'PreSeqLab EG 6.0', 'Sequencing Plate Preparation EG 2.0')
+            first_call = self.epp.lims.route_artifacts.call_args_list[0]
+            assert sorted([a.id for a in first_call[0][0]]) == ['a1', 'fx2']
+            assert first_call[1] == {'stage_uri': 'a_uri'}
+
+            pws.assert_any_call(self.epp.lims, "Remove From Processing EG 1.0 WF", "Remove From Processing EG 1.0 ST")
+            second_call = self.epp.lims.route_artifacts.call_args_list[1]
+            assert sorted([a.id for a in second_call[0][0]]) == ['a3']
+            assert second_call[1] == {'stage_uri': 'a_uri'}
+            # Test the Step creation
+            psc.assert_called_with(
+                self.epp.lims,
+                inputs=set([self.epp.artifacts[2].samples[0].artifact]),
+                protocol_step=self.workflow_stage.step,
+                container_type_name='Tube'
+            )
+            # Test udf in details
+            assert 'Reason for removal from processing:' in self.mocked_step.details.udf
+            self.mocked_step.details.put.assert_called_with()
+            # Test next actions has been set.
+            assert self.mocked_step.actions.next_actions == [{'action': 'complete'}]
+            self.mocked_step.actions.put.assert_called_with()
+
+            # Test advance has been called.
+            assert self.mocked_step.advance.call_count == 2
+
+    def test_finish_step(self):
+        # Successful attempt
+        step = Mock()
+        self.epp._finish_step(step)
+        assert step.get.call_count == 1
+        assert step.advance.call_count == 1
+
+        # 3 failed attempts before raising
+        step = Mock(
+            advance=Mock(side_effect=HTTPError('400: Cannot advance a step that has an external program queued, '
+                                               'running or pending acknowledgement'))
+        )
+        with patch('time.sleep'):
+            with pytest.raises(HTTPError):
+                self.epp._finish_step(step)
+        assert step.get.call_count == 3
+        assert step.advance.call_count == 3
+
+
+

pip install -e .
parse_log_pytest
requirements.txt
pytest
apt-get update apt-get install -y gcc
3.6
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
asana==0.6.7
attrs==22.2.0
cached-property==1.5.2
certifi==2021.5.30
-e git+https://github.com/EdinburghGenomics/clarity_scripts.git@1e4cc541ba056e8d89301fb41fc2b8a560c81857#egg=clarity_scripts
EGCG-Core==0.8.1
importlib-metadata==4.8.3
iniconfig==1.1.1
Jinja2==2.8
MarkupSafe==2.0.1
oauthlib==3.2.2
packaging==21.3
pluggy==1.0.0
py==1.11.0
pyclarity-lims==0.4.8
pyparsing==3.1.4
pytest==7.0.1
PyYAML==6.0.1
requests==2.14.2
requests-oauthlib==0.8.0
six==1.17.0
tomli==1.2.3
typing_extensions==4.1.1
zipp==3.6.0

name: clarity_scripts
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - asana==0.6.7
      - attrs==22.2.0
      - cached-property==1.5.2
      - egcg-core==0.8.1
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - jinja2==2.8
      - markupsafe==2.0.1
      - oauthlib==3.2.2
      - packaging==21.3
      - pluggy==1.0.0
      - py==1.11.0
      - pyclarity-lims==0.4.8
      - pyparsing==3.1.4
      - pytest==7.0.1
      - pyyaml==6.0.1
      - requests==2.14.2
      - requests-oauthlib==0.8.0
      - six==1.17.0
      - tomli==1.2.3
      - typing-extensions==4.1.1
      - zipp==3.6.0
prefix: /opt/conda/envs/clarity_scripts

tests/test_assign_to_workflow_preseqlab.py::TestAssignWorkflowPreSeqLab::test_assign tests/test_assign_to_workflow_preseqlab.py::TestAssignWorkflowPreSeqLab::test_finish_step
tests/test_assign_to_workflow_preseqlab.py::TestEPP::test_init tests/test_assign_to_workflow_preseqlab.py::TestAssignWorkflowPreSeqLab::test_init
scripts/assign_workflow_preseqlab.py
scripts/assign_workflow_preseqlab.py