diff --git a/api/api.py b/api/api.py
index 52d9a2f581bc2c2b597ea64e8f22bd357afbae13..b8b1a1d700b9ccca39330b7d67ce178e41aaa263 100644
--- a/api/api.py
+++ b/api/api.py
@@ -7,27 +7,28 @@ import traceback
 import webapp2
 import webapp2_extras.routes
 
-from . import base
-from .jobs.handlers import JobsHandler, JobHandler, GearsHandler, GearHandler, RulesHandler
 from . import encoder
 from . import root
 from . import util
 from . import config
-from . import centralclient
-from . import download
-from . import upload
-from .handlers import listhandler
-from .handlers import userhandler
-from .handlers import grouphandler
-from .handlers import containerhandler
-from .handlers import collectionshandler
-from .handlers import resolvehandler
-from .handlers import searchhandler
-from .handlers import schemahandler
-from .handlers import reporthandler
-from .handlers import devicehandler
 from .request import SciTranRequest
 
+from .centralclient               import CentralClient
+from .download                    import Download
+from .handlers.collectionshandler import CollectionsHandler
+from .handlers.confighandler      import Config, Version
+from .handlers.containerhandler   import ContainerHandler
+from .handlers.devicehandler      import DeviceHandler
+from .handlers.grouphandler       import GroupHandler
+from .handlers.listhandler        import AnalysesHandler, ListHandler, FileListHandler, NotesListHandler, PermissionsListHandler, TagsListHandler
+from .handlers.reporthandler      import ReportHandler
+from .handlers.resolvehandler     import ResolveHandler
+from .handlers.schemahandler      import SchemaHandler
+from .handlers.searchhandler      import SearchHandler
+from .handlers.userhandler        import UserHandler
+from .jobs.handlers               import JobsHandler, JobHandler, GearsHandler, GearHandler, RulesHandler
+from .upload                      import Upload
+
 log = config.log
 
 try:
@@ -35,182 +36,237 @@ try:
 except ImportError:
     uwsgi = None
 
