diff --git a/collections_.py b/collections_.py
new file mode 100644
index 0000000000000000000000000000000000000000..8bfb6c9eb105212892a33681faaa1af1c702768e
--- /dev/null
+++ b/collections_.py
@@ -0,0 +1,273 @@
+# @author:  Gunnar Schaefer
+
+import re
+import json
+import webapp2
+import bson.json_util
+
+import nimsapiutil
+
+# curator (later: multiple curators and authorizers)
+# name
+# permissions
+# epochs point to collections
+
+# /collections
+# /collections/<cid>/sessions
+# /collections/<cid>/epochs?session=<sid>
+# /collections/<cid>/sessions/<sid>/epochs
+
+class Collections(nimsapiutil.NIMSRequestHandler):
+
+    """/collections """
+
+    json_schema = {
+        '$schema': 'http://json-schema.org/draft-04/schema#',
+        'title': 'Collection List',
+        'type': 'array',
+        'items': {
+            'title': 'Collection',
+            'type': 'object',
+            'properties': {
+                '_id': {
+                    'title': 'Database ID',
+                },
+                'site': {
+                    'title': 'Site',
+                    'type': 'string',
+                },
+                'group': {
+                    'title': 'Group',
+                    'type': 'string',
+                },
+                'name': {
+                    'title': 'Name',
+                    'type': 'string',
+                },
+                'timestamp': {
+                    'title': 'Timestamp',
+                },
+                'permissions': {
+                    'title': 'Permissions',
+                    'type': 'object',
+                },
+            }
+        }
+    }
+
+    def count(self):
+        """Return the number of Collections."""
+        self.response.write(json.dumps(self.app.db.collections.count()))
+
+    def post(self):
+        """Create a new Collection."""
+        self.response.write('collections post\n')
+
+    def get(self):
+        """Return the list of Collections."""
+        query = {'permissions.' + self.userid: {'$exists': 'true'}} if not self.user_is_superuser else None
+        projection = ['curator', 'name', 'permissions.'+self.userid]
+        collections = list(self.app.db.collections.find(query, projection))
+        #session_aggregates = self.app.db.sessions.aggregate([
+        #        {'$match': {'collection': {'$in': [col['_id'] for col in collections]}}},
+        #        {'$group': {'_id': '$collection', 'timestamp': {'$max': '$timestamp'}}},
+        #        ])['result']
+        #timestamps = {sa['_id']: sa['timestamp'] for sa in session_aggregates}
+        #for col in collections:
+        #    col['timestamp'] = timestamps[col['_id']]
+        self.response.write(json.dumps(collections, default=bson.json_util.default))
+
+    def put(self):
+        """Update many Collections."""
+        self.response.write('collections put\n')
+
+
+class Collection(nimsapiutil.NIMSRequestHandler):
+
+    """/collections/<cid> """
+
+    json_schema = {
+        '$schema': 'http://json-schema.org/draft-04/schema#',
+        'title': 'Collection',
+        'type': 'object',
+        'properties': {
+            '_id': {
+                'title': 'Database ID',
+            },
+            'site': {
+                'title': 'Site',
+                'type': 'string',
+            },
+            'group': {
+                'title': 'Group',
+                'type': 'string',
+            },
+            'name': {
+                'title': 'Name',
+                'type': 'string',
+                'maxLength': 32,
+            },
+            'timestamp': {
+                'title': 'Timestamp',
+            },
+            'permissions': {
+                'title': 'Permissions',
+                'type': 'object',
+                'minProperties': 1,
+            },
+            'files': {
+                'title': 'Files',
+                'type': 'array',
+                'items': nimsapiutil.NIMSRequestHandler.file_schema,
+                'uniqueItems': True,
+            },
+        },
+        'required': ['_id', 'group', 'name'], #FIXME
+    }
+
+    def get(self, cid):
+        """Return one Collection, conditionally with details."""
+        collection = self.app.db.collections.find_one({'_id': bson.objectid.ObjectId(cid)})
+        if not collection:
+            self.abort(404)
+        collection['timestamp'] = self.app.db.sessions.aggregate([
+                {'$match': {'collection': bson.objectid.ObjectId(cid)}},
+                {'$group': {'_id': '$collection', 'timestamp': {'$max': '$timestamp'}}},
+                ])['result'][0]['timestamp']
+        if not self.user_is_superuser:
+            if self.userid not in collection['permissions']:
+                self.abort(403)
+            if collection['permissions'][self.userid] != 'admin' and collection['permissions'][self.userid] != 'pi':
+                collection['permissions'] = {self.userid: collection['permissions'][self.userid]}
+        self.response.write(json.dumps(collection, default=bson.json_util.default))
+
+    def put(self, cid):
+        """Update an existing Collection."""
+        self.response.write('collection %s put, %s\n' % (exp_id, self.request.params))
+
+    def delete(self, cid):
+        """Delete an Collection."""
+        self.response.write('collection %s delete, %s\n' % (exp_id, self.request.params))
+
+
+class Sessions(nimsapiutil.NIMSRequestHandler):
+
+    """/collections/<cid>/sessions """
+
+    json_schema = {
+        '$schema': 'http://json-schema.org/draft-04/schema#',
+        'title': 'Session List',
+        'type': 'array',
+        'items': {
+            'title': 'Session',
+            'type': 'object',
+            'properties': {
+                '_id': {
+                    'title': 'Database ID',
+                },
+                'name': {
+                    'title': 'Session',
+                    'type': 'string',
+                },
+                'subject': {
+                    'title': 'Subject',
+                    'type': 'string',
+                },
+                'site': {
+                    'title': 'Site',
+                    'type': 'string',
+                },
+            }
+        }
+    }
+
+    def count(self):
+        """Return the number of Sessions."""
+        self.response.write(json.dumps(self.app.db.sessions.count()))
+
+    def post(self):
+        """Create a new Session"""
+        self.response.write('sessions post\n')
+
+    def get(self, cid):
+        """Return the list of Session Epochs."""
+        collection = self.app.db.collections.find_one({'_id': bson.objectid.ObjectId(cid)})
+        if not collection:
+            self.abort(404)
+        if not self.user_is_superuser and self.userid not in collection['permissions']:
+            self.abort(403)
+        aggregated_epochs = self.app.db.epochs.aggregate([
+                {'$match': {'collections': bson.objectid.ObjectId(cid)}},
+                {'$group': {'_id': '$session'}},
+                ])['result']
+        query = {'_id': {'$in': [agg_epoch['_id'] for agg_epoch in aggregated_epochs]}}
+        projection = ['name', 'subject']
+        sessions = list(self.app.db.sessions.find(query, projection))
+        for sess in sessions:
+            sess['site'] = self.app.config['site_id']
+        self.response.write(json.dumps(sessions, default=bson.json_util.default))
+
+    def put(self):
+        """Update many Sessions."""
+        self.response.write('sessions put\n')
+
+
+class Epochs(nimsapiutil.NIMSRequestHandler):
+
+    """/collections/<cid>/epochs """
+
+    json_schema = {
+        '$schema': 'http://json-schema.org/draft-04/schema#',
+        'title': 'Epoch List',
+        'type': 'array',
+        'items': {
+            'title': 'Epoch',
+            'type': 'object',
+            'properties': {
+                '_id': {
+                    'title': 'Database ID',
+                },
+                'name': {
+                    'title': 'Epoch',
+                    'type': 'string',
+                },
+                'description': {
+                    'title': 'Description',
+                    'type': 'string',
+                },
+                'datatype': {
+                    'title': 'Datatype',
+                    'type': 'string',
+                },
+            }
+        }
+    }
+
+    def count(self):
+        """Return the number of Epochs."""
+        self.response.write(json.dumps(self.app.db.epochs.count()))
+
+    def post(self):
+        """Create a new Epoch."""
+        self.response.write('epochs post\n')
+
+    def get(self, cid):
+        """Return the list of Session Epochs."""
+        collection = self.app.db.collections.find_one({'_id': bson.objectid.ObjectId(cid)})
+        if not collection:
+            self.abort(404)
+        if not self.user_is_superuser and self.userid not in collection['permissions']:
+            self.abort(403)
+        query = {'collections': bson.objectid.ObjectId(cid)}
+        sid = self.request.get('session')
+        if re.match(r'^[0-9a-f]{24}$', sid):
+            query['session'] = bson.objectid.ObjectId(sid)
+        elif sid != '':
+            self.abort(400)
+        projection = ['name', 'description', 'datatype']
+        epochs = list(self.app.db.epochs.find(query, projection))
+        self.response.write(json.dumps(epochs, default=bson.json_util.default))
+
+    def put(self):
+        """Update many Epochs."""
+        self.response.write('epochs put\n')
diff --git a/epochs.py b/epochs.py
deleted file mode 100644
index fb62300dda39543e851497b755c6690b14ade99b..0000000000000000000000000000000000000000
--- a/epochs.py
+++ /dev/null
@@ -1,133 +0,0 @@
-# @author:  Gunnar Schaefer
-
-import json
-import webapp2
-import bson.json_util
-
-import nimsdata
-import nimsapiutil
-
-
-class Epochs(nimsapiutil.NIMSRequestHandler):
-
-    """/nimsapi/epochs """
-
-    json_schema = {
-        '$schema': 'http://json-schema.org/draft-04/schema#',
-        'title': 'Epoch List',
-        'type': 'array',
-        'items': {
-            'title': 'Epoch',
-            'type': 'object',
-            'properties': {
-                '_id': {
-                    'title': 'Database ID',
-                },
-                'timestamp': {
-                    'title': 'Timestamp',
-                },
-                'datatype': {
-                    'title': 'Datatype',
-                    'type': 'string',
-                },
-                'series': {
-                    'title': 'Series',
-                    'type': 'integer',
-                },
-                'acquisition': {
-                    'title': 'Acquisition',
-                    'type': 'integer',
-                },
-                'description': {
-                    'title': 'Description',
-                    'type': 'string',
-                },
-            }
-        }
-    }
-
-    def count(self):
-        """Return the number of Epochs."""
-        self.response.write(json.dumps(self.app.db.epochs.count()))
-
-    def post(self):
-        """Create a new Epoch."""
-        self.response.write('epochs post\n')
-
-    def get(self, sid):
-        """Return the list of Session Epochs."""
-        session = self.app.db.sessions.find_one({'_id': bson.objectid.ObjectId(sid)})
-        if not session:
-            self.abort(404)
-        experiment = self.app.db.experiments.find_one({'_id': bson.objectid.ObjectId(session['experiment'])})
-        if not experiment:
-            self.abort(500)
-        if not self.user_is_superuser and self.userid not in experiment['permissions']:
-            self.abort(403)
-        query = {'session': bson.objectid.ObjectId(sid)}
-        projection = ['timestamp', 'series', 'acquisition', 'description', 'datatype']
-        epochs = list(self.app.db.epochs.find(query, projection))
-        self.response.write(json.dumps(epochs, default=bson.json_util.default))
-
-    def put(self):
-        """Update many Epochs."""
-        self.response.write('epochs put\n')
-
-
-class Epoch(nimsapiutil.NIMSRequestHandler):
-
-    """/nimsapi/epochs/<eid> """
-
-    json_schema = {
-        '$schema': 'http://json-schema.org/draft-04/schema#',
-        'title': 'Epoch',
-        'type': 'object',
-        'properties': {
-            '_id': {
-                'title': 'Database ID',
-            },
-            'uid': {
-                'title': 'UID',
-                'type': 'string',
-            },
-            'session': {
-                'title': 'Session ID',
-            },
-            'files': {
-                'title': 'Files',
-                'type': 'array',
-                'items': nimsapiutil.NIMSRequestHandler.file_schema,
-                'uniqueItems': True,
-            },
-        },
-        'required': ['_id'],
-    }
-
-    def schema(self, *args, **kwargs):
-        import copy
-        json_schema = copy.deepcopy(self.json_schema)
-        json_schema['properties'].update(nimsdata.nimsdicom.NIMSDicom.epoch_properties)
-        self.response.write(json.dumps(json_schema, default=bson.json_util.default))
-
-    def get(self, eid):
-        """Return one Epoch, conditionally with details."""
-        epoch = self.app.db.epochs.find_one({'_id': bson.objectid.ObjectId(eid)})
-        if not epoch:
-            self.abort(404)
-        session = self.app.db.sessions.find_one({'_id': epoch['session']})
-        if not session:
-            self.abort(500)
-        experiment = self.app.db.experiments.find_one({'_id': bson.objectid.ObjectId(session['experiment'])})
-        if not experiment:
-            self.abort(500)
-        if not self.user_is_superuser and self.userid not in experiment['permissions']:
-            self.abort(403)
-        self.response.write(json.dumps(epoch, default=bson.json_util.default))
-
-    def put(self, eid):
-        """Update an existing Epoch."""
-        self.response.write('epoch %s put, %s\n' % (epoch_id, self.request.params))
-
-    def delete(self, eid):
-        """Delete an Epoch."""
-        self.response.write('epoch %s delete, %s\n' % (epoch_id, self.request.params))
diff --git a/experiments.py b/experiments.py
index cb3a7fd1fca1f9e6fc05aad7325e3e55969ba116..1a88fb1d5a18f67e24c007eac825bb0963568804 100644
--- a/experiments.py
+++ b/experiments.py
@@ -4,6 +4,7 @@ import json
 import webapp2
 import bson.json_util
 
+#import nimsdata
 import nimsapiutil
 
 
@@ -22,8 +23,9 @@ class Experiments(nimsapiutil.NIMSRequestHandler):
                 '_id': {
                     'title': 'Database ID',
                 },
-                'timestamp': {
-                    'title': 'Timestamp',
+                'site': {
+                    'title': 'Site',
+                    'type': 'string',
                 },
                 'group': {
                     'title': 'Group',
@@ -33,6 +35,9 @@ class Experiments(nimsapiutil.NIMSRequestHandler):
                     'title': 'Name',
                     'type': 'string',
                 },
+                'timestamp': {
+                    'title': 'Timestamp',
+                },
                 'permissions': {
                     'title': 'Permissions',
                     'type': 'object',
@@ -54,13 +59,14 @@ class Experiments(nimsapiutil.NIMSRequestHandler):
         query = {'permissions.' + self.userid: {'$exists': 'true'}} if not self.user_is_superuser else None
         projection = ['group', 'name', 'permissions.'+self.userid]
         experiments = list(self.app.db.experiments.find(query, projection))
-        session_aggregates = self.app.db.sessions.aggregate([
+        aggregated_sessions = self.app.db.sessions.aggregate([
                 {'$match': {'experiment': {'$in': [exp['_id'] for exp in experiments]}}},
                 {'$group': {'_id': '$experiment', 'timestamp': {'$max': '$timestamp'}}},
                 ])['result']
-        timestamps = {sa['_id']: sa['timestamp'] for sa in session_aggregates}
+        timestamps = {agg_sess['_id']: agg_sess['timestamp'] for agg_sess in aggregated_sessions}
         for exp in experiments:
             exp['timestamp'] = timestamps[exp['_id']]
+            exp['site'] = self.app.config['site_id']
         self.response.write(json.dumps(experiments, default=bson.json_util.default))
 
     def put(self):
@@ -80,8 +86,9 @@ class Experiment(nimsapiutil.NIMSRequestHandler):
             '_id': {
                 'title': 'Database ID',
             },
-            'timestamp': {
-                'title': 'Timestamp',
+            'site': {
+                'title': 'Site',
+                'type': 'string',
             },
             'group': {
                 'title': 'Group',
@@ -92,6 +99,9 @@ class Experiment(nimsapiutil.NIMSRequestHandler):
                 'type': 'string',
                 'maxLength': 32,
             },
+            'timestamp': {
+                'title': 'Timestamp',
+            },
             'permissions': {
                 'title': 'Permissions',
                 'type': 'object',
@@ -104,7 +114,7 @@ class Experiment(nimsapiutil.NIMSRequestHandler):
                 'uniqueItems': True,
             },
         },
-        'required': ['_id', 'group', 'name'],
+        'required': ['_id', 'group', 'name'], #FIXME
     }
 
     def get(self, xid):
@@ -130,3 +140,246 @@ class Experiment(nimsapiutil.NIMSRequestHandler):
     def delete(self, xid):
         """Delete an Experiment."""
         self.response.write('experiment %s delete, %s\n' % (exp_id, self.request.params))
+
+
+class Sessions(nimsapiutil.NIMSRequestHandler):
+
+    """/sessions """
+
+    json_schema = {
+        '$schema': 'http://json-schema.org/draft-04/schema#',
+        'title': 'Session List',
+        'type': 'array',
+        'items': {
+            'title': 'Session',
+            'type': 'object',
+            'properties': {
+                '_id': {
+                    'title': 'Database ID',
+                },
+                'name': {
+                    'title': 'Session',
+                    'type': 'string',
+                },
+                'subject': {
+                    'title': 'Subject',
+                    'type': 'string',
+                },
+                'site': {
+                    'title': 'Site',
+                    'type': 'string',
+                },
+            }
+        }
+    }
+
+    def count(self):
+        """Return the number of Sessions."""
+        self.response.write(json.dumps(self.app.db.sessions.count()))
+
+    def post(self):
+        """Create a new Session"""
+        self.response.write('sessions post\n')
+
+    def get(self, xid):
+        """Return the list of Experiment Sessions."""
+        experiment = self.app.db.experiments.find_one({'_id': bson.objectid.ObjectId(xid)})
+        if not experiment:
+            self.abort(404)
+        if not self.user_is_superuser and self.userid not in experiment['permissions']:
+            self.abort(403)
+        query = {'experiment': bson.objectid.ObjectId(xid)}
+        projection = ['name', 'subject']
+        sessions = list(self.app.db.sessions.find(query, projection))
+        self.response.write(json.dumps(sessions, default=bson.json_util.default))
+
+    def put(self):
+        """Update many Sessions."""
+        self.response.write('sessions put\n')
+
+
+class Session(nimsapiutil.NIMSRequestHandler):
+
+    """/sessions/<sid> """
+
+    json_schema = {
+        '$schema': 'http://json-schema.org/draft-04/schema#',
+        'title': 'Session',
+        'type': 'object',
+        'properties': {
+            '_id': {
+                'title': 'Database ID',
+            },
+            'uid': {
+                'title': 'UID',
+                'type': 'string',
+            },
+            'experiment': {
+                'title': 'Experiment ID',
+            },
+            'site': {
+                'title': 'Site',
+                'type': 'string',
+            },
+            'files': {
+                'title': 'Files',
+                'type': 'array',
+                'items': nimsapiutil.NIMSRequestHandler.file_schema,
+                'uniqueItems': True,
+            },
+        },
+        'required': ['_id', 'experiment', 'uid', 'patient_id', 'subject'], #FIXME
+    }
+
+    def schema(self, *args, **kwargs):
+        import copy
+        json_schema = copy.deepcopy(self.json_schema)
+        json_schema['properties'].update(nimsdata.NIMSData.session_properties)
+        self.response.write(json.dumps(json_schema, default=bson.json_util.default))
+
+    def get(self, sid):
+        """Return one Session, conditionally with details."""
+        session = self.app.db.sessions.find_one({'_id': bson.objectid.ObjectId(sid)})
+        if not session:
+            self.abort(404)
+        experiment = self.app.db.experiments.find_one({'_id': bson.objectid.ObjectId(session['experiment'])})
+        if not experiment:
+            self.abort(500)
+        if not self.user_is_superuser and self.userid not in experiment['permissions']:
+            self.abort(403)
+        self.response.write(json.dumps(session, default=bson.json_util.default))
+
+    def put(self, sid):
+        """Update an existing Session."""
+        self.response.write('session %s put, %s\n' % (sid, self.request.params))
+
+    def delete(self, sid):
+        """Delete an Session."""
+        self.response.write('session %s delete, %s\n' % (sid, self.request.params))
+
+    def move(self, sid):
+        """
+        Move a Session to another Experiment.
+
+        Usage:
+            /nimsapi/sessions/123/move?dest=456
+        """
+        self.response.write('session %s move, %s\n' % (sid, self.request.params))
+
+
+class Epochs(nimsapiutil.NIMSRequestHandler):
+
+    """/nimsapi/epochs """
+
+    json_schema = {
+        '$schema': 'http://json-schema.org/draft-04/schema#',
+        'title': 'Epoch List',
+        'type': 'array',
+        'items': {
+            'title': 'Epoch',
+            'type': 'object',
+            'properties': {
+                '_id': {
+                    'title': 'Database ID',
+                },
+                'name': {
+                    'title': 'Epoch',
+                    'type': 'string',
+                },
+                'description': {
+                    'title': 'Description',
+                    'type': 'string',
+                },
+                'datatype': {
+                    'title': 'Datatype',
+                    'type': 'string',
+                },
+            }
+        }
+    }
+
+    def count(self):
+        """Return the number of Epochs."""
+        self.response.write(json.dumps(self.app.db.epochs.count()))
+
+    def post(self):
+        """Create a new Epoch."""
+        self.response.write('epochs post\n')
+
+    def get(self, sid):
+        """Return the list of Session Epochs."""
+        session = self.app.db.sessions.find_one({'_id': bson.objectid.ObjectId(sid)})
+        if not session:
+            self.abort(404)
+        experiment = self.app.db.experiments.find_one({'_id': bson.objectid.ObjectId(session['experiment'])})
+        if not experiment:
+            self.abort(500)
+        if not self.user_is_superuser and self.userid not in experiment['permissions']:
+            self.abort(403)
+        query = {'session': bson.objectid.ObjectId(sid)}
+        projection = ['name', 'description', 'datatype']
+        epochs = list(self.app.db.epochs.find(query, projection))
+        self.response.write(json.dumps(epochs, default=bson.json_util.default))
+
+    def put(self):
+        """Update many Epochs."""
+        self.response.write('epochs put\n')
+
+
+class Epoch(nimsapiutil.NIMSRequestHandler):
+
+    """/nimsapi/epochs/<eid> """
+
+    json_schema = {
+        '$schema': 'http://json-schema.org/draft-04/schema#',
+        'title': 'Epoch',
+        'type': 'object',
+        'properties': {
+            '_id': {
+                'title': 'Database ID',
+            },
+            'uid': {
+                'title': 'UID',
+                'type': 'string',
+            },
+            'session': {
+                'title': 'Session ID',
+            },
+            'files': {
+                'title': 'Files',
+                'type': 'array',
+                'items': nimsapiutil.NIMSRequestHandler.file_schema,
+                'uniqueItems': True,
+            },
+        },
+        'required': ['_id'], #FIXME
+    }
+
+    def schema(self, *args, **kwargs):
+        import copy
+        json_schema = copy.deepcopy(self.json_schema)
+        json_schema['properties'].update(nimsdata.nimsdicom.NIMSDicom.epoch_properties)
+        self.response.write(json.dumps(json_schema, default=bson.json_util.default))
+
+    def get(self, eid):
+        """Return one Epoch, conditionally with details."""
+        epoch = self.app.db.epochs.find_one({'_id': bson.objectid.ObjectId(eid)})
+        if not epoch:
+            self.abort(404)
+        session = self.app.db.sessions.find_one({'_id': epoch['session']})
+        if not session:
+            self.abort(500)
+        experiment = self.app.db.experiments.find_one({'_id': bson.objectid.ObjectId(session['experiment'])})
+        if not experiment:
+            self.abort(500)
+        if not self.user_is_superuser and self.userid not in experiment['permissions']:
+            self.abort(403)
+        self.response.write(json.dumps(epoch, default=bson.json_util.default))
+
+    def put(self, eid):
+        """Update an existing Epoch."""
+        self.response.write('epoch %s put, %s\n' % (epoch_id, self.request.params))
+
+    def delete(self, eid):
+        """Delete an Epoch."""
+        self.response.write('epoch %s delete, %s\n' % (epoch_id, self.request.params))
diff --git a/nimsapi.py b/nimsapi.py
index e205035404d21b03dc58313cd7e3ad2c26d4be39..8d9dfbeb1d2bf8edcac3762021d9f5f761aeff95 100755
--- a/nimsapi.py
+++ b/nimsapi.py
@@ -20,10 +20,9 @@ import Crypto.PublicKey.RSA
 
 import nimsutil
 
-import epochs
-import sessions
 import experiments
 import nimsapiutil
+import collections_
 
 log = logging.getLogger('nimsapi')
 
@@ -39,38 +38,45 @@ class NIMSAPI(nimsapiutil.NIMSRequestHandler):
     def get(self):
         """Return API documentation"""
         resources = """
-                      Resource                                          | Description
-                      :-------------------------------------------------|:-----------------------
-                      /nimsapi/download                                 | download
-                      /nimsapi/dump                                     | dump
-                      /nimsapi/upload                                   | upload
-                      /nimsapi/remotes                                  | list of remote instances
-                      [(/nimsapi/users)]                                | list of users
-                      [(/nimsapi/users/count)]                          | count of users
-                      [(/nimsapi/users/listschema)]                     | schema for user list
-                      [(/nimsapi/users/schema)]                         | schema for single user
-                      /nimsapi/users/*<uid>*                            | details for one user, *<uid>*
-                      [(/nimsapi/groups)]                               | list of groups
-                      [(/nimsapi/groups/count)]                         | count of groups
-                      [(/nimsapi/groups/listschema)]                    | schema for group list
-                      [(/nimsapi/groups/schema)]                        | schema for single group
-                      /nimsapi/groups/*<gid>*                           | details for one group, *<gid>*
-                      [(/nimsapi/experiments)]                          | list of experiments
-                      [(/nimsapi/experiments/count)]                    | count of experiments
-                      [(/nimsapi/experiments/listschema)]               | schema for experiment list
-                      [(/nimsapi/experiments/schema)]                   | schema for single experiment
-                      /nimsapi/experiments/*<xid>*                      | details for one experiment, *<xid>*
-                      /nimsapi/experiments/*<xid>*/sessions             | list sessions for one experiment, *<xid>*
-                      [(/nimsapi/sessions/count)]                       | count of sessions
-                      [(/nimsapi/sessions/listschema)]                  | schema for sessions list
-                      [(/nimsapi/sessions/schema)]                      | schema for single session
-                      /nimsapi/sessions/*<sid>*                         | details for one session, *<sid>*
-                      /nimsapi/sessions/*<sid>*/move                    | move one session, *<sid>*, to a different experiment
-                      /nimsapi/sessions/*<sid>*/epochs                  | list epochs for one session, *<sid>*
-                      [(/nimsapi/epochs/count)]                         | count of epochs
-                      [(/nimsapi/epochs/listschema)]                    | schema for epoch list
-                      [(/nimsapi/epochs/schema)]                        | schema for single epoch
-                      /nimsapi/epochs/*<eid>*                           | details for one epoch, *<eid>*"""
+            Resource                                            | Description
+            :---------------------------------------------------|:-----------------------
+            nimsapi/download                                    | download
+            nimsapi/upload                                      | upload
+            nimsapi/remotes                                     | list of remote instances
+            [(nimsapi/users)]                                   | list of users
+            [(nimsapi/users/count)]                             | count of users
+            [(nimsapi/users/listschema)]                        | schema for user list
+            [(nimsapi/users/schema)]                            | schema for single user
+            nimsapi/users/*<uid>*                               | details for user *<uid>*
+            [(nimsapi/groups)]                                  | list of groups
+            [(nimsapi/groups/count)]                            | count of groups
+            [(nimsapi/groups/listschema)]                       | schema for group list
+            [(nimsapi/groups/schema)]                           | schema for single group
+            nimsapi/groups/*<gid>*                              | details for group *<gid>*
+            [(nimsapi/experiments)]                             | list of experiments
+            [(nimsapi/experiments/count)]                       | count of experiments
+            [(nimsapi/experiments/listschema)]                  | schema for experiment list
+            [(nimsapi/experiments/schema)]                      | schema for single experiment
+            nimsapi/experiments/*<xid>*                         | details for experiment *<xid>*
+            nimsapi/experiments/*<xid>*/sessions                | list sessions for experiment *<xid>*
+            [(nimsapi/sessions/count)]                          | count of sessions
+            [(nimsapi/sessions/listschema)]                     | schema for sessions list
+            [(nimsapi/sessions/schema)]                         | schema for single session
+            nimsapi/sessions/*<sid>*                            | details for session *<sid>*
+            nimsapi/sessions/*<sid>*/move                       | move session *<sid>* to a different experiment
+            nimsapi/sessions/*<sid>*/epochs                     | list epochs for session *<sid>*
+            [(nimsapi/epochs/count)]                            | count of epochs
+            [(nimsapi/epochs/listschema)]                       | schema for epoch list
+            [(nimsapi/epochs/schema)]                           | schema for single epoch
+            nimsapi/epochs/*<eid>*                              | details for epoch *<eid>*
+            [(nimsapi/collections)]                             | list of collections
+            [(nimsapi/collections/count)]                       | count of collections
+            [(nimsapi/collections/listschema)]                  | schema for collections list
+            [(nimsapi/collections/schema)]                      | schema for single collection
+            nimsapi/collections/*<cid>*                         | details for collection *<cid>*
+            nimsapi/collections/*<cid>*/sessions                | list sessions for collection *<cid>*
+            nimsapi/collections/*<cid>*/epochs?session=*<sid>*  | list of epochs for collection *<cid>*, optionally restricted to session *<sid>*
+            """
         resources = re.sub(r'\[\((.*)\)\]', r'[\1](\1)', resources).replace('<', '&lt;').replace('>', '&gt;').strip()
         self.response.headers['Content-Type'] = 'text/html; charset=utf-8'
         self.response.write('<html>\n')
@@ -123,9 +129,6 @@ class NIMSAPI(nimsapiutil.NIMSRequestHandler):
             paths += _idpaths
             symlinks += _idsymlinks
 
-    def dump(self):
-        self.response.write(json.dumps(list(self.app.db.sessions.find()), default=bson.json_util.default))
-
 
 class Users(nimsapiutil.NIMSRequestHandler):
 
@@ -392,7 +395,7 @@ class ArgumentParser(argparse.ArgumentParser):
         super(ArgumentParser, self).__init__()
         self.add_argument('uri', help='NIMS DB URI')
         self.add_argument('stage_path', help='path to staging area')
-        self.add_argument('--privkey', help='path to private SSL key file')
+        self.add_argument('-k', '--privkey', help='path to private SSL key file')
         self.add_argument('-u', '--uid', help='site UID')
         self.add_argument('-f', '--logfile', help='path to log file')
         self.add_argument('-l', '--loglevel', default='info', help='path to log file')
@@ -402,7 +405,6 @@ routes = [
     webapp2.Route(r'/nimsapi',                                      NIMSAPI),
     webapp2_extras.routes.PathPrefixRoute(r'/nimsapi', [
         webapp2.Route(r'/download',                                 NIMSAPI, handler_method='download', methods=['GET']),
-        webapp2.Route(r'/dump',                                     NIMSAPI, handler_method='dump', methods=['GET']),
         webapp2.Route(r'/upload',                                   NIMSAPI, handler_method='upload', methods=['PUT']),
         webapp2.Route(r'/remotes',                                  Remotes),
         webapp2.Route(r'/users',                                    Users),
@@ -420,17 +422,24 @@ routes = [
         webapp2.Route(r'/experiments/listschema',                   experiments.Experiments, handler_method='schema', methods=['GET']),
         webapp2.Route(r'/experiments/schema',                       experiments.Experiment, handler_method='schema', methods=['GET']),
         webapp2.Route(r'/experiments/<xid:[0-9a-f]{24}>',           experiments.Experiment),
-        webapp2.Route(r'/experiments/<xid:[0-9a-f]{24}>/sessions',  sessions.Sessions),
-        webapp2.Route(r'/sessions/count',                           sessions.Sessions, handler_method='count', methods=['GET']),
-        webapp2.Route(r'/sessions/listschema',                      sessions.Sessions, handler_method='schema', methods=['GET']),
-        webapp2.Route(r'/sessions/schema',                          sessions.Session, handler_method='schema', methods=['GET']),
-        webapp2.Route(r'/sessions/<sid:[0-9a-f]{24}>',              sessions.Session),
-        webapp2.Route(r'/sessions/<sid:[0-9a-f]{24}>/move',         sessions.Session, handler_method='move'),
-        webapp2.Route(r'/sessions/<sid:[0-9a-f]{24}>/epochs',       epochs.Epochs),
-        webapp2.Route(r'/epochs/count',                             epochs.Epochs, handler_method='count', methods=['GET']),
-        webapp2.Route(r'/epochs/listschema',                        epochs.Epochs, handler_method='schema', methods=['GET']),
-        webapp2.Route(r'/epochs/schema',                            epochs.Epoch, handler_method='schema', methods=['GET']),
-        webapp2.Route(r'/epochs/<eid:[0-9a-f]{24}>',                epochs.Epoch),
+        webapp2.Route(r'/experiments/<xid:[0-9a-f]{24}>/sessions',  experiments.Sessions),
+        webapp2.Route(r'/sessions/count',                           experiments.Sessions, handler_method='count', methods=['GET']),
+        webapp2.Route(r'/sessions/listschema',                      experiments.Sessions, handler_method='schema', methods=['GET']),
+        webapp2.Route(r'/sessions/schema',                          experiments.Session, handler_method='schema', methods=['GET']),
+        webapp2.Route(r'/sessions/<sid:[0-9a-f]{24}>',              experiments.Session),
+        webapp2.Route(r'/sessions/<sid:[0-9a-f]{24}>/move',         experiments.Session, handler_method='move'),
+        webapp2.Route(r'/sessions/<sid:[0-9a-f]{24}>/epochs',       experiments.Epochs),
+        webapp2.Route(r'/epochs/count',                             experiments.Epochs, handler_method='count', methods=['GET']),
+        webapp2.Route(r'/epochs/listschema',                        experiments.Epochs, handler_method='schema', methods=['GET']),
+        webapp2.Route(r'/epochs/schema',                            experiments.Epoch, handler_method='schema', methods=['GET']),
+        webapp2.Route(r'/epochs/<eid:[0-9a-f]{24}>',                experiments.Epoch),
+        webapp2.Route(r'/collections',                              collections_.Collections),
+        webapp2.Route(r'/collections/count',                        collections_.Collections, handler_method='count', methods=['GET']),
+        webapp2.Route(r'/collections/listschema',                   collections_.Collections, handler_method='schema', methods=['GET']),
+        webapp2.Route(r'/collections/schema',                       collections_.Collection, handler_method='schema', methods=['GET']),
+        webapp2.Route(r'/collections/<cid:[0-9a-f]{24}>',           collections_.Collection),
+        webapp2.Route(r'/collections/<cid:[0-9a-f]{24}>/sessions',  collections_.Sessions),
+        webapp2.Route(r'/collections/<cid:[0-9a-f]{24}>/epochs',    collections_.Epochs),
     ]),
 ]
 
diff --git a/nimsapiutil.py b/nimsapiutil.py
index a07d5a549dce91dd3bb95021e0b515b7af63e141..17025f02db1209c02d224dacab00042b50e465a4 100644
--- a/nimsapiutil.py
+++ b/nimsapiutil.py
@@ -27,17 +27,9 @@ class NIMSRequestHandler(webapp2.RequestHandler):
         'title': 'File',
         'type': 'object',
         'properties': {
-            'datakind': {
-                'title': 'Data Kind',
-                'type': 'string',
-            },
-            'datatype': {
-                'title': 'Data Type',
-                'type': 'string',
-            },
-            'filetype': {
-                'title': 'File Type',
-                'type': 'string',
+            'type': {
+                'title': 'Type',
+                'type': 'array',
             },
             'filename': {
                 'title': 'File Name',
diff --git a/sessions.py b/sessions.py
deleted file mode 100644
index a86cc17a98477758c370301be0353aca11767c7d..0000000000000000000000000000000000000000
--- a/sessions.py
+++ /dev/null
@@ -1,132 +0,0 @@
-# @author:  Gunnar Schaefer
-
-import json
-import webapp2
-import bson.json_util
-
-import nimsdata
-import nimsapiutil
-
-
-class Sessions(nimsapiutil.NIMSRequestHandler):
-
-    """/sessions """
-
-    json_schema = {
-        '$schema': 'http://json-schema.org/draft-04/schema#',
-        'title': 'Session List',
-        'type': 'array',
-        'items': {
-            'title': 'Session',
-            'type': 'object',
-            'properties': {
-                '_id': {
-                    'title': 'Database ID',
-                },
-                'timestamp': {
-                    'title': 'Timestamp',
-                },
-                'subject': {
-                    'title': 'Subject Code',
-                    'type': 'string',
-                },
-            }
-        }
-    }
-
-    def count(self):
-        """Return the number of Sessions."""
-        self.response.write(json.dumps(self.app.db.sessions.count()))
-
-    def post(self):
-        """Create a new Session"""
-        self.response.write('sessions post\n')
-
-    def get(self, xid):
-        """Return the list of Experiment Sessions."""
-        experiment = self.app.db.experiments.find_one({'_id': bson.objectid.ObjectId(xid)})
-        if not experiment:
-            self.abort(404)
-        if not self.user_is_superuser and self.userid not in experiment['permissions']:
-            self.abort(403)
-        query = {'experiment': bson.objectid.ObjectId(xid)}
-        projection = ['timestamp', 'subject']
-        sessions = list(self.app.db.sessions.find(query, projection))
-        self.response.write(json.dumps(sessions, default=bson.json_util.default))
-
-    def put(self):
-        """Update many Sessions."""
-        self.response.write('sessions put\n')
-
-
-class Session(nimsapiutil.NIMSRequestHandler):
-
-    """/sessions/<sid> """
-
-    json_schema = {
-        '$schema': 'http://json-schema.org/draft-04/schema#',
-        'title': 'Session',
-        'type': 'object',
-        'properties': {
-            '_id': {
-                'title': 'Database ID',
-            },
-            'uid': {
-                'title': 'UID',
-                'type': 'string',
-            },
-            'experiment': {
-                'title': 'Experiment ID',
-            },
-            'timestamp': {
-                'title': 'Timestamp',
-            },
-            'subject': {
-                'title': 'Subject Code',
-                'type': 'string',
-                'maxLength': 16,
-            },
-            'files': {
-                'title': 'Files',
-                'type': 'array',
-                'items': nimsapiutil.NIMSRequestHandler.file_schema,
-                'uniqueItems': True,
-            },
-        },
-        'required': ['_id', 'experiment', 'uid', 'patient_id', 'subject'],
-    }
-
-    def schema(self, *args, **kwargs):
-        import copy
-        json_schema = copy.deepcopy(self.json_schema)
-        json_schema['properties'].update(nimsdata.NIMSData.session_properties)
-        self.response.write(json.dumps(json_schema, default=bson.json_util.default))
-
-    def get(self, sid):
-        """Return one Session, conditionally with details."""
-        session = self.app.db.sessions.find_one({'_id': bson.objectid.ObjectId(sid)})
-        if not session:
-            self.abort(404)
-        experiment = self.app.db.experiments.find_one({'_id': bson.objectid.ObjectId(session['experiment'])})
-        if not experiment:
-            self.abort(500)
-        if not self.user_is_superuser and self.userid not in experiment['permissions']:
-            self.abort(403)
-        self.response.write(json.dumps(session, default=bson.json_util.default))
-
-    def put(self, sid):
-        """Update an existing Session."""
-        self.response.write('session %s put, %s\n' % (sid, self.request.params))
-
-    def delete(self, sid):
-        """Delete an Session."""
-        self.response.write('session %s delete, %s\n' % (sid, self.request.params))
-
-    def move(self, sid):
-        """
-        Move a Session to another Experiment.
-
-        Usage:
-            /nimsapi/sessions/123/move?dest=456
-        """
-        self.response.write('session %s move, %s\n' % (sid, self.request.params))