diff --git a/services/workflow/src/workflow/server.py b/services/workflow/src/workflow/server.py
index a3a066f0d26353e51f0168e441112ba32957e65c..544248f4bb2eb5e17c48b09703828029b7655a9e 100644
--- a/services/workflow/src/workflow/server.py
+++ b/services/workflow/src/workflow/server.py
@@ -24,12 +24,12 @@ def lookup_workflow(request):
     return request.info.lookup_workflow_definition(request.matchdict['name'])
 
 
-# def lookup_request(request):
-#     return request.info.lookup_workflow_request(request.matchdict['requests'])
+def lookup_request(request):
+    return request.info.lookup_workflow_request(request.matchdict['request_id'])
 
 
 def lookup_file(request):
-    return lookup_workflow(request)['files'][request.matchdict['filename']]
+    return next(file for file in lookup_workflow(request).files if file.filename == request.matchdict['filename'])
 
 
 @view_defaults(route_name='workflows', renderer='json')
@@ -52,8 +52,31 @@ class WorkflowRestService:
         """
         return self.request.info.all_workflows()
 
-    @view_config(request_method='POST', route_name='create_workflow')
-    def create_workflow(self):
+    @view_config(request_method='GET', route_name='workflow')
+    def get_workflow(self):
+        """
+        Look up a workflow request
+
+        Audience: front-end
+        :return:
+        """
+        return self.request.context
+
+
+@view_defaults(route_name='workflow_request', renderer='json')
+class WorkflowRequestRestService:
+    """
+    Services for the user-submitted files attached to workflows.
+    """
+    def __init__(self, request):
+        self.request = request
+
+    @view_config(request_method='GET')
+    def get_workflow_request(self):
+        return self.request.context
+
+    @view_config(request_method='POST', route_name='create_workflow_request')
+    def create_workflow_request(self):
         """
         Create a new workflow request from the name/arguments supplied.
 
@@ -63,21 +86,10 @@ class WorkflowRestService:
         # all we should have to do here is take the WorkflowRequest from the context and
         # hand it to WorkflowInfo to save it, but we're still conflating
         # workflows and workflow requests right now
-        request = self.request.info.create_workflow_request(
-            workflow_name=self.request.context.workflow_name, argument=self.request.GET.getall('args'))
+        request = self.request.info.create_workflow_request(self.request.context, self.request.GET.getall('args'))
         return request
 
-    @view_config(request_method='GET', route_name='workflow')
-    def get_workflow(self):
-        """
-        Look up a workflow request
-
-        Audience: front-end
-        :return:
-        """
-        return self.request.context
-
-    @view_config(request_method='POST', route_name='submit_workflow')
+    @view_config(request_method='POST', route_name='submit_workflow_request')
     def submit_workflow(self):
         """
         Submit this workflow request for processing.
@@ -89,7 +101,7 @@ class WorkflowRestService:
         return self.request.workflows.execute(self.request.context)
 
 
-@view_defaults(route_name='workflow_files', renderer='json')
+@view_defaults(route_name='workflow_request_files', renderer='json')
 class WorkflowFilesRestService:
     """
     Services for the user-submitted files attached to workflows.
@@ -97,7 +109,7 @@ class WorkflowFilesRestService:
     def __init__(self, request):
         self.request = request
 
-    @view_config(request_method='POST', route_name='add_file')
+    @view_config(request_method='PUT', route_name='add_file_to_workflow_request')
     def add_file(self):
         """
         Add a file to this workflow request.
@@ -105,9 +117,9 @@ class WorkflowFilesRestService:
         Audience: front-end and CLI
         """
         print('Adding a file')
-        file = self.request.info.save_file(request_id=self.request.GET.get("request"),
-                                           filename=self.request.GET.get("filename"),
-                                           content=self.request.GET.get("content"))
+        file = self.request.info.save_file(request_id=self.request.matchdict['request_id'],
+                                           filename=self.request.matchdict['filename'],
+                                           content=self.request.body)
         return file
 
     @view_config(request_method='GET')
@@ -194,13 +206,20 @@ def main(global_config, **settings):
         # make workflow_service available for use in Pyramid
         config.add_request_method(lambda r: WorkflowService(r.info), 'workflows', reify=True)
 