-class Config(base.RequestHandler):
+routing_regexes = {
 
-    def get(self):
-        """Return public Scitran configuration information."""
-        return config.get_public_config()
+    # Group ID: 2-32 characters of form [0-9a-z.@_-]. Start and ends with alphanum.
+    'gid': '[0-9a-z][0-9a-z.@_-]{0,30}[0-9a-z]',
 
-    def get_js(self):
-        """Return scitran config in javascript format."""
-        self.response.write(
-            'config = ' +
-            json.dumps( self.get(), sort_keys=True, indent=4, separators=(',', ': '), default=encoder.custom_json_serializer,) +
-            ';'
-        )
+    # Container ID: 24-character hex
+    'cid': '[0-9a-f]{24}',
 
-class Version(base.RequestHandler):
+    # Site ID: <= 24-character alphanum
+    'sid': '[0-9a-z_]{0,24}',
 
-    def get(self):
-        """Return database schema version"""
-        return config.get_version()
+    # User ID: any length, [0-9a-z.@_-]
+    'uid': '[0-9a-z.@_-]*',
 
-#regexes used in routing table:
-routing_regexes = {
-    # group id regex
-    # length between 2 and 32 characters
-    # allowed characters are [0-9a-z.@_-] (start and ends only with [0-9a-z])
-    'group_id_re': '[0-9a-z][0-9a-z.@_-]{0,30}[0-9a-z]',
-    # container id regex
-    # hexadecimal string exactly of length 24
-    'cid_re': '[0-9a-f]{24}',
-    # site id regex
-    # length less than 24 characters
-    # allowed characters are [0-9a-z]
-    'site_id_re': '[0-9a-z_]{0,24}',
-    # user id regex
-    # any length, allowed chars are [0-9a-z.@_-]
-    'user_id_re': '[0-9a-z.@_-]*',
-    # container name regex
-    # possible values are projects, sessions, acquisitions or collections
-    'cont_name_re': 'projects|sessions|acquisitions|collections',
-    # tag regex
-    # length between 3 and 24 characters
-    # any character allowed except '/''
-    'tag_re': '[^/]{1,32}',
-    # filename regex
-    # any character allowed except '/'
-    'filename_re': '[^/]+',
-    # note id regex
-    # hexadecimal string exactly of length 24
-    'note_id_re': '[0-9a-f]{24}',
-    # schema regex
-    # example: schema_path/schema.json
-    'schema_re': r'[^/.]{3,60}/[^/.]{3,60}\.json'
+    # Container name
+    'cname': 'projects|sessions|acquisitions|collections',
+
+    # Tag name
+    'tag': '[^/]{1,32}',
+
+    # Filename
+    'fname': '[^/]+',
+
+    # Note ID
+    'nid': '[0-9a-f]{24}',
+
+    # Schema path
+    'schema': r'[^/.]{3,60}/[^/.]{3,60}\.json'
 }
 
-def _format(route):
-    return route.format(**routing_regexes)
-
-routes = [
-    webapp2.Route(r'/api',                  root.Root),
-    webapp2_extras.routes.PathPrefixRoute(r'/api', [
-        webapp2.Route(r'/download',         download.Download, handler_method='download', methods=['GET', 'POST'], name='download'),
-        webapp2.Route(r'/upload/<strategy:label|uid|uid-match>',           upload.Upload, handler_method='upload', methods=['POST']),
-        webapp2.Route(r'/clean-packfiles',  upload.Upload, handler_method='clean_packfile_tokens', methods=['POST']),
-        webapp2.Route(r'/engine',           upload.Upload, handler_method='engine', methods=['POST']),
-        webapp2.Route(r'/sites',            centralclient.CentralClient, handler_method='sites', methods=['GET']),
-        webapp2.Route(r'/register',         centralclient.CentralClient, handler_method='register', methods=['POST']),
-        webapp2.Route(r'/resolve',         resolvehandler.ResolveHandler, handler_method='resolve', methods=['POST']),
-        webapp2.Route(r'/config',           Config, methods=['GET']),
-        webapp2.Route(r'/config.js',        Config, handler_method='get_js', methods=['GET']),
-        webapp2.Route(r'/version',          Version, methods=['GET']),
-    ]),
-    webapp2.Route(r'/api/users',            userhandler.UserHandler, handler_method='get_all', methods=['GET']),
-    webapp2.Route(r'/api/users',            userhandler.UserHandler, methods=['POST']),
-    webapp2_extras.routes.PathPrefixRoute(r'/api/users', [
-        webapp2.Route(r'/self',                                 userhandler.UserHandler, handler_method='self', methods=['GET']),
-        webapp2.Route(r'/self/avatar',                          userhandler.UserHandler, handler_method='self_avatar', methods=['GET']),
-        webapp2.Route(r'/self/key',                             userhandler.UserHandler, handler_method='generate_api_key',methods=['POST']),
-        webapp2.Route(_format(r'/<_id:{user_id_re}>'),          userhandler.UserHandler, name='user'),
-        webapp2.Route(_format(r'/<uid:{user_id_re}>/groups'),   grouphandler.GroupHandler, handler_method='get_all', methods=['GET'], name='groups'),
-        webapp2.Route(_format(r'/<uid:{user_id_re}>/avatar'),   userhandler.UserHandler, handler_method='avatar', methods=['GET'], name='avatar'),
-    ]),
-    webapp2.Route(r'/api/devices',                              devicehandler.DeviceHandler, name='device_list', handler_method='get_all', methods=['GET']),
-    webapp2.Route(r'/api/devices',                              devicehandler.DeviceHandler, methods=['POST']),
-    webapp2_extras.routes.PathPrefixRoute(r'/api/devices', [
-        webapp2.Route(r'/status',                               devicehandler.DeviceHandler, handler_method='get_status', methods=['GET']),
-        webapp2.Route(r'/self',                                 devicehandler.DeviceHandler, handler_method='get_self', methods=['GET']),
-        webapp2.Route(_format(r'/<device_id:[^/]+>'),           devicehandler.DeviceHandler, name='device_details', methods=['GET']),
-    ]),
-    webapp2.Route(r'/api/jobs',             JobsHandler),
-    webapp2_extras.routes.PathPrefixRoute(r'/api/jobs', [
-        webapp2.Route(r'/next',             JobsHandler, handler_method='next', methods=['GET']),
-        webapp2.Route(r'/stats',            JobsHandler, handler_method='stats', methods=['GET']),
-        webapp2.Route(r'/reap',             JobsHandler, handler_method='reap_stale', methods=['POST']),
-        webapp2.Route(r'/add',              JobsHandler, handler_method='add', methods=['POST']),
-        webapp2.Route(r'/<:[^/]+>',         JobHandler,  name='job'),
-        webapp2.Route(r'/<:[^/]+>/config.json', JobHandler,  name='job', handler_method='get_config'),
-        webapp2.Route(r'/<:[^/]+>/retry',   JobHandler,  name='job', handler_method='retry', methods=['POST']),
-    ]),
-    webapp2.Route(r'/api/gears',             GearsHandler),
-    webapp2_extras.routes.PathPrefixRoute(r'/api/gears', [
-        webapp2.Route(r'/<:[^/]+>',            GearHandler),
-        webapp2.Route(r'/<:[^/]+>/invocation', GearHandler, handler_method='get_invocation'),
-        webapp2.Route(r'/<:[^/]+>/suggest/<:[^/]+>/<:[^/]+>', GearHandler, handler_method='suggest'),
+def route(path, target, h=None, m=None, name=None):
+
+    # https://webapp2.readthedocs.io/en/latest/api/webapp2.html#webapp2.Route
+    return webapp2.Route(
+        # re.compile(path)
+        path.format(**routing_regexes),
+        target,
+        handler_method=h,
+        methods=m,
+        name=name
+    )
+
+def prefix(path, routes):
+
+    # https://webapp2.readthedocs.io/en/latest/api/webapp2_extras/routes.html#webapp2_extras.routes.PathPrefixRoute
+    return webapp2_extras.routes.PathPrefixRoute(
+        path.format(**routing_regexes),
+        routes
+    )
+
+endpoints = [
+    route('/api',                  root.Root),
+    prefix('/api', [
+
+        # System configuration
+
+        route('/config',           Config,              m=['GET']),
+        route('/config.js',        Config,  h='get_js', m=['GET']),
+        route('/version',          Version,             m=['GET']),
+
+
+        # General-purpose upload & download
+
+        route('/download',                              Download, h='download',              m=['GET', 'POST']),
+        route('/upload/<strategy:label|uid|uid-match>', Upload,   h='upload',                m=['POST']),
+        route('/clean-packfiles',                       Upload,   h='clean_packfile_tokens', m=['POST']),
+        route('/engine',                                Upload,   h='engine',                m=['POST']),
+
+
+        # Top-level endpoints
+
+        route('/resolve',                           ResolveHandler, h='resolve', m=['POST']),
+        route('/schemas/<schema:{schema}>',         SchemaHandler,               m=['GET']),
+        route('/report/<report_type:site|project>', ReportHandler,               m=['GET']),
+
+
+        # Search
+
+        route('/search',                     SearchHandler, h='advanced_search', m=['POST']),
+        route('/search/files',               SearchHandler, h='get_datatree',    m=['GET']),
+        route('/search/<cont_name:{cname}>', SearchHandler,                      m=['GET']),
+
+
+        # Users
+
+        route( '/users',                   UserHandler, 'get_all', m=['GET']),
+        route( '/users',                   UserHandler,            m=['POST']),
+        prefix('/users', [
+            route('/self',                 UserHandler, h='self',            m=['GET']),
+            route('/self/avatar',          UserHandler, h='self_avatar',     m=['GET']),
+            route('/self/key',             UserHandler, h='generate_api_key',m=['POST']),
+
+            route('/<_id:{uid}>',          UserHandler),
+            route('/<uid:{uid}>/groups',   GroupHandler,                h='get_all',          m=['GET']),
+            route('/<uid:{uid}>/avatar',   UserHandler,                 h='avatar',           m=['GET']),
+            route('/<uid:{uid}>/<cont_name:{cname}>', ContainerHandler, h='get_all_for_user', m=['GET']),
+
+        ]),
+
+
+        # Jobs & gears
+
+        route( '/jobs',                    JobsHandler),
+        prefix('/jobs', [
+            route('/next',                 JobsHandler, h='next',       m=['GET']),
+            route('/stats',                JobsHandler, h='stats',      m=['GET']),
+            route('/reap',                 JobsHandler, h='reap_stale', m=['POST']),
+            route('/add',                  JobsHandler, h='add',        m=['POST']),
+            route('/<:[^/]+>',             JobHandler),
+            route('/<:[^/]+>/config.json', JobHandler,  h='get_config'),
+            route('/<:[^/]+>/retry',       JobHandler,  h='retry',      m=['POST']),
+        ]),
+
+        route('/gears',                                  GearsHandler),
+        prefix('/gears', [
+            route('/<:[^/]+>',                           GearHandler),
+            route('/<:[^/]+>/invocation',                GearHandler, h='get_invocation'),
+            route('/<:[^/]+>/suggest/<:[^/]+>/<:[^/]+>', GearHandler, h='suggest'),
+        ]),
+
+        route('/rules', RulesHandler),
+
+
+        # Devices
+
+        route( '/devices',              DeviceHandler, h='get_all',    m=['GET']),
+        route( '/devices',              DeviceHandler,                 m=['POST']),
+        prefix('/devices', [
+            route('/status',            DeviceHandler, h='get_status', m=['GET']),
+            route('/self',              DeviceHandler, h='get_self',   m=['GET']),
+            route('/<device_id:[^/]+>', DeviceHandler,                 m=['GET']),
+        ]),
+
+
+        # Groups
+
+        route('/groups',             GroupHandler, h='get_all', m=['GET']),
+        route('/groups',             GroupHandler,              m=['POST']),
+        route('/groups/<_id:{gid}>', GroupHandler,              m=['GET', 'DELETE', 'PUT']),
+
+        prefix('/<cont_name:groups>', [
+            route('/<cid:{gid}>/<list_name:roles>',                          ListHandler,     m=['POST']),
+            route('/<cid:{gid}>/<list_name:roles>/<site:{sid}>/<_id:{uid}>', ListHandler,     m=['GET', 'PUT', 'DELETE']),
+
+            route('/<cid:{gid}>/<list_name:tags>',                           TagsListHandler, m=['POST']),
+            route('/<cid:{gid}>/<list_name:tags>/<value:{tag}>',             TagsListHandler, m=['GET', 'PUT', 'DELETE']),
+        ]),
+
+
+        # Projects
+
+        prefix('/projects', [
+            route('/groups',               ContainerHandler, h='get_groups_with_project',      m=['GET']),
+            route('/recalc',               ContainerHandler, h='calculate_project_compliance', m=['POST']),
+            route('/<cid:{cid}>/template', ContainerHandler, h='set_project_template',         m=['POST']),
+            route('/<cid:{cid}>/template', ContainerHandler, h='delete_project_template',      m=['DELETE']),
+            route('/<cid:{cid}>/recalc',   ContainerHandler, h='calculate_project_compliance', m=['POST']),
+        ]),
+
+
+        # Sessions
+
+        route('/sessions/<cid:{cid}>/jobs',    ContainerHandler, h='get_jobs', m=['GET']),
+
+
+        # Collections
+
+        route( '/collections',                 CollectionsHandler, h='get_all',                    m=['GET']),
+        route( '/collections',                 CollectionsHandler,                                 m=['POST']),
+        prefix('/collections', [
+            route('/curators',                 CollectionsHandler, h='curators',                   m=['GET']),
+            route('/<cid:{cid}>',              CollectionsHandler,                                 m=['GET', 'PUT', 'DELETE']),
+            route('/<cid:{cid}>/sessions',     CollectionsHandler, h='get_sessions',               m=['GET']),
+            route('/<cid:{cid}>/acquisitions', CollectionsHandler, h='get_acquisitions',           m=['GET']),
+        ]),
+
+
+        # Collections / Projects
+
+        prefix('/<cont_name:collections|projects>', [
+            prefix('/<cid:{cid}>', [
+                route('/<list_name:permissions>',                          PermissionsListHandler, m=['POST']),
+                route('/<list_name:permissions>/<site:{sid}>/<_id:{uid}>', PermissionsListHandler, m=['GET', 'PUT', 'DELETE']),
+            ]),
+        ]),
+
+
+        # Containers
+
+        route('/<cont_name:{cname}>', ContainerHandler, name='cont_list', h='get_all', m=['GET']),
+        route('/<cont_name:{cname}>', ContainerHandler, m=['POST']),
+
+        prefix('/<cont_name:{cname}>', [
+            route('/<cid:{cid}>',                          ContainerHandler,                    m=['GET','PUT','DELETE']),
+
+            prefix('/<cid:{cid}>', [
+
+                route('/<list_name:tags>',                 TagsListHandler,                     m=['POST']),
+                route('/<list_name:tags>/<value:{tag}>',   TagsListHandler,                     m=['GET', 'PUT', 'DELETE']),
+
+                route('/packfile-start',                   FileListHandler, h='packfile_start', m=['POST']),
+                route('/packfile',                         FileListHandler, h='packfile',       m=['POST']),
+                route('/packfile-end',                     FileListHandler, h='packfile_end'),
+                route('/<list_name:files>',                FileListHandler,                     m=['POST']),
+                route('/<list_name:files>/<name:{fname}>', FileListHandler,                     m=['GET', 'DELETE']),
+
+
+                route('/<list_name:analyses>', AnalysesHandler, m=['POST']),
+                # Could be in a prefix. Had weird syntax highlighting issues so leaving for another day
+                route('/<list_name:analyses>/<_id:{cid}>',                       AnalysesHandler,                  m=['GET', 'DELETE']),
+                route('/<list_name:analyses>/<_id:{cid}>/files',                 AnalysesHandler, h='download',    m=['GET']),
+                route('/<list_name:analyses>/<_id:{cid}>/files/<name:{fname}>',  AnalysesHandler, h='download',    m=['GET']),
+                route('/<list_name:analyses>/<_id:{cid}>/notes',                 AnalysesHandler, h='add_note',    m=['POST']),
+                route('/<list_name:analyses>/<_id:{cid}>/notes/<note_id:{cid}>', AnalysesHandler, h='delete_note', m=['DELETE']),
+                route('/<list_name:notes>',                                      NotesListHandler,                 m=['POST']),
+                route('/<list_name:notes>/<_id:{nid}>',                          NotesListHandler, name='notes',   m=['GET', 'PUT', 'DELETE']),
+            ])
+        ]),
+
+
+        # Misc (to be cleaned up later)
+
+        route('/<par_cont_name:groups>/<par_id:{gid}>/<cont_name:projects>', ContainerHandler, h='get_all', m=['GET']),
+        route('/<par_cont_name:{cname}>/<par_id:{cid}>/<cont_name:{cname}>', ContainerHandler, h='get_all', m=['GET']),
+
+
+        # Multi - site
+        route('/sites',    CentralClient, h='sites',    m=['GET']),
+        route('/register', CentralClient, h='register', m=['POST']),
+
     ]),
-    webapp2.Route(r'/api/rules',             RulesHandler),
-    webapp2.Route(r'/api/groups',                                   grouphandler.GroupHandler, handler_method='get_all', methods=['GET']),
-    webapp2.Route(r'/api/groups',                                   grouphandler.GroupHandler, methods=['POST']),
-    webapp2.Route(_format(r'/api/groups/<_id:{group_id_re}>'),      grouphandler.GroupHandler, name='group_details', methods=['GET', 'DELETE', 'PUT']),
-
-    webapp2.Route(r'/api/collections/curators',                                         collectionshandler.CollectionsHandler, handler_method='curators', methods=['GET']),
-    webapp2.Route(r'/api/collections',                                      collectionshandler.CollectionsHandler, name='colls', handler_method='get_all', methods=['GET']),
-    webapp2.Route(r'/api/collections',                                      collectionshandler.CollectionsHandler, methods=['POST']),
-
-    webapp2.Route(_format(r'/api/collections/<cid:{cid_re}>'),              collectionshandler.CollectionsHandler, name='coll_details', methods=['GET', 'PUT', 'DELETE']),
-    webapp2.Route(_format(r'/api/collections/<cid:{cid_re}>/sessions'),     collectionshandler.CollectionsHandler, name='coll_ses', handler_method='get_sessions', methods=['GET']),
-    webapp2.Route(_format(r'/api/collections/<cid:{cid_re}>/acquisitions'), collectionshandler.CollectionsHandler, name='coll_acq', handler_method='get_acquisitions', methods=['GET']),
-
-    webapp2.Route(_format(r'/api/<cont_name:{cont_name_re}>'),                          containerhandler.ContainerHandler, name='cont_list', handler_method='get_all', methods=['GET']),
-    webapp2.Route(_format(r'/api/<cont_name:{cont_name_re}>'),                          containerhandler.ContainerHandler, methods=['POST']),
-    webapp2.Route(_format(r'/api/<cont_name:{cont_name_re}>/<cid:{cid_re}>'),           containerhandler.ContainerHandler, name='cont_details', methods=['GET','PUT','DELETE']),
-    webapp2.Route(_format(r'/api/sessions/<cid:{cid_re}>/jobs'),           containerhandler.ContainerHandler, name='cont_jobs', handler_method='get_jobs', methods=['GET']),
-
-    webapp2.Route(_format(r'/api/<cont_name:groups>/<cid:{group_id_re}>/<list_name:roles>'),                                        listhandler.ListHandler, name='group_roles_post', methods=['POST']),
-    webapp2.Route(_format(r'/api/<cont_name:groups>/<cid:{group_id_re}>/<list_name:roles>/<site:{site_id_re}>/<_id:{user_id_re}>'),    listhandler.ListHandler, name='group_roles', methods=['GET', 'PUT', 'DELETE']),
-
-
-    webapp2.Route(_format(r'/api/<cont_name:groups>/<cid:{group_id_re}>/<list_name:tags>'),                                      listhandler.TagsListHandler, methods=['POST'], name='tags_post'),
-    webapp2.Route(_format(r'/api/<cont_name:groups>/<cid:{group_id_re}>/<list_name:tags>/<value:{tag_re}>'),                     listhandler.TagsListHandler, name='tags', methods=['GET', 'PUT', 'DELETE']),
-
-    webapp2.Route(_format(r'/api/<cont_name:{cont_name_re}>/<cid:{cid_re}>/<list_name:tags>'),                                      listhandler.TagsListHandler, methods=['POST'], name='tags_post'),
-    webapp2.Route(_format(r'/api/<cont_name:{cont_name_re}>/<cid:{cid_re}>/<list_name:tags>/<value:{tag_re}>'),                     listhandler.TagsListHandler, name='tags', methods=['GET', 'PUT', 'DELETE']),
-
-    webapp2.Route(_format(r'/api/<cont_name:{cont_name_re}>/<cid:{cid_re}>/packfile-start'),                                     listhandler.FileListHandler, name='packfile-start', handler_method='packfile_start', methods=['POST']),
-    webapp2.Route(_format(r'/api/<cont_name:{cont_name_re}>/<cid:{cid_re}>/packfile'),                                     listhandler.FileListHandler, name='packfile', handler_method='packfile', methods=['POST']),
-    webapp2.Route(_format(r'/api/<cont_name:{cont_name_re}>/<cid:{cid_re}>/packfile-end'),                                     listhandler.FileListHandler, name='packfile-end', handler_method='packfile_end'),
-    webapp2.Route(_format(r'/api/<cont_name:{cont_name_re}>/<cid:{cid_re}>/<list_name:files>'),                                     listhandler.FileListHandler, name='files_post', methods=['POST']),
-    webapp2.Route(_format(r'/api/<cont_name:{cont_name_re}>/<cid:{cid_re}>/<list_name:files>/<name:{filename_re}>'),            listhandler.FileListHandler, name='files',  methods=['GET', 'DELETE']),
-
-    webapp2.Route(_format(r'/api/<cont_name:collections|projects>/<cid:{cid_re}>/<list_name:permissions>'),                                     listhandler.PermissionsListHandler, name='perms_post', methods=['POST']),
-    webapp2.Route(_format(r'/api/<cont_name:collections|projects>/<cid:{cid_re}>/<list_name:permissions>/<site:{site_id_re}>/<_id:{user_id_re}>'), listhandler.PermissionsListHandler, name='perms', methods=['GET', 'PUT', 'DELETE']),
-
-    webapp2.Route(_format(r'/api/<cont_name:{cont_name_re}>/<cid:{cid_re}>/<list_name:notes>'),                                     listhandler.NotesListHandler, name='notes_post', methods=['POST']),
-    webapp2.Route(_format(r'/api/<cont_name:{cont_name_re}>/<cid:{cid_re}>/<list_name:notes>/<_id:{note_id_re}>'),                  listhandler.NotesListHandler, name='notes', methods=['GET', 'PUT', 'DELETE']),
-
-    webapp2.Route(_format(r'/api/users/<uid:{user_id_re}>/<cont_name:{cont_name_re}>'), containerhandler.ContainerHandler, name='user_conts', handler_method='get_all_for_user', methods=['GET']),
-
-    webapp2.Route(r'/api/projects/groups',                                              containerhandler.ContainerHandler, handler_method='get_groups_with_project', methods=['GET']),
-    webapp2.Route(r'/api/projects/recalc',                                              containerhandler.ContainerHandler, handler_method='calculate_project_compliance', methods=['POST']),
-    webapp2.Route(_format(r'/api/projects/<cid:{cid_re}>/template'),                    containerhandler.ContainerHandler, handler_method='set_project_template', methods=['POST']),
-    webapp2.Route(_format(r'/api/projects/<cid:{cid_re}>/template'),                    containerhandler.ContainerHandler, handler_method='delete_project_template', methods=['DELETE']),
-    webapp2.Route(_format(r'/api/projects/<cid:{cid_re}>/recalc'),             containerhandler.ContainerHandler, handler_method='calculate_project_compliance', methods=['POST']),
-
-    webapp2.Route(_format(r'/api/<par_cont_name:groups>/<par_id:{group_id_re}>/<cont_name:projects>'),          containerhandler.ContainerHandler, name='cont_sublist_groups', handler_method='get_all', methods=['GET']),
-    webapp2.Route(_format(r'/api/<par_cont_name:{cont_name_re}>/<par_id:{cid_re}>/<cont_name:{cont_name_re}>'), containerhandler.ContainerHandler, name='cont_sublist', handler_method='get_all', methods=['GET']),
-    webapp2.Route(_format(r'/api/search'),                                            searchhandler.SearchHandler, handler_method='advanced_search', name='es_proxy', methods=['POST']),
-    webapp2.Route(_format(r'/api/search/files'),                                      searchhandler.SearchHandler, handler_method='get_datatree', name='es_data', methods=['GET']),
-    webapp2.Route(_format(r'/api/search/<cont_name:{cont_name_re}>'),                 searchhandler.SearchHandler, name='es_proxy', methods=['GET']),
-    webapp2.Route(_format(r'/api/schemas/<schema:{schema_re}>'),                      schemahandler.SchemaHandler, name='schemas', methods=['GET']),
-    webapp2.Route(r'/api/report/<report_type:site|project>',                          reporthandler.ReportHandler, methods=['GET']),
-    webapp2.Route(_format(r'/api/<cont_name:{cont_name_re}>/<cid:{cid_re}>/<list_name:analyses>'),
-                                                                                      listhandler.AnalysesHandler, name='analysis_post', methods=['POST']),
-    webapp2.Route(_format(r'/api/<cont_name:{cont_name_re}>/<cid:{cid_re}>/<list_name:analyses>/<_id:{cid_re}>'),
-                                                                                      listhandler.AnalysesHandler, name='analysis',
-                                                                                      methods=['GET', 'DELETE']),
-    webapp2.Route(_format(r'/api/<cont_name:{cont_name_re}>/<cid:{cid_re}>/<list_name:analyses>/<_id:{cid_re}>/files'),
-                                                                                      listhandler.AnalysesHandler, handler_method='download',
-                                                                                      methods=['GET'], name='analysis_files'),
-    webapp2.Route(_format(r'/api/<cont_name:{cont_name_re}>/<cid:{cid_re}>/<list_name:analyses>/<_id:{cid_re}>/files/<name:{filename_re}>'),
-                                                                                      listhandler.AnalysesHandler, handler_method='download',
-                                                                                      methods=['GET'], name='analysis_single_file'),
-    webapp2.Route(_format(r'/api/<cont_name:{cont_name_re}>/<cid:{cid_re}>/<list_name:analyses>/<_id:{cid_re}>/notes'),
-                                                                                      listhandler.AnalysesHandler, handler_method='add_note',
-                                                                                      methods=['POST'], name='analysis_add_note'),
-    webapp2.Route(_format(r'/api/<cont_name:{cont_name_re}>/<cid:{cid_re}>/<list_name:analyses>/<_id:{cid_re}>/notes/<note_id:{cid_re}>'),
-                                                                                      listhandler.AnalysesHandler, handler_method='delete_note',
-                                                                                      methods=['DELETE'], name='analysis_delete_note'),
 ]
 
 
@@ -240,7 +296,7 @@ def app_factory(*_, **__):
     # pylint: disable=protected-access,unused-argument
 
     # don't use config.get_item() as we don't want to require the database at startup
-    application = webapp2.WSGIApplication(routes, debug=config.__config['core']['debug'])
+    application = webapp2.WSGIApplication(endpoints, debug=config.__config['core']['debug'])
     application.router.set_dispatcher(dispatcher)
     application.request_class = SciTranRequest
     if os.environ.get("SCITRAN_RUNTIME_COVERAGE") == "true":
diff --git a/api/handlers/confighandler.py b/api/handlers/confighandler.py
new file mode 100644
index 0000000000000000000000000000000000000000..ef4312fe25c29b88b21e8fa402306d9f533dc98f
--- /dev/null
+++ b/api/handlers/confighandler.py
@@ -0,0 +1,25 @@
+import json
+
+from .. import encoder
+from .. import base
+from .. import config
+
+class Config(base.RequestHandler):
+
+    def get(self):
+        """Return public Scitran configuration information."""
+        return config.get_public_config()
+
+    def get_js(self):
+        """Return scitran config in javascript format."""
+        self.response.write(
+            'config = ' +
+            json.dumps( self.get(), sort_keys=True, indent=4, separators=(',', ': '), default=encoder.custom_json_serializer,) +
+            ';'
+        )
+
+class Version(base.RequestHandler):
+
+    def get(self):
+        """Return database schema version"""
+        return config.get_version()