diff --git a/epochs.py b/epochs.py
index 18d46f690229a0246e51423a236131f957087161..945e06fc794b973a92ec24f83e17a16e7dc01aaa 100644
--- a/epochs.py
+++ b/epochs.py
@@ -9,17 +9,17 @@ import nimsapiutil
 
 class Epochs(nimsapiutil.NIMSRequestHandler):
 
-    def count(self):
+    def count(self, iid):
         """Return the number of Epochs."""
         self.response.write('epochs count\n')
 
-    def post(self):
-        """Create a new Epoch"""
+    def post(self, iid):
+        """Create a new Epoch."""
         self.response.write('epochs post\n')
 
-    def get(self, sess_id):
+    def get(self, iid, sid):
         """Return the list of Session Epochs."""
-        session = self.app.db.sessions.find_one({'_id': bson.objectid.ObjectId(sess_id)})
+        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'])})
@@ -27,21 +27,21 @@ class Epochs(nimsapiutil.NIMSRequestHandler):
             self.abort(500)
         if not self.user_is_superuser and self.userid not in experiment['permissions']:
             self.abort(403)
-        query = {'session': bson.objectid.ObjectId(sess_id)}
+        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):
+    def put(self, iid):
         """Update many Epochs."""
         self.response.write('epochs put\n')
 
 
 class Epoch(nimsapiutil.NIMSRequestHandler):
 
-    def get(self, epoch_id):
+    def get(self, iid, eid):
         """Return one Epoch, conditionally with details."""
-        epoch = self.app.db.epochs.find_one({'_id': bson.objectid.ObjectId(epoch_id)})
+        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']})
@@ -54,10 +54,10 @@ class Epoch(nimsapiutil.NIMSRequestHandler):
             self.abort(403)
         self.response.write(json.dumps(epoch, default=bson.json_util.default))
 
-    def put(self, _id):
+    def put(self, iid, eid):
         """Update an existing Epoch."""
-        self.response.write('epoch %s put, %s\n' % (_id, self.request.params))
+        self.response.write('epoch %s put, %s\n' % (epoch_id, self.request.params))
 
-    def delete(self, _id):
+    def delete(self, iid, eid):
         """Delete an Epoch."""
-        self.response.write('epoch %s delete, %s\n' % (_id, self.request.params))
+        self.response.write('epoch %s delete, %s\n' % (epoch_id, self.request.params))
diff --git a/experiments.py b/experiments.py
index 7a5d85d23f96747d253de64429ab504e43decd76..8c0acfd767577866b7a4fd4a06bf678738a198dd 100644
--- a/experiments.py
+++ b/experiments.py
@@ -9,15 +9,15 @@ import nimsapiutil
 
 class Experiments(nimsapiutil.NIMSRequestHandler):
 
-    def count(self):
+    def count(self, iid):
         """Return the number of Experiments."""
         self.response.write('%d experiments\n' % self.app.db.experiments.count())
 
-    def post(self):
-        """Create a new Experiment"""
+    def post(self, iid):
+        """Create a new Experiment."""
         self.response.write('experiments post\n')
 
-    def get(self):
+    def get(self, iid):
         """Return the list of Experiments."""
         query = {'permissions.' + self.userid: {'$exists': 'true'}} if not self.user_is_superuser else None
         projection = ['group', 'name', 'permissions.'+self.userid]
@@ -31,20 +31,20 @@ class Experiments(nimsapiutil.NIMSRequestHandler):
             exp['timestamp'] = timestamps[exp['_id']]
         self.response.write(json.dumps(experiments, default=bson.json_util.default))
 
-    def put(self):
+    def put(self, iid):
         """Update many Experiments."""
         self.response.write('experiments put\n')
 
 
 class Experiment(nimsapiutil.NIMSRequestHandler):
 
-    def get(self, exp_id):
+    def get(self, iid, xid):
         """Return one Experiment, conditionally with details."""