+        # GET  /workflows                            <- list of workflows
+        # GET  /workflows/null                       <- info about the null workflow
+        # POST /workflows/null/requests/create       <- create a request for the null workflow
+        # PUT  /workflows/requests/23/files/foo.txt  <- attach foo.txt to request #23 on workflow null
+        # POST /workflows/requests/23/submit         <- launch request #23
+
         config.add_route('workflows', '/workflows')
         config.add_route('workflow', '/workflows/{name}', factory=lookup_workflow)
-        config.add_route('create_workflow', '/workflows/{name}/create', factory=lookup_workflow)
-        config.add_route('submit_workflow', '/workflows/{name}/submit', factory=lookup_workflow)
-        config.add_route('add_file', '/workflows/file')
-        config.add_route('workflow_files', '/workflows/{name}/files', factory=lookup_workflow)
-        config.add_route('workflow_file', '/workflows/{name}/files/{filename}', factory=lookup_file)
+        config.add_route('create_workflow_request', '/workflows/{name}/requests/create', factory=lookup_workflow)
+        config.add_route('workflow_request', '/workflows/requests/{request_id}', factory=lookup_request)
+        config.add_route('workflow_request_files', '/workflows/requests/{request_id}/files', factory=lookup_request)
+        config.add_route('add_file_to_workflow_request', '/workflows/requests/{request_id}/files/{filename}', factory=lookup_request)
+        config.add_route('submit_workflow_request', '/workflows/requests/{request_id}/submit', factory=lookup_request)
+        config.add_route('workflow_file', '/workflows/requests/{request_id}/files/{filename}', factory=lookup_file)
 
         config.include('pyramid_beaker')
         config.scan('.')
diff --git a/shared/workspaces/src/workspaces/services.py b/shared/workspaces/src/workspaces/services.py
index d3b3959f5fcf651b493d9250c61ae39c570a6a47..d71da7331fbe66e70863e99fcefa5326a28a6954 100644
--- a/shared/workspaces/src/workspaces/services.py
+++ b/shared/workspaces/src/workspaces/services.py
@@ -398,6 +398,14 @@ class WorkflowService(WorkflowServiceIF):
     """
     Executes workflows; should be a freestanding service.
     """
+    # The next few things that need to happen here are in response to
+    # WorkflowEvents that we receive from wf_monitor as the workflow
+    # execution evolves. So we have to set up listening at some point
+    # in this class
+    def __init__(self, info: WorkflowInfoIF):
+        # 1. Start listening for events from the wf_monitor stream
+        # self.channel = workflow_events.listen(self.on_workflow_event)
+        self.info = info
 
     def execute(self, workflow_name: str, argument: Dict, files: List[Path]):
         """
@@ -405,11 +413,10 @@ class WorkflowService(WorkflowServiceIF):
         """
 
         # 1. look up workflow, returns workflow
-        info = WorkflowInfo(self)
-        definition = info.lookup_workflow_definition(workflow_name)
+        definition = self.info.lookup_workflow_definition(workflow_name)
 
         # 2. create and save request, return request id
-        record = info.create_workflow_request(workflow_name, argument)
+        record = self.info.create_workflow_request(workflow_name, argument)
 
         # 3. render templates to files, returns list of rendered files
         contents = definition.render_templates(argument, files)
@@ -475,15 +482,6 @@ class WorkflowService(WorkflowServiceIF):
         # return the logfile
         return logfile
 
-    # The next few things that need to happen here are in response to
-    # WorkflowEvents that we receive from wf_monitor as the workflow
-    # execution evolves. So we have to set up listening at some point
-    # in this class
-    def __init__(self):
-        # 1. Start listening for events from the wf_monitor stream
-        # self.channel = workflow_events.listen(self.on_workflow_event)
-        pass
-
     def on_workflow_event(
         self, event: WorkflowEvent, request_record: WorkflowRequest, tmp_folder: Path
     ):
@@ -564,7 +562,7 @@ class WorkflowInfo(WorkflowInfoIF):
 
     def create_workflow_request(
             self,
-            workflow_name: str,
+            workflow: Workflow,
             argument: Dict) -> WorkflowRequest:
         """
         Create new workflow request and save to database
@@ -572,7 +570,7 @@ class WorkflowInfo(WorkflowInfoIF):
         :param argument: workflow arguments
         :return: new WorkflowRequest
         """
-        request = WorkflowRequest(workflow_name=workflow_name, argument=argument)
+        request = WorkflowRequest(workflow_name=workflow.workflow_name, argument=argument)
         self.save_request(request)
         return request