diff --git a/environment.yml b/environment.yml
index f64d06a7133dbcea3db8ec4b3610aaeffb882891..0b38e3f5d46ac533d6cb8d541b56e3c1d45c0468 100644
--- a/environment.yml
+++ b/environment.yml
@@ -6,10 +6,12 @@ dependencies:
   - alembic=1.4
   - astropy=4.0
   - beautifulsoup4=4.9
+  - beaker=1.11.0
   - blessings=1.7
   - conda-build=3.18
   - cx_Oracle=7.2
   - fabric=2.5
+  - funcsigs=1.0.2
   - jxmlease=1.0
   - lxml=4.5
   - mysqlclient=1.4
@@ -21,7 +23,9 @@ dependencies:
   - pycapo=0.3.0
   - pyopenssl=19.1.0
   - pyramid=1.10
+  - pyramid_beaker=0.8
   - pyramid_debugtoolbar=4.5
+  - pyramid_tm=2.2.1
   - pysftp=0.2.9
   - pytest=5.4
   - python=3.8
@@ -29,5 +33,7 @@ dependencies:
   - simplejson=3.17
   - sqlalchemy=1.3
   - tqdm=4.46
+  - transaction=3.0.0
   - waitress=1.4
-  - zc.buildout=2.13.2
\ No newline at end of file
+  - zc.buildout=2.13.2
+  - zope.sqlalchemy=1.1
diff --git a/services/workflow/MANIFEST.in b/services/workflow/MANIFEST.in
new file mode 100644
index 0000000000000000000000000000000000000000..5e84c8812c023a08f88d019026c11f34ebcde103
--- /dev/null
+++ b/services/workflow/MANIFEST.in
@@ -0,0 +1,5 @@
+include *.txt *.ini *.cfg *.rst *.wsgi *.sh *.spec *.md *.py
+recursive-include services *.ico *.png *.svg *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml *.ttf *.woff *.woff2
+include MANIFEST.in
+include README.txt
+include workflow/*
diff --git a/services/workflow/README.txt b/services/workflow/README.txt
new file mode 100644
index 0000000000000000000000000000000000000000..97c634472eac4749d2358fc8b07c3a04e0135f75
--- /dev/null
+++ b/services/workflow/README.txt
@@ -0,0 +1 @@
+# Workflow: the Workspaces Workflow Service
diff --git a/services/workflow/development.ini b/services/workflow/development.ini
new file mode 100644
index 0000000000000000000000000000000000000000..005980125efd6aa0ffd4627d40140cf53e7c14cf
--- /dev/null
+++ b/services/workflow/development.ini
@@ -0,0 +1,49 @@
+[app:main]
+use = egg:workflow
+pyramid.includes =
+    pyramid_debugtoolbar
+    pyramid_tm
+pyramid.reload_all = true
+
+session.cookie_expires = true
+session.auto = true
+
+[server:main]
+use = egg:waitress#main
+listen = 0.0.0.0:3456
+
+
+[loggers]
+keys = root, workflow
+
+[handlers]
+keys = console, filelog
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[logger_workflow]
+level = DEBUG
+handlers =
+qualname = workflow
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[handler_filelog]
+class = logging.handlers.TimedRotatingFileHandler
+args = ('%(here)s/workflow.log','midnight',1,3)
+level = INFO
+formatter = generic
+# When using the TimedRotatingFileHandler, we probably should set up the rollover time
+# and other aspects of the log to strike the balance between disk usage and log history
+
+[formatter_generic]
+format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
diff --git a/services/workflow/production.ini b/services/workflow/production.ini
new file mode 100644
index 0000000000000000000000000000000000000000..d1430a9b8c25dff1eeacadaebf6bd3869bb15e5d
--- /dev/null
+++ b/services/workflow/production.ini
@@ -0,0 +1,22 @@
+###
+# app configuration
+# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
+###
+
+[app:main]
+use = egg:workflow
+
+session.cookie_expires = true
+session.auto = true
+
+pyramid.reload_templates = false
+pyramid.debug_authorization = false
+pyramid.debug_notfound = false
+pyramid.debug_routematch = false
+pyramid.default_locale_name = en
+pyramid.includes = pyramid_tm
+
+
+[server:main]
+use = egg:waitress#main
+listen = 0.0.0.0:6543
diff --git a/services/workflow/setup.py b/services/workflow/setup.py
new file mode 100644
index 0000000000000000000000000000000000000000..fc677861a78142608b9af3e093d063d990f2c4a1
--- /dev/null
+++ b/services/workflow/setup.py
@@ -0,0 +1,104 @@
+"""A setuptools based setup module.
+
+See:
+https://packaging.python.org/en/latest/distributing.html
+https://github.com/pypa/sampleproject
+"""
+# To use a consistent encoding
+from codecs import open
+from os import path
+
+# Always prefer setuptools over distutils
+from setuptools import setup, find_packages
+
+# For matching the version string.
+import re
+
+this_module = 'workflow'
+here = path.abspath(path.dirname(__file__))
+
+# Get the long description from the README file
+with open(path.join(here, 'README.txt'), encoding='utf-8') as f:
+    long_description = f.read()
+
+
+def read(*parts):
+    with open(path.join(here, *parts), 'r') as fp:
+        return fp.read()
+
+
+def find_version(*file_paths):
+    version_file = read(*file_paths)
+    version_match = re.search(r"^___version___ = ['\"]([^'\"]*)['\"]",
+                              version_file, re.M)
+    if version_match:
+        return version_match.group(1)
+    raise RuntimeError("Unable to find version string.")
+
+
+requires = [
+    'cornice',
+    'pycapo',
+    'pyramid',
+    'pyramid_beaker',
+    'pyramid_debugtoolbar',
+    'pyramid_tm',
+    'requests',
+    'schema',
+    'sqlalchemy',
+    'waitress',
+    'zope.sqlalchemy'
+]
+
+setup(
+    name=this_module,
+
+    # Versions should comply with PEP440.  For a discussion on single-sourcing
+    # the version across setup.py and the project code, see
+    # https://packaging.python.org/en/latest/single_source_version.html
+    version=find_version('src/' + this_module, '_version.py'),
+
+    description='Workflow: the Workspaces Workflow Service',
+    long_description=long_description,
+
+    # Author details
+    author='Daniel Lyons',
+    author_email='dlyons@nrao.edu',
+
+    # Choose your license
+    license='GPL',
+
+    # See https://pypi.python.org/pypi?%3Aaction=list_classifiers
+    classifiers=[
+        # How mature is this project? Common values are
+        #   3 - Alpha
+        #   4 - Beta
+        #   5 - Production/Stable
+        'Development Status :: 4 - Beta',
+
+        # Indicate who your project is intended for
+        'Intended Audience :: Developers',
+        'Topic :: Software Development :: Build Tools',
+
+        # Pick your license as you wish (should match "license" above)
+        'License :: OSI Approved :: GPL License',
+
+        # Specify the Python versions you support here. In particular, ensure
+        # that you indicate whether you support Python 2, Python 3 or both.
+        'Programming Language :: Python :: 3.6',
+    ],
+
+    install_requires=requires,
+    extras_require={
+        'dev': [
+            'pyramid_debugtoolbar',
+        ],
+    },
+    package_dir={'': 'src'},
+    packages=find_packages(),
+    entry_points={
+        'paste.app_factory': [
+            'main = workflow:main'
+        ],
+    },
+)
diff --git a/services/workflow/src/workflow/__init__.py b/services/workflow/src/workflow/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..587758fdfe8269a8930781cc2ae257119b130618
--- /dev/null
+++ b/services/workflow/src/workflow/__init__.py
@@ -0,0 +1,23 @@
+from schema import create_engine
+from pyramid.config import Configurator
+from pyramid_beaker import session_factory_from_settings
+from sqlalchemy.orm import scoped_session, sessionmaker
+from zope.sqlalchemy import ZopeTransactionExtension
+from pyramid.renderers import JSONP
+
+DB = {}
+
+
+def main(global_config, **settings):
+    # DB['SDM'] = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
+
+    with Configurator(settings=settings) as config:
+        session_factory = session_factory_from_settings(settings)
+        config.include('cornice')
+        config.set_session_factory(session_factory)
+        config.add_renderer('jsonp', JSONP(param_name='callback'))
+
+        config.include('pyramid_beaker')
+
+        config.scan('workflow.workflow')
+        return config.make_wsgi_app()
diff --git a/services/workflow/src/workflow/_version.py b/services/workflow/src/workflow/_version.py
new file mode 100644
index 0000000000000000000000000000000000000000..f27d146a3f39885ce269bacf9ab4510254147c8d
--- /dev/null
+++ b/services/workflow/src/workflow/_version.py
@@ -0,0 +1,2 @@
+""" Version information for this package, don't put anything else here. """
+___version___ = '4.0.0a1.dev1'
diff --git a/services/workflow/src/workflow/workflow.py b/services/workflow/src/workflow/workflow.py
new file mode 100644
index 0000000000000000000000000000000000000000..4ebc22599bd9e6e5a14f3a77c3aa21e7baa52a58
--- /dev/null
+++ b/services/workflow/src/workflow/workflow.py
@@ -0,0 +1,22 @@
+from cornice.resource import resource
+
+
+WORKFLOWS = [{'id': 1, 'name': 'foo'}]
+
+
+@resource(collection_path="/workflows", path="/workflows/{id}")
+class Workflow:
+    def __init__(self, request, context=None):
+        self.request = request
+
+    def collection_get(self):
+        return WORKFLOWS
+
+    def collection_post(self):
+        WORKFLOWS.append(self.request.json_body)
+        return True
+
+    def get(self):
+        return WORKFLOWS[int(self.request.matchdict['id'])]
+
+
diff --git a/services/workflow/workflow.wsgi b/services/workflow/workflow.wsgi
new file mode 100644
index 0000000000000000000000000000000000000000..d0452ee043312de9e5f3d2a1a85b1b5dced7392d
--- /dev/null
+++ b/services/workflow/workflow.wsgi
@@ -0,0 +1,23 @@
+# workflow.wsgi
+from pyramid.paster import get_app, setup_logging
+import os
+
+import socket
+
+fqdn = socket.getfqdn()
+if "mcilroy" in fqdn:
+    os.environ['CAPO_PROFILE'] = 'dsoc-prod'
+elif "wirth" in fqdn:
+    os.environ['CAPO_PROFILE'] = 'nmtest'
+    #os.environ['CAPO_PROFILE'] = 'dsoc-dev'
+elif "hamilton" in fqdn:
+    os.environ['CAPO_PROFILE'] = 'dsoc-test'
+else:
+    os.environ['CAPO_PROFILE'] = 'local'
+
+# os.environ['CAPO_PROFILE'] = 'products'
+
+ini_path = os.path.dirname(__file__) + '/production.ini'
+
+setup_logging(ini_path)
+application = get_app(ini_path, 'main')