-        experiment = self.app.db.experiments.find_one({'_id': bson.objectid.ObjectId(exp_id)})
+        experiment = self.app.db.experiments.find_one({'_id': bson.objectid.ObjectId(xid)})
         if not experiment:
             self.abort(404)
         experiment['timestamp'] = self.app.db.sessions.aggregate([
-                {'$match': {'experiment': bson.objectid.ObjectId(exp_id)}},
+                {'$match': {'experiment': bson.objectid.ObjectId(xid)}},
                 {'$group': {'_id': '$experiment', 'timestamp': {'$max': '$timestamp'}}},
                 ])['result'][0]['timestamp']
         if not self.user_is_superuser:
@@ -54,10 +54,10 @@ class Experiment(nimsapiutil.NIMSRequestHandler):
                 experiment['permissions'] = {self.userid: experiment['permissions'][self.userid]}
         self.response.write(json.dumps(experiment, default=bson.json_util.default))
 
-    def put(self, exp_id):
+    def put(self, iid, xid):
         """Update an existing Experiment."""
         self.response.write('experiment %s put, %s\n' % (exp_id, self.request.params))
 
-    def delete(self, exp_id):
+    def delete(self, iid, xid):
         """Delete an Experiment."""
         self.response.write('experiment %s delete, %s\n' % (exp_id, self.request.params))
diff --git a/nimsapi.py b/nimsapi.py
index 460dfb3ff9b5d1b0d626750dffecf3eb6f005d19..717f5f081a8cdc637c66de78b83dfdfebf0a9139 100755
--- a/nimsapi.py
+++ b/nimsapi.py
@@ -10,9 +10,11 @@ import logging
 import pymongo
 import tarfile
 import webapp2
+import requests
 import zipfile
 import argparse
 import bson.json_util
+import webapp2_extras.routes
 
 import nimsutil
 
@@ -26,8 +28,14 @@ log = logging.getLogger('nimsapi')
 
 class NIMSAPI(nimsapiutil.NIMSRequestHandler):
 
+    def head(self):
+        """Return 200 OK."""
+        self.response.set_status(200)
+
     def get(self):
-        self.response.write('nimsapi\n')
+        """Return API documentation"""
+        self.response.headers['Content-Type'] = 'text/html; charset=utf-8'
+        self.response.write('nimsapi - {0}\n'.format(self.app.config['site_id']))
 
     def upload(self):
         # TODO add security: either authenticated user or machine-to-machine CRAM
@@ -47,7 +55,7 @@ class NIMSAPI(nimsapiutil.NIMSRequestHandler):
                 self.abort(400, 'Content-MD5 mismatch.')
             if not tarfile.is_tarfile(upload_filepath) and not zipfile.is_zipfile(upload_filepath):
                 self.abort(415)
-            os.rename(upload_filepath, os.path.join(stage_path, str(uuid.uuid1()) + '_' + filename)) # add UUID to prevent clobbering files
+            os.rename(upload_filepath, os.path.join(stage_path, str(uuid.uuid1()) + '_' + fid)) # add UUID to prevent clobbering files
 
     def download(self):
         paths = []
@@ -64,33 +72,33 @@ class NIMSAPI(nimsapiutil.NIMSRequestHandler):
 
 class Users(nimsapiutil.NIMSRequestHandler):
 
-    def count(self):
+    def count(self, iid):
         """Return the number of Users."""
         self.response.write('%d users\n' % self.app.db.users.count())
 
-    def post(self):
+    def post(self, iid):
         """Create a new User"""
         self.response.write('users post\n')
 
-    def get(self):
+    def get(self, iid):
         """Return the list of Users."""
         projection = ['firstname', 'lastname', 'email_hash']
         users = list(self.app.db.users.find({}, projection))
         self.response.write(json.dumps(users, default=bson.json_util.default))
 
-    def put(self):
+    def put(self, iid):
         """Update many Users."""
         self.response.write('users put\n')
 
 
 class User(nimsapiutil.NIMSRequestHandler):
 
-    def get(self, uid):
+    def get(self, iid, uid):
         """Return User details."""
         user = self.app.db.users.find_one({'_id': uid})
         self.response.write(json.dumps(user, default=bson.json_util.default))
 
-    def put(self, uid):
+    def put(self, iid, uid):
         """Update an existing User."""
         user = self.app.db.users.find_one({'_id': uid})
         if not user:
@@ -112,80 +120,127 @@ class User(nimsapiutil.NIMSRequestHandler):
             self.abort(403)
         self.response.write(json.dumps(user, default=bson.json_util.default) + '\n')
 
-    def delete(self, uid):
+    def delete(self, iid, uid):
         """Delete an User."""
         self.response.write('user %s delete, %s\n' % (uid, self.request.params))
 
 
 class Groups(nimsapiutil.NIMSRequestHandler):
 
-    def count(self):
+    def count(self, iid):
         """Return the number of Groups."""
         self.response.write('%d groups\n' % self.app.db.groups.count())
 
-    def post(self):
+    def post(self, iid):
         """Create a new Group"""
         self.response.write('groups post\n')
 
-    def get(self):
+    def get(self, iid):
         """Return the list of Groups."""
         projection = ['_id']
         groups = list(self.app.db.groups.find({}, projection))
         self.response.write(json.dumps(groups, default=bson.json_util.default))
 
-    def put(self):
+    def put(self, iid):
         """Update many Groups."""
         self.response.write('groups put\n')
 
 
 class Group(nimsapiutil.NIMSRequestHandler):
 
-    def get(self, gid):
+    def get(self, iid, gid):
         """Return Group details."""
         group = self.app.db.groups.find_one({'_id': gid})
         self.response.write(json.dumps(group, default=bson.json_util.default))
 
-    def put(self, gid):
+    def put(self, iid, gid):
         """Update an existing Group."""
         self.response.write('group %s put, %s\n' % (gid, self.request.params))
 
-    def delete(self, gid):
+    def delete(self, iid, gid):
         """Delete an Group."""
 
 
+class Remotes(nimsapiutil.NIMSRequestHandler):
+
+    def get(self):
+        """Return Remote NIMS sites"""
+        logging.info(self.user)
+        # TODO: remotes by default will show all registered remote site
+        if self.request.get('all'):
+            projection = ['_id', 'hostname', 'ip4']
+            sites = list(self.app.db.remotes.find({}, projection))
+            self.response.write(json.dumps(sites, default=bson.json_util.default))
+        # if 'all' not specificed, then user MUST be speficied
+        else:
+            """Return the list of remotes where user has membership"""
+            logging.info(self.user['_id'])
+            # projection = ['_id', 'hostname', 'ip4']
+            projection = ['_id', 'hostname', 'ip4', 'users']
+
+            remotes = list(self.app.db.remotes.find({'users': {'$in': [self.user['_id']]}}, projection))
+            self.response.write(json.dumps(remotes, default=bson.json_util.default))
+
+            # TODO: IMPORTANT; refine how remotes are returned from pymongo queries
+
+            # in the list of remotes; in which sites, are there experiments, which the person has access to?
+            # return ONLY remote site names.
+
+            # if not superuser, does person have permissions to what is being queried
+            # query = {'permissions.' + self.userid: {'$in': 'true'}} if not self.user_is_superuser else None
+            # projection = ['_id', 'users', 'hostname', 'ip4']
+            # remotes = list(self.app.db.remotes.find(query, projection))
+
+            # session_aggregates = 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}
+            # for exp in experiments:
+            #     exp['timestamp'] = timestamps[exp['_id']]
+            # self.response.write(json.dumps(experiments, default=bson.json_util.default))
+
+
 class ArgumentParser(argparse.ArgumentParser):
 
     def __init__(self):
         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('--pubkey', default='internims/NIMSpubkey.pub', help='path to ssl pubkey')
+        self.add_argument('-u', '--uid', default='local', 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')
         self.add_argument('-q', '--quiet', action='store_true', default=False, help='disable console logging')
 
-
 routes = [
-        webapp2.Route(r'/nimsapi',                                          NIMSAPI),
-        webapp2.Route(r'/nimsapi/upload',                                   NIMSAPI, handler_method='upload', methods=['PUT']),
-        webapp2.Route(r'/nimsapi/download',                                 NIMSAPI, handler_method='download', methods=['GET']),
-        webapp2.Route(r'/nimsapi/dump',                                     NIMSAPI, handler_method='dump', methods=['GET']),
-        webapp2.Route(r'/nimsapi/users',                                    Users),
-        webapp2.Route(r'/nimsapi/users/count',                              Users, handler_method='count', methods=['GET']),
-        webapp2.Route(r'/nimsapi/users/<:.+>',                              User),
-        webapp2.Route(r'/nimsapi/groups',                                   Groups),
-        webapp2.Route(r'/nimsapi/groups/count',                             Groups, handler_method='count', methods=['GET']),
-        webapp2.Route(r'/nimsapi/groups/<:.+>',                             Group),
-        webapp2.Route(r'/nimsapi/experiments',                              experiments.Experiments),
-        webapp2.Route(r'/nimsapi/experiments/count',                        experiments.Experiments, handler_method='count', methods=['GET']),
-        webapp2.Route(r'/nimsapi/experiments/<:[0-9a-f]+>',                 experiments.Experiment),
-        webapp2.Route(r'/nimsapi/experiments/<:[0-9a-f]+>/sessions',        sessions.Sessions),
-        webapp2.Route(r'/nimsapi/sessions/count',                           sessions.Sessions, handler_method='count', methods=['GET']),
-        webapp2.Route(r'/nimsapi/sessions/<:[0-9a-f]+>',                    sessions.Session),
-        webapp2.Route(r'/nimsapi/sessions/<:[0-9a-f]+>/move',               sessions.Session, handler_method='move'),
-        webapp2.Route(r'/nimsapi/sessions/<:[0-9a-f]+>/epochs',             epochs.Epochs),
-        webapp2.Route(r'/nimsapi/epochs/count',                             epochs.Epochs, handler_method='count', methods=['GET']),
-        webapp2.Route(r'/nimsapi/epochs/<:[0-9a-f]+>',                      epochs.Epoch),
-        ]
+    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/<fid>',                             NIMSAPI, handler_method='upload', methods=['PUT']),
+        webapp2.Route(r'/remotes',                                  Remotes),
+        ]),
+    # webapp2_extras.routes.PathPrefixRoute has bug, variable MUST have regex
+    webapp2_extras.routes.PathPrefixRoute(r'/nimsapi/<iid:[^/]+>', [
+        webapp2.Route(r'/users',                                    Users),
+        webapp2.Route(r'/users/count',                              Users, handler_method='count', methods=['GET']),
+        webapp2.Route(r'/users/<uid>',                              User),
+        webapp2.Route(r'/groups',                                   Groups),
+        webapp2.Route(r'/groups/count',                             Groups, handler_method='count', methods=['GET']),
+        webapp2.Route(r'/groups/<gid>',                             Group),
+        webapp2.Route(r'/experiments',                              experiments.Experiments),
+        webapp2.Route(r'/experiments/count',                        experiments.Experiments, handler_method='count', 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/<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/<eid:[0-9a-f]{24}>',                epochs.Epoch),
+    ]),
+]
 
 
 if __name__ == '__main__':
@@ -193,37 +248,6 @@ if __name__ == '__main__':
     nimsutil.configure_log(args.logfile, not args.quiet, args.loglevel)
 
     from paste import httpserver
-    app = webapp2.WSGIApplication(routes, debug=True, config=dict(stage_path=args.stage_path))
+    app = webapp2.WSGIApplication(routes, debug=True, config=dict(stage_path=args.stage_path, site_id=args.uid, pubkey=args.pubkey))
     app.db = (pymongo.MongoReplicaSetClient(args.uri) if 'replicaSet' in args.uri else pymongo.MongoClient(args.uri)).get_default_database()
     httpserver.serve(app, host=httpserver.socket.gethostname(), port='8080')
-
-
-#API = NIMSAPI
-#APIResource = experiments.Experiments
-#routes = [
-#        webapp2.Route(r'/', API),
-#        webapp2.Route(r'/resource', APIResource),
-#        ]
-#
-#from webapp2_extras.routes import PathPrefixRoute
-#
-#if __name__ == '__main__':
-#    from paste import httpserver
-#    nimsapi = webapp2.WSGIApplication([PathPrefixRoute('/', routes)], debug=True)
-#    httpserver.serve(nimsapi, host='127.0.0.1', port='8080')
-#else:
-#    from webapp2_extras.routes import PathPrefixRoute
-#    nimsapi = webapp2.WSGIApplication([PathPrefixRoute('/nimsapi', routes)], debug=True)
-
-
-# /experiments                  experiment info for all experiments
-# /experiments/ID/sessions      experiment info with embedded sessions for one experiment
-# /experiments/ID/epochs        experiment info with embedded sessions and embedded epochs for one experiment
-# /sessions/ID/epochs           experiment info with embedded sessions and embedded epochs for one session
-
-# /sessions                     experiment info with embedded sessions for all experiments
-# /epochs                       experiment info with embedded sessions and embedded epochs for all sessions
-
-# /experiments/ID               experiment details for one experiment
-# /sessions/ID                  sessions details for one session
-# /epochs/ID                    epoch details for one epoch
diff --git a/nimsapiutil.py b/nimsapiutil.py
index c4acbcafc0b725f6c21bcefb392ebd9f76de8adc..08d817be4766edbd25a068f25b2cbdcc8b362ace 100644
--- a/nimsapiutil.py
+++ b/nimsapiutil.py
@@ -1,14 +1,105 @@
 # @author:  Gunnar Schaefer
+# @author:  Kevin S. Hahn
 
+import base64
+import datetime
+import json
+import logging
+import requests
 import webapp2
+import socket       # socket.gethostname()
+from Crypto.Hash import HMAC
+from Crypto.Random import random
+logging.basicConfig(level=logging.INFO)
 
 
 class NIMSRequestHandler(webapp2.RequestHandler):
 
+    """fetches pubkey from own self.db.remotes. needs to be aware of OWN site uid"""
+
     def __init__(self, request=None, response=None):
         webapp2.RequestHandler.__init__(self, request, response)
+        # call initialize, isntead of __init__
         self.request.remote_user = self.request.get('user', None) # FIXME: auth system should set REMOTE_USER
         self.userid = self.request.remote_user or '@public'
         self.user = self.app.db.users.find_one({'_id': self.userid})
         self.user_is_superuser = self.user.get('superuser')
         self.response.headers['Content-Type'] = 'application/json'
+        try:
+            self.target_id = request.route_kwargs['iid']            # for what site is the request meant
+        except KeyError:
+            self.target_id = 'local'                                # change to site_id?
+        self.useragent = self.request.headers['user-agent']         # browser or internimsP2P request
+        self.site_id = self.app.config['site_id']                   # what is THIS site
+        self.pubkey = open(self.app.config['pubkey']).read()
+
+        # requests coming from another NIMS instance are dealt with differently
+        if self.useragent.startswith('NIMS Instance'):
+            logging.info("request from '{0}', interNIMS p2p initiated".format(self.useragent))
+            try:
+                authinfo = self.request.headers['authorization']
+                challenge_id, digest = base64.b64decode(authinfo).split()
+                user, remote_site = challenge_id.split(':')
+                # logging.info('{0} {1} {2}'.format(user, remote_site, digest))
+                projection = {'_id': False, 'pubkey': True}
+                remote_pubkey = self.app.db.remotes.find_one({'_id': remote_site}, projection)['pubkey']
+                # get the challenge from db.challenges
+                projection = {'_id': False, 'challenge': True}
+                challenge = self.app.db.challenges.find_one({'_id': challenge_id}, projection)['challenge']
+                # purge challenge (challenges are single use)
+                self.app.db.challenges.remove({'_id': challenge_id})
+                # verify
+                h = HMAC.new(remote_pubkey, challenge)
+                self.expected = base64.b64encode('%s %s' % (challenge_id, h.hexdigest()))
+                if self.expected == authinfo:
+                    logging.info('CRAM successful')
+                else:
+                    self.abort(403, 'Not Authorized: cram failed')
+            except KeyError, e:
+                # send a 401 with a fresh challenge
+                cid = self.request.get('cid')
+                if not cid: self.abort(403, 'challenge_id not in payload')
+                challenge = {'_id': cid,
+                             'challenge': str(random.getrandbits(128)),
+                             'timestamp': datetime.datetime.now()}
+                # upsert challenge with time of creation
+                spam = self.app.db.challenges.find_and_modify(query={'_id': cid}, update=challenge, upsert=True, new=True)
+                # send 401 + challenge in 'www-authenticate' header
+                self.response.headers['www-authenticate'] = base64.b64encode(challenge['challenge'])
+                self.response.set_status(401)
+
+    def dispatch(self):
+        """dispatching and request forwarding"""
+        if self.target_id in ['local', self.site_id]:
+            logging.info('{0} delegating to local {1}'.format(socket.gethostname(), self.request.url))
+            super(NIMSRequestHandler, self).dispatch()
+        else:
+            logging.info('{0} delegating to remote {1}'.format(socket.gethostname(), self.target_id))
+            # is target registered?
+            target = self.app.db.remotes.find_one({'_id': self.target_id}, {'_id':False, 'hostname':True})
+            if not target:
+                logging.info('remote host {0} not in auth list. DENIED'.format(self.target_id))
+                self.abort(403, 'forbidden: site is not registered with interNIMS')
+            self.cid = self.userid + ':' + self.site_id
+            reqheaders = dict(self.request.headers)
+
+            # adjust the request, pass as much of orig request as possible
+            reqheaders['User-Agent'] = 'NIMS Instance {0}'.format(self.site_id)
+            del reqheaders['Host']
+            target_api = 'http://{0}{1}?{2}'.format(target['hostname'], self.request.path, self.request.query_string)
+            reqparams = {'cid': self.cid}
+
+            # first attempt, expect 401, send as little as possible...
+            r = requests.request(method=self.request.method, url=target_api, params=reqparams, headers=reqheaders, cookies=self.request.cookies)
+
+            if r.status_code == 401:
+                challenge = base64.b64decode(r.headers['www-authenticate'])
+                # logging.info('challenge {0} recieved'.format(challenge))
+                h = HMAC.new(self.pubkey, challenge)
+                response = base64.b64encode('%s %s' % (self.cid, h.hexdigest()))
+                # logging.info('sending: {0} {1}'.format(self.cid, h.hexdigest()))
+                #adjust the request and try again
+                reqheaders['authorization'] = response
+                r = requests.request(method=self.request.method, url=target_api, params=reqparams, data=self.request.body, headers=reqheaders, cookies=self.request.cookies)
+
+            self.response.write(r.content)
diff --git a/sessions.py b/sessions.py
index d79f6bb454cf18d37e24728ffee0fd8b0c9afd87..3be07e3efc38f461c872931c080ca6b34a05a7d8 100644
--- a/sessions.py
+++ b/sessions.py
@@ -9,36 +9,36 @@ import nimsapiutil
 
 class Sessions(nimsapiutil.NIMSRequestHandler):
 
-    def count(self):
+    def count(self, iid):
         """Return the number of Sessions."""
         self.response.write('sessions count\n')
 
-    def post(self):
+    def post(self, iid):
         """Create a new Session"""
         self.response.write('sessions post\n')
 
-    def get(self, exp_id):
+    def get(self, iid, xid):
         """Return the list of Experiment Sessions."""
-        experiment = self.app.db.experiments.find_one({'_id': bson.objectid.ObjectId(exp_id)})
+        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(exp_id)}
+        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):
+    def put(self, iid):
         """Update many Sessions."""
         self.response.write('sessions put\n')
 
 
 class Session(nimsapiutil.NIMSRequestHandler):
 
-    def get(self, sess_id):
+    def get(self, iid, sid):
         """Return one Session, conditionally with details."""
-        session = self.app.db.sessions.find_one({'_id': bson.objectid.ObjectId(sess_id)})
+        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'])})
@@ -48,19 +48,19 @@ class Session(nimsapiutil.NIMSRequestHandler):
             self.abort(403)
         self.response.write(json.dumps(session, default=bson.json_util.default))
 
-    def put(self, _id):
+    def put(self, iid, sid):
         """Update an existing Session."""
-        self.response.write('session %s put, %s\n' % (_id, self.request.params))
+        self.response.write('session %s put, %s\n' % (sid, self.request.params))
 
-    def delete(self, _id):
+    def delete(self, iid, sid):
         """Delete an Session."""
-        self.response.write('session %s delete, %s\n' % (_id, self.request.params))
+        self.response.write('session %s delete, %s\n' % (sid, self.request.params))
 
-    def move(self, _id):
+    def move(self, iid, sid):
         """
         Move a Session to another Experiment.
 
         Usage:
             /nimsapi/sessions/123/move?dest=456
         """
-        self.response.write('session %s move, %s\n' % (_id, self.request.params))
+        self.response.write('session %s move, %s\n' % (sid, self.request.params))