diff --git a/astakosclient/astakosclient/__init__.py b/astakosclient/astakosclient/__init__.py
index 8e5f32f72d07b72d804e025b38fe25ca2c074e5f..6e34793d23b5e3666ec0997980d215c0f43a9aba 100644
--- a/astakosclient/astakosclient/__init__.py
+++ b/astakosclient/astakosclient/__init__.py
@@ -31,6 +31,10 @@
 # interpreted as representing official policies, either expressed
 # or implied, of GRNET S.A.
 
+"""
+Simple and minimal client for the Astakos authentication service
+"""
+
 import logging
 import urlparse
 import urllib
@@ -39,45 +43,10 @@ from copy import copy
 
 import simplejson
 from astakosclient.utils import \
-    retry, scheme_to_class, parse_request, check_input
+    retry_dec, scheme_to_class, parse_request, check_input, join_urls
 from astakosclient.errors import \
     AstakosClientException, Unauthorized, BadRequest, NotFound, Forbidden, \
-    NoUserName, NoUUID, BadValue, QuotaLimit, InvalidResponse
-from .keypath import get_path
-from .services import astakos_services
-
-
-# Customize astakos_services here?
-
-
-def join_urls(a, b):
-    """join_urls from synnefo.lib"""
-    return a.rstrip("/") + "/" + b.lstrip("/")
-
-# --------------------------------------------------------------------
-# Astakos API urls
-UI_PREFIX = get_path(astakos_services, 'astakos_ui.prefix')
-ACCOUNTS_PREFIX = get_path(astakos_services, 'astakos_account.prefix')
-ACCOUNTS_PREFIX = join_urls(ACCOUNTS_PREFIX, 'v1.0')
-API_AUTHENTICATE = join_urls(ACCOUNTS_PREFIX, "authenticate")
-API_USERCATALOGS = join_urls(ACCOUNTS_PREFIX, "user_catalogs")
-API_SERVICE_USERCATALOGS = join_urls(ACCOUNTS_PREFIX, "service/user_catalogs")
-API_GETSERVICES = join_urls(UI_PREFIX, "get_services")
-API_RESOURCES = join_urls(ACCOUNTS_PREFIX, "resources")
-API_QUOTAS = join_urls(ACCOUNTS_PREFIX, "quotas")
-API_SERVICE_QUOTAS = join_urls(ACCOUNTS_PREFIX, "service_quotas")
-API_COMMISSIONS = join_urls(ACCOUNTS_PREFIX, "commissions")
-API_COMMISSIONS_ACTION = join_urls(API_COMMISSIONS, "action")
-API_FEEDBACK = join_urls(ACCOUNTS_PREFIX, "feedback")
-API_PROJECTS = join_urls(ACCOUNTS_PREFIX, "projects")
-API_APPLICATIONS = join_urls(API_PROJECTS, "apps")
-API_MEMBERSHIPS = join_urls(API_PROJECTS, "memberships")
-
-# --------------------------------------------------------------------
-# Astakos Keystone API urls
-IDENTITY_PREFIX = get_path(astakos_services, 'astakos_identity.prefix')
-IDENTITY_PREFIX = join_urls(IDENTITY_PREFIX, "v2.0")
-API_TOKENS = join_urls(IDENTITY_PREFIX, "tokens")
+    NoUserName, NoUUID, BadValue, QuotaLimit, InvalidResponse, NoEndpoints
 
 
 # --------------------------------------------------------------------
@@ -94,80 +63,149 @@ def get_token_from_cookie(request, cookie_name):
     try:
         cookie_content = urllib.unquote(request.COOKIE.get(cookie_name, None))
         return cookie_content.split("|")[1]
-    except:
+    except BaseException:
         return None
 
 
-class AstakosClient():
+# Too many instance attributes. pylint: disable-msg=R0902
+# Too many public methods. pylint: disable-msg=R0904
+class AstakosClient(object):
     """AstakosClient Class Implementation"""
 
     # ----------------------------------
-    def __init__(self, astakos_url, retry=0,
-                 use_pool=False, pool_size=8, logger=None):
+    # Initialize AstakosClient Class
+    # Too many arguments. pylint: disable-msg=R0913
+    # Too many local variables. pylint: disable-msg=R0914
+    # Too many statements. pylint: disable-msg=R0915
+    def __init__(self, token, auth_url,
+                 retry=0, use_pool=False, pool_size=8, logger=None):
         """Initialize AstakosClient Class
 
         Keyword arguments:
-        astakos_url -- i.e https://accounts.example.com (string)
-        use_pool    -- use objpool for http requests (boolean)
+        token       -- user's/service's token (string)
+        auth_url    -- i.e https://accounts.example.com/identity/v2.0
         retry       -- how many time to retry (integer)
+        use_pool    -- use objpool for http requests (boolean)
+        pool_size   -- if using pool, define the pool size
         logger      -- pass a different logger
 
         """
+
+        # Get logger
         if logger is None:
             logging.basicConfig(
                 format='%(asctime)s [%(levelname)s] %(name)s %(message)s',
                 datefmt='%Y-%m-%d %H:%M:%S',
                 level=logging.INFO)
             logger = logging.getLogger("astakosclient")
-        logger.debug("Intialize AstakosClient: astakos_url = %s, "
-                     "use_pool = %s" % (astakos_url, use_pool))
+        logger.debug("Intialize AstakosClient: auth_url = %s, "
+                     "use_pool = %s, pool_size = %s",
+                     auth_url, use_pool, pool_size)
 
-        check_input("__init__", logger, astakos_url=astakos_url)
+        # Check that token and auth_url (mandatory options) are given
+        check_input("__init__", logger, token=token, auth_url=auth_url)
 
-        # Check for supported scheme
-        p = urlparse.urlparse(astakos_url)
-        conn_class = scheme_to_class(p.scheme, use_pool, pool_size)
+        # Initialize connection class
+        parsed_auth_url = urlparse.urlparse(auth_url)
+        conn_class = \
+            scheme_to_class(parsed_auth_url.scheme, use_pool, pool_size)
         if conn_class is None:
-            m = "Unsupported scheme: %s" % p.scheme
-            logger.error(m)
-            raise BadValue(m)
+            msg = "Unsupported scheme: %s" % parsed_auth_url.scheme
+            logger.error(msg)
+            raise BadValue(msg)
 
-        # Save astakos_url etc. in our class
+        # Save astakos base url, logger, connection class etc in our class
         self.retry = retry
         self.logger = logger
-        self.netloc = p.netloc
-        self.scheme = p.scheme
-        self.path = p.path.rstrip('/')
+        self.token = token
+        self.astakos_base_url = parsed_auth_url.netloc
+        self.scheme = parsed_auth_url.scheme
         self.conn_class = conn_class
 
+        # Initialize astakos api prefixes
+        # API urls under auth_url
+        self.auth_prefix = parsed_auth_url.path
+        self.api_tokens = join_urls(self.auth_prefix, "tokens")
+
+        # ------------------------------
+        # API urls under account_url
+        # Get account_url from get_endpoints
+        # get_endpoints needs self.api_tokens
+        endpoints = self.get_endpoints(non_authentication=True)
+        account_service_catalog = parse_endpoints(
+            endpoints, ep_name="astakos_account", ep_version_id="v1.0")
+        self.account_url = \
+            account_service_catalog[0]['endpoints'][0]['publicURL']
+        parsed_account_url = urlparse.urlparse(self.account_url)
+
+        self.account_prefix = parsed_account_url.path
+        self.logger.debug("Got account_prefix \"%s\"" % self.account_prefix)
+
+        self.api_authenticate = join_urls(
+            self.account_prefix, "authenticate")
+        self.api_usercatalogs = join_urls(
+            self.account_prefix, "user_catalogs")
+        self.api_service_usercatalogs = join_urls(
+            self.account_prefix, "service/user_catalogs")
+        self.api_resources = join_urls(
+            self.account_prefix, "resources")
+        self.api_quotas = join_urls(
+            self.account_prefix, "quotas")
+        self.api_service_quotas = join_urls(
+            self.account_prefix, "service_quotas")
+        self.api_commissions = join_urls(
+            self.account_prefix, "commissions")
+        self.api_commissions_action = join_urls(
+            self.api_commissions, "action")
+        self.api_feedback = join_urls(
+            self.account_prefix, "feedback")
+        self.api_projects = join_urls(
+            self.account_prefix, "projects")
+        self.api_applications = join_urls(
+            self.api_projects, "apps")
+        self.api_memberships = join_urls(
+            self.api_projects, "memberships")
+
+        # ------------------------------
+        # API urls under ui_url
+        # Get ui url from get_endpoints
+        # get_endpoints needs self.api_tokens
+        ui_service_catalog = parse_endpoints(
+            endpoints, ep_name="astakos_account", ep_version_id="v1.0")
+        parsed_ui_url = urlparse.urlparse(
+            ui_service_catalog[0]['endpoints'][0]['SNF:uiURL'])
+        self.ui_url = \
+            ui_service_catalog[0]['endpoints'][0]['SNF:uiURL']
+        parsed_ui_url = urlparse.urlparse(self.ui_url)
+
+        self.ui_prefix = parsed_ui_url.path
+        self.logger.debug("Got ui_prefix \"%s\"" % self.ui_prefix)
+
+        self.api_getservices = join_urls(self.ui_prefix, "get_services")
+
     # ----------------------------------
-    @retry
-    def _call_astakos(self, token, request_path, headers=None,
+    @retry_dec
+    def _call_astakos(self, request_path, headers=None,
                       body=None, method="GET", log_body=True):
         """Make the actual call to Astakos Service"""
-        if token is not None:
-            hashed_token = hashlib.sha1()
-            hashed_token.update(token)
-            using_token = "using token %s" % (hashed_token.hexdigest())
-        else:
-            using_token = "without using token"
+        hashed_token = hashlib.sha1()
+        hashed_token.update(self.token)
         self.logger.debug(
-            "Make a %s request to %s %s with headers %s and body %s"
-            % (method, request_path, using_token, headers,
-               body if log_body else "(not logged)"))
+            "Make a %s request to %s, using token with hash %s, "
+            "with headers %s and body %s",
+            method, request_path, hashed_token.hexdigest(), headers,
+            body if log_body else "(not logged)")
 
         # Check Input
         if headers is None:
             headers = {}
         if body is None:
             body = {}
-        path = self.path + "/" + request_path.strip('/')
 
         # Build request's header and body
         kwargs = {}
         kwargs['headers'] = copy(headers)
-        if token is not None:
-            kwargs['headers']['X-Auth-Token'] = token
+        kwargs['headers']['X-Auth-Token'] = self.token
         if body:
             kwargs['body'] = copy(body)
             kwargs['headers'].setdefault(
@@ -177,10 +215,11 @@ class AstakosClient():
 
         try:
             # Get the connection object
-            with self.conn_class(self.netloc) as conn:
+            with self.conn_class(self.astakos_base_url) as conn:
                 # Send request
+                # Used * or ** magic. pylint: disable-msg=W0142
                 (message, data, status) = \
-                    _do_request(conn, method, path, **kwargs)
+                    _do_request(conn, method, request_path, **kwargs)
         except Exception as err:
             self.logger.error("Failed to send request: %s" % repr(err))
             raise AstakosClientException(str(err))
@@ -210,11 +249,10 @@ class AstakosClient():
 
     # ------------------------
     # do a GET to ``API_AUTHENTICATE``
-    def get_user_info(self, token, usage=False):
+    def get_user_info(self, usage=False):
         """Authenticate user and get user's info as a dictionary
 
         Keyword arguments:
-        token   -- user's token (string)
         usage   -- return usage information for user (boolean)
 
         In case of success return user information (json parsed format).
@@ -222,59 +260,57 @@ class AstakosClient():
 
         """
         # Send request
-        auth_path = copy(API_AUTHENTICATE)
+        auth_path = self.api_authenticate
         if usage:
             auth_path += "?usage=1"
-        return self._call_astakos(token, auth_path)
+        return self._call_astakos(auth_path)
 
     # ----------------------------------
     # do a POST to ``API_USERCATALOGS`` (or ``API_SERVICE_USERCATALOGS``)
     #   with {'uuids': uuids}
-    def _uuid_catalog(self, token, uuids, req_path):
+    def _uuid_catalog(self, uuids, req_path):
+        """Helper function to retrieve uuid catalog"""
         req_headers = {'content-type': 'application/json'}
         req_body = parse_request({'uuids': uuids}, self.logger)
-        data = self._call_astakos(
-            token, req_path, req_headers, req_body, "POST")
+        data = self._call_astakos(req_path, headers=req_headers,
+                                  body=req_body, method="POST")
         if "uuid_catalog" in data:
             return data.get("uuid_catalog")
         else:
-            m = "_uuid_catalog request returned %s. No uuid_catalog found" \
-                % data
-            self.logger.error(m)
-            raise AstakosClientException(m)
+            msg = "_uuid_catalog request returned %s. No uuid_catalog found" \
+                  % data
+            self.logger.error(msg)
+            raise AstakosClientException(msg)
 
-    def get_usernames(self, token, uuids):
+    def get_usernames(self, uuids):
         """Return a uuid_catalog dictionary for the given uuids
 
         Keyword arguments:
-        token   -- user's token (string)
         uuids   -- list of user ids (list of strings)
 
         The returned uuid_catalog is a dictionary with uuids as
         keys and the corresponding user names as values
 
         """
-        req_path = copy(API_USERCATALOGS)
-        return self._uuid_catalog(token, uuids, req_path)
+        return self._uuid_catalog(uuids, self.api_usercatalogs)
 
-    def get_username(self, token, uuid):
+    def get_username(self, uuid):
         """Return the user name of a uuid (see get_usernames)"""
         check_input("get_username", self.logger, uuid=uuid)
-        uuid_dict = self.get_usernames(token, [uuid])
+        uuid_dict = self.get_usernames([uuid])
         if uuid in uuid_dict:
             return uuid_dict.get(uuid)
         else:
             raise NoUserName(uuid)
 
-    def service_get_usernames(self, token, uuids):
+    def service_get_usernames(self, uuids):
         """Return a uuid_catalog dict using a service's token"""
-        req_path = copy(API_SERVICE_USERCATALOGS)
-        return self._uuid_catalog(token, uuids, req_path)
+        return self._uuid_catalog(uuids, self.api_service_usercatalogs)
 
-    def service_get_username(self, token, uuid):
+    def service_get_username(self, uuid):
         """Return the displayName of a uuid using a service's token"""
         check_input("service_get_username", self.logger, uuid=uuid)
-        uuid_dict = self.service_get_usernames(token, [uuid])
+        uuid_dict = self.service_get_usernames([uuid])
         if uuid in uuid_dict:
             return uuid_dict.get(uuid)
         else:
@@ -283,51 +319,51 @@ class AstakosClient():
     # ----------------------------------
     # do a POST to ``API_USERCATALOGS`` (or ``API_SERVICE_USERCATALOGS``)
     #   with {'displaynames': display_names}
-    def _displayname_catalog(self, token, display_names, req_path):
+    def _displayname_catalog(self, display_names, req_path):
+        """Helper function to retrieve display names catalog"""
         req_headers = {'content-type': 'application/json'}
         req_body = parse_request({'displaynames': display_names}, self.logger)
-        data = self._call_astakos(
-            token, req_path, req_headers, req_body, "POST")
+        data = self._call_astakos(req_path, headers=req_headers,
+                                  body=req_body, method="POST")
         if "displayname_catalog" in data:
             return data.get("displayname_catalog")
         else:
-            m = "_displayname_catalog request returned %s. " \
-                "No displayname_catalog found" % data
-            self.logger.error(m)
-            raise AstakosClientException(m)
+            msg = "_displayname_catalog request returned %s. " \
+                  "No displayname_catalog found" % data
+            self.logger.error(msg)
+            raise AstakosClientException(msg)
 
-    def get_uuids(self, token, display_names):
+    def get_uuids(self, display_names):
         """Return a displayname_catalog for the given names
 
         Keyword arguments:
-        token           -- user's token (string)
         display_names   -- list of user names (list of strings)
 
         The returned displayname_catalog is a dictionary with
         the names as keys and the corresponding uuids as values
 
         """
-        req_path = copy(API_USERCATALOGS)
-        return self._displayname_catalog(token, display_names, req_path)
+        return self._displayname_catalog(
+            display_names, self.api_usercatalogs)
 
-    def get_uuid(self, token, display_name):
+    def get_uuid(self, display_name):
         """Return the uuid of a name (see getUUIDs)"""
         check_input("get_uuid", self.logger, display_name=display_name)
-        name_dict = self.get_uuids(token, [display_name])
+        name_dict = self.get_uuids([display_name])
         if display_name in name_dict:
             return name_dict.get(display_name)
         else:
             raise NoUUID(display_name)
 
-    def service_get_uuids(self, token, display_names):
+    def service_get_uuids(self, display_names):
         """Return a display_name catalog using a service's token"""
-        req_path = copy(API_SERVICE_USERCATALOGS)
-        return self._displayname_catalog(token, display_names, req_path)
+        return self._displayname_catalog(
+            display_names, self.api_service_usercatalogs)
 
-    def service_get_uuid(self, token, display_name):
+    def service_get_uuid(self, display_name):
         """Return the uuid of a name using a service's token"""
         check_input("service_get_uuid", self.logger, display_name=display_name)
-        name_dict = self.service_get_uuids(token, [display_name])
+        name_dict = self.service_get_uuids([display_name])
         if display_name in name_dict:
             return name_dict.get(display_name)
         else:
@@ -337,21 +373,20 @@ class AstakosClient():
     # do a GET to ``API_GETSERVICES``
     def get_services(self):
         """Return a list of dicts with the registered services"""
-        return self._call_astakos(None, copy(API_GETSERVICES))
+        return self._call_astakos(self.api_getservices)
 
     # ----------------------------------
     # do a GET to ``API_RESOURCES``
     def get_resources(self):
         """Return a dict of dicts with the available resources"""
-        return self._call_astakos(None, copy(API_RESOURCES))
+        return self._call_astakos(self.api_resources)
 
     # ----------------------------------
     # do a POST to ``API_FEEDBACK``
-    def send_feedback(self, token, message, data):
+    def send_feedback(self, message, data):
         """Send feedback to astakos service
 
         keyword arguments:
-        token       -- user's token (string)
         message     -- Feedback message
         data        -- Additional information about service client status
 
@@ -360,55 +395,63 @@ class AstakosClient():
 
         """
         check_input("send_feedback", self.logger, message=message, data=data)
-        path = copy(API_FEEDBACK)
         req_body = urllib.urlencode(
             {'feedback_msg': message, 'feedback_data': data})
-        self._call_astakos(token, path, None, req_body, "POST")
+        self._call_astakos(self.api_feedback, headers=None,
+                           body=req_body, method="POST")
 
     # ----------------------------------
     # do a POST to ``API_TOKENS``
-    def get_endpoints(self, token, uuid=None):
-        """ Fallback call for authenticate
+    def get_endpoints(self, tenant_name=None, non_authentication=False):
+        """ Authenticate and get services' endpoints
 
         Keyword arguments:
-        token   -- user's token (string)
-        uuid    -- user's uniq id
+        tenant_name         -- user's uniq id (optional)
+        non_authentication  -- get only non authentication protected info
+
 
         It returns back the token as well as information about the token
         holder and the services he/she can acess (in json format).
+
+        The tenant_name is optional and if it is given it must match the
+        user's uuid.
+
+        In case on of the `name', `type', `region', `version_id' parameters
+        is given, return only the endpoints that match all of these criteria.
+        If no match is found then raise NoEndpoints exception.
+
         In case of error raise an AstakosClientException.
 
         """
-        req_path = copy(API_TOKENS)
         req_headers = {'content-type': 'application/json'}
-        body = {'auth': {'token': {'id': token}}}
-        if uuid is not None:
-            body['auth']['tenantName'] = uuid
-        req_body = parse_request(body, self.logger)
-        return self._call_astakos(token, req_path, req_headers,
-                                  req_body, "POST", False)
+        if non_authentication:
+            req_body = None
+        else:
+            body = {'auth': {'token': {'id': self.token}}}
+            if tenant_name is not None:
+                body['auth']['tenantName'] = tenant_name
+            req_body = parse_request(body, self.logger)
+        return self._call_astakos(self.api_tokens, headers=req_headers,
+                                  body=req_body, method="POST",
+                                  log_body=False)
 
     # ----------------------------------
     # do a GET to ``API_QUOTAS``
-    def get_quotas(self, token):
+    def get_quotas(self):
         """Get user's quotas
 
-        Keyword arguments:
-        token   -- user's token (string)
-
         In case of success return a dict of dicts with user's current quotas.
         Otherwise raise an AstakosClientException
 
         """
-        return self._call_astakos(token, copy(API_QUOTAS))
+        return self._call_astakos(self.api_quotas)
 
     # ----------------------------------
     # do a GET to ``API_SERVICE_QUOTAS``
-    def service_get_quotas(self, token, user=None):
+    def service_get_quotas(self, user=None):
         """Get all quotas for resources associated with the service
 
         Keyword arguments:
-        token   -- service's token (string)
         user    -- optionally, the uuid of a specific user
 
         In case of success return a dict of dicts of dicts with current quotas
@@ -416,18 +459,17 @@ class AstakosClient():
         Otherwise raise an AstakosClientException
 
         """
-        query = copy(API_SERVICE_QUOTAS)
+        query = self.api_service_quotas
         if user is not None:
             query += "?user=" + user
-        return self._call_astakos(token, query)
+        return self._call_astakos(query)
 
     # ----------------------------------
     # do a POST to ``API_COMMISSIONS``
-    def issue_commission(self, token, request):
+    def issue_commission(self, request):
         """Issue a commission
 
         Keyword arguments:
-        token   -- service's token (string)
         request -- commision request (dict)
 
         In case of success return commission's id (int).
@@ -437,8 +479,10 @@ class AstakosClient():
         req_headers = {'content-type': 'application/json'}
         req_body = parse_request(request, self.logger)
         try:
-            response = self._call_astakos(token, copy(API_COMMISSIONS),
-                                          req_headers, req_body, "POST")
+            response = self._call_astakos(self.api_commissions,
+                                          headers=req_headers,
+                                          body=req_body,
+                                          method="POST")
         except AstakosClientException as err:
             if err.status == 413:
                 raise QuotaLimit(err.message, err.details)
@@ -448,17 +492,16 @@ class AstakosClient():
         if "serial" in response:
             return response['serial']
         else:
-            m = "issue_commission_core request returned %s. No serial found" \
-                % response
-            self.logger.error(m)
-            raise AstakosClientException(m)
+            msg = "issue_commission_core request returned %s. " + \
+                  "No serial found" % response
+            self.logger.error(msg)
+            raise AstakosClientException(msg)
 
-    def issue_one_commission(self, token, holder, source, provisions,
+    def issue_one_commission(self, holder, source, provisions,
                              name="", force=False, auto_accept=False):
         """Issue one commission (with specific holder and source)
 
         keyword arguments:
-        token       -- service's token (string)
         holder      -- user's id (string)
         source      -- commission's source (ex system) (string)
         provisions  -- resources with their quantity (dict from string to int)
@@ -482,36 +525,32 @@ class AstakosClient():
         try:
             request["provisions"] = []
             for resource, quantity in provisions.iteritems():
-                t = {"holder": holder, "source": source,
-                     "resource": resource, "quantity": quantity}
-                request["provisions"].append(t)
+                prov = {"holder": holder, "source": source,
+                        "resource": resource, "quantity": quantity}
+                request["provisions"].append(prov)
         except Exception as err:
             self.logger.error(str(err))
             raise BadValue(str(err))
 
-        return self.issue_commission(token, request)
+        return self.issue_commission(request)
 
     # ----------------------------------
     # do a GET to ``API_COMMISSIONS``
-    def get_pending_commissions(self, token):
+    def get_pending_commissions(self):
         """Get Pending Commissions
 
-        Keyword arguments:
-        token   -- service's token (string)
-
         In case of success return a list of pending commissions' ids
         (list of integers)
 
         """
-        return self._call_astakos(token, copy(API_COMMISSIONS))
+        return self._call_astakos(self.api_commissions)
 
     # ----------------------------------
     # do a GET to ``API_COMMISSIONS``/<serial>
-    def get_commission_info(self, token, serial):
+    def get_commission_info(self, serial):
         """Get Description of a Commission
 
         Keyword arguments:
-        token   -- service's token (string)
         serial  -- commission's id (int)
 
         In case of success return a dict of dicts containing
@@ -520,16 +559,15 @@ class AstakosClient():
         """
         check_input("get_commission_info", self.logger, serial=serial)
 
-        path = API_COMMISSIONS + "/" + str(serial)
-        return self._call_astakos(token, path)
+        path = self.api_commissions.rstrip('/') + "/" + str(serial)
+        return self._call_astakos(path)
 
     # ----------------------------------
     # do a POST to ``API_COMMISSIONS``/<serial>/action"
-    def commission_action(self, token, serial, action):
+    def commission_action(self, serial, action):
         """Perform a commission action
 
         Keyword arguments:
-        token   -- service's token (string)
         serial  -- commission's id (int)
         action  -- action to perform, currently accept/reject (string)
 
@@ -539,26 +577,26 @@ class AstakosClient():
         check_input("commission_action", self.logger,
                     serial=serial, action=action)
 
-        path = API_COMMISSIONS + "/" + str(serial) + "/action"
+        path = self.api_commissions.rstrip('/') + "/" + str(serial) + "/action"
         req_headers = {'content-type': 'application/json'}
         req_body = parse_request({str(action): ""}, self.logger)
-        self._call_astakos(token, path, req_headers, req_body, "POST")
+        self._call_astakos(path, headers=req_headers,
+                           body=req_body, method="POST")
 
-    def accept_commission(self, token, serial):
+    def accept_commission(self, serial):
         """Accept a commission (see commission_action)"""
-        self.commission_action(token, serial, "accept")
+        self.commission_action(serial, "accept")
 
-    def reject_commission(self, token, serial):
+    def reject_commission(self, serial):
         """Reject a commission (see commission_action)"""
-        self.commission_action(token, serial, "reject")
+        self.commission_action(serial, "reject")
 
     # ----------------------------------
     # do a POST to ``API_COMMISSIONS_ACTION``
-    def resolve_commissions(self, token, accept_serials, reject_serials):
+    def resolve_commissions(self, accept_serials, reject_serials):
         """Resolve multiple commissions at once
 
         Keyword arguments:
-        token           -- service's token (string)
         accept_serials  -- commissions to accept (list of ints)
         reject_serials  -- commissions to reject (list of ints)
 
@@ -571,27 +609,26 @@ class AstakosClient():
                     accept_serials=accept_serials,
                     reject_serials=reject_serials)
 
-        path = copy(API_COMMISSIONS_ACTION)
         req_headers = {'content-type': 'application/json'}
         req_body = parse_request({"accept": accept_serials,
                                   "reject": reject_serials},
                                  self.logger)
-        return self._call_astakos(token, path, req_headers, req_body, "POST")
+        return self._call_astakos(self.api_commissions_action,
+                                  headers=req_headers, body=req_body,
+                                  method="POST")
 
     # ----------------------------
     # do a GET to ``API_PROJECTS``
-    def get_projects(self, token, name=None, state=None, owner=None):
+    def get_projects(self, name=None, state=None, owner=None):
         """Retrieve all accessible projects
 
         Arguments:
-        token -- user's token (string)
         name  -- filter by name (optional)
         state -- filter by state (optional)
         owner -- filter by owner (optional)
 
         In case of success, return a list of project descriptions.
         """
-        path = API_PROJECTS
         filters = {}
         if name is not None:
             filters["name"] = name
@@ -602,62 +639,61 @@ class AstakosClient():
         req_headers = {'content-type': 'application/json'}
         req_body = (parse_request({"filter": filters}, self.logger)
                     if filters else None)
-        return self._call_astakos(token, path, req_headers, req_body)
+        return self._call_astakos(self.api_projects,
+                                  headers=req_headers, body=req_body)
 
     # -----------------------------------------
     # do a GET to ``API_PROJECTS``/<project_id>
-    def get_project(self, token, project_id):
+    def get_project(self, project_id):
         """Retrieve project description, if accessible
 
         Arguments:
-        token      -- user's token (string)
         project_id -- project identifier
 
         In case of success, return project description.
         """
-        path = join_urls(API_PROJECTS, str(project_id))
-        return self._call_astakos(token, path)
+        path = join_urls(self.api_projects, str(project_id))
+        return self._call_astakos(path)
 
     # -----------------------------
     # do a POST to ``API_PROJECTS``
-    def create_project(self, token, specs):
+    def create_project(self, specs):
         """Submit application to create a new project
 
         Arguments:
-        token -- user's token (string)
         specs -- dict describing a project
 
         In case of success, return project and application identifiers.
         """
-        path = API_PROJECTS
         req_headers = {'content-type': 'application/json'}
         req_body = parse_request(specs, self.logger)
-        return self._call_astakos(token, path, req_headers, req_body, "POST")
+        return self._call_astakos(self.api_projects,
+                                  headers=req_headers, body=req_body,
+                                  method="POST")
 
     # ------------------------------------------
     # do a POST to ``API_PROJECTS``/<project_id>
-    def modify_project(self, token, project_id, specs):
+    def modify_project(self, project_id, specs):
         """Submit application to modify an existing project
 
         Arguments:
-        token      -- user's token (string)
         project_id -- project identifier
         specs      -- dict describing a project
 
         In case of success, return project and application identifiers.
         """
-        path = join_urls(API_PROJECTS, str(project_id))
+        path = join_urls(self.api_projects, str(project_id))
         req_headers = {'content-type': 'application/json'}
         req_body = parse_request(specs, self.logger)
-        return self._call_astakos(token, path, req_headers, req_body, "POST")
+        return self._call_astakos(path, headers=req_headers,
+                                  body=req_body, method="POST")
 
     # -------------------------------------------------
     # do a POST to ``API_PROJECTS``/<project_id>/action
-    def project_action(self, token, project_id, action, reason=""):
+    def project_action(self, project_id, action, reason=""):
         """Perform action on a project
 
         Arguments:
-        token      -- user's token (string)
         project_id -- project identifier
         action     -- action to perform, one of "suspend", "unsuspend",
                       "terminate", "reinstate"
@@ -665,50 +701,48 @@ class AstakosClient():
 
         In case of success, return nothing.
         """
-        path = join_urls(API_PROJECTS, str(project_id))
+        path = join_urls(self.api_projects, str(project_id))
         path = join_urls(path, "action")
         req_headers = {'content-type': 'application/json'}
         req_body = parse_request({action: reason}, self.logger)
-        return self._call_astakos(token, path, req_headers, req_body, "POST")
+        return self._call_astakos(path, headers=req_headers,
+                                  body=req_body, method="POST")
 
     # --------------------------------
     # do a GET to ``API_APPLICATIONS``
-    def get_applications(self, token, project=None):
+    def get_applications(self, project=None):
         """Retrieve all accessible applications
 
         Arguments:
-        token   -- user's token (string)
         project -- filter by project (optional)
 
         In case of success, return a list of application descriptions.
         """
-        path = API_APPLICATIONS
         req_headers = {'content-type': 'application/json'}
         body = {"project": project} if project is not None else None
         req_body = parse_request(body, self.logger) if body else None
-        return self._call_astakos(token, path, req_headers, req_body)
+        return self._call_astakos(self.api_applications,
+                                  headers=req_headers, body=req_body)
 
     # -----------------------------------------
     # do a GET to ``API_APPLICATIONS``/<app_id>
-    def get_application(self, token, app_id):
+    def get_application(self, app_id):
         """Retrieve application description, if accessible
 
         Arguments:
-        token  -- user's token (string)
         app_id -- application identifier
 
         In case of success, return application description.
         """
-        path = join_urls(API_APPLICATIONS, str(app_id))
-        return self._call_astakos(token, path)
+        path = join_urls(self.api_applications, str(app_id))
+        return self._call_astakos(path)
 
     # -------------------------------------------------
     # do a POST to ``API_APPLICATIONS``/<app_id>/action
-    def application_action(self, token, app_id, action, reason=""):
+    def application_action(self, app_id, action, reason=""):
         """Perform action on an application
 
         Arguments:
-        token  -- user's token (string)
         app_id -- application identifier
         action -- action to perform, one of "approve", "deny",
                   "dismiss", "cancel"
@@ -716,50 +750,48 @@ class AstakosClient():
 
         In case of success, return nothing.
         """
-        path = join_urls(API_APPLICATIONS, str(app_id))
+        path = join_urls(self.api_applications, str(app_id))
         path = join_urls(path, "action")
         req_headers = {'content-type': 'application/json'}
         req_body = parse_request({action: reason}, self.logger)
-        return self._call_astakos(token, path, req_headers, req_body, "POST")
+        return self._call_astakos(path, headers=req_headers,
+                                  body=req_body, method="POST")
 
     # -------------------------------
     # do a GET to ``API_MEMBERSHIPS``
-    def get_memberships(self, token, project=None):
+    def get_memberships(self, project=None):
         """Retrieve all accessible memberships
 
         Arguments:
-        token   -- user's token (string)
         project -- filter by project (optional)
 
         In case of success, return a list of membership descriptions.
         """
-        path = API_MEMBERSHIPS
         req_headers = {'content-type': 'application/json'}
         body = {"project": project} if project is not None else None
         req_body = parse_request(body, self.logger) if body else None
-        return self._call_astakos(token, path, req_headers, req_body)
+        return self._call_astakos(self.api_memberships,
+                                  headers=req_headers, body=req_body)
 
     # -----------------------------------------
     # do a GET to ``API_MEMBERSHIPS``/<memb_id>
-    def get_membership(self, token, memb_id):
+    def get_membership(self, memb_id):
         """Retrieve membership description, if accessible
 
         Arguments:
-        token   -- user's token (string)
         memb_id -- membership identifier
 
         In case of success, return membership description.
         """
-        path = join_urls(API_MEMBERSHIPS, str(memb_id))
-        return self._call_astakos(token, path)
+        path = join_urls(self.api_memberships, str(memb_id))
+        return self._call_astakos(path)
 
     # -------------------------------------------------
     # do a POST to ``API_MEMBERSHIPS``/<memb_id>/action
-    def membership_action(self, token, memb_id, action, reason=""):
+    def membership_action(self, memb_id, action, reason=""):
         """Perform action on a membership
 
         Arguments:
-        token   -- user's token (string)
         memb_id -- membership identifier
         action  -- action to perform, one of "leave", "cancel", "accept",
                    "reject", "remove"
@@ -767,46 +799,96 @@ class AstakosClient():
 
         In case of success, return nothing.
         """
-        path = join_urls(API_MEMBERSHIPS, str(memb_id))
+        path = join_urls(self.api_memberships, str(memb_id))
         path = join_urls(path, "action")
         req_headers = {'content-type': 'application/json'}
         req_body = parse_request({action: reason}, self.logger)
-        return self._call_astakos(token, path, req_headers, req_body, "POST")
+        return self._call_astakos(path, headers=req_headers,
+                                  body=req_body, method="POST")
 
     # --------------------------------
     # do a POST to ``API_MEMBERSHIPS``
-    def join_project(self, token, project_id):
+    def join_project(self, project_id):
         """Join a project
 
         Arguments:
-        token      -- user's token (string)
         project_id -- project identifier
 
         In case of success, return membership identifier.
         """
-        path = API_MEMBERSHIPS
         req_headers = {'content-type': 'application/json'}
         body = {"join": {"project": project_id}}
         req_body = parse_request(body, self.logger)
-        return self._call_astakos(token, path, req_headers, req_body, "POST")
+        return self._call_astakos(self.api_memberships, headers=req_headers,
+                                  body=req_body, method="POST")
 
     # --------------------------------
     # do a POST to ``API_MEMBERSHIPS``
-    def enroll_member(self, token, project_id, email):
+    def enroll_member(self, project_id, email):
         """Enroll a user in a project
 
         Arguments:
-        token      -- user's token (string)
         project_id -- project identifier
         email      -- user identified by email
 
         In case of success, return membership identifier.
         """
-        path = API_MEMBERSHIPS
         req_headers = {'content-type': 'application/json'}
         body = {"enroll": {"project": project_id, "user": email}}
         req_body = parse_request(body, self.logger)
-        return self._call_astakos(token, path, req_headers, req_body, "POST")
+        return self._call_astakos(self.api_memberships, headers=req_headers,
+                                  body=req_body, method="POST")
+
+
+# --------------------------------------------------------------------
+# parse endpoints
+def parse_endpoints(endpoints, ep_name=None, ep_type=None,
+                    ep_region=None, ep_version_id=None):
+    """Parse endpoints server response and extract the ones needed
+
+    Keyword arguments:
+    endpoints     -- the endpoints (json response from get_endpoints)
+    ep_name       -- return only endpoints with this name (optional)
+    ep_type       -- return only endpoints with this type (optional)
+    ep_region     -- return only endpoints with this region (optional)
+    ep_version_id -- return only endpoints with this versionId (optional)
+
+    In case on of the `name', `type', `region', `version_id' parameters
+    is given, return only the endpoints that match all of these criteria.
+    If no match is found then raise NoEndpoints exception.
+
+    """
+    try:
+        catalog = endpoints['access']['serviceCatalog']
+        if ep_name is not None:
+            catalog = \
+                [c for c in catalog if c['name'] == ep_name]
+        if ep_type is not None:
+            catalog = \
+                [c for c in catalog if c['type'] == ep_type]
+        if ep_region is not None:
+            for c in catalog:
+                c['endpoints'] = [e for e in c['endpoints']
+                                  if e['region'] == ep_region]
+            # Remove catalog entries with no endpoints
+            catalog = \
+                [c for c in catalog if c['endpoints']]
+        if ep_version_id is not None:
+            for c in catalog:
+                c['endpoints'] = [e for e in c['endpoints']
+                                  if e['versionId'] == ep_version_id]
+            # Remove catalog entries with no endpoints
+            catalog = \
+                [c for c in catalog if c['endpoints']]
+
+        if not catalog:
+            raise NoEndpoints(ep_name, ep_type,
+                              ep_region, ep_version_id)
+        else:
+            return catalog
+    except KeyError:
+        raise NoEndpoints()
+
 
 # --------------------------------------------------------------------
 # Private functions
diff --git a/astakosclient/astakosclient/errors.py b/astakosclient/astakosclient/errors.py
index a5f66beff3fdbfd4cf99fdf951ad22f84229720c..a85d38da02da784637ea4316565f10a1e45fe83f 100644
--- a/astakosclient/astakosclient/errors.py
+++ b/astakosclient/astakosclient/errors.py
@@ -31,8 +31,13 @@
 # interpreted as representing official policies, either expressed
 # or implied, of GRNET S.A.
 
+"""
+Astakos Client Exceptions
+"""
+
 
 class AstakosClientException(Exception):
+    """Base AstakosClientException Class"""
     def __init__(self, message='', details='', status=500):
         self.message = message
         self.details = details
@@ -43,47 +48,65 @@ class AstakosClientException(Exception):
 
 
 class BadValue(AstakosClientException):
+    """Re-define ValueError Exception under AstakosClientException"""
     def __init__(self, details):
-        """Re-define ValueError Exception under AstakosClientException"""
         message = "ValueError"
         super(BadValue, self).__init__(message, details)
 
 
 class InvalidResponse(AstakosClientException):
+    """Return simplejson parse Exception as AstakosClient one"""
     def __init__(self, message, details):
-        """Return simplejson parse Exception as AstakosClient one"""
         super(InvalidResponse, self).__init__(message, details)
 
 
 class BadRequest(AstakosClientException):
+    """BadRequest Exception"""
     status = 400
 
 
 class Unauthorized(AstakosClientException):
+    """Unauthorized Exception"""
     status = 401
 
 
 class Forbidden(AstakosClientException):
+    """Forbidden Exception"""
     status = 403
 
 
 class NotFound(AstakosClientException):
+    """NotFound Exception"""
     status = 404
 
 
 class QuotaLimit(AstakosClientException):
+    """QuotaLimit Exception"""
     status = 413
 
 
 class NoUserName(AstakosClientException):
+    """No display name for the given uuid"""
     def __init__(self, uuid):
-        """No display name for the given uuid"""
         message = "No display name for the given uuid: %s" % uuid
         super(NoUserName, self).__init__(message)
 
 
 class NoUUID(AstakosClientException):
+    """No uuid for the given display name"""
     def __init__(self, display_name):
-        """No uuid for the given display name"""
         message = "No uuid for the given display name: %s" % display_name
         super(NoUUID, self).__init__(message)
+
+
+class NoEndpoints(AstakosClientException):
+    """No endpoints found matching the criteria given"""
+    def __init__(self, ep_name, ep_type, ep_region, ep_version_id):
+        message = "No endpoints found matching" + \
+                  (", name = %s" % ep_name) if ep_name is not None else "" + \
+                  (", type = %s" % ep_type) if ep_type is not None else "" + \
+                  (", region = %s" % ep_region) \
+                  if ep_region is not None else "" + \
+                  (", version_id = %s" % ep_version_id) \
+                  if ep_version_id is not None else "."
+        super(NoEndpoints, self).__init__(message)
diff --git a/astakosclient/astakosclient/keypath.py b/astakosclient/astakosclient/keypath.py
deleted file mode 100644
index 883a40c8d3f7785444c12dff74f7f14fbc3619f3..0000000000000000000000000000000000000000
--- a/astakosclient/astakosclient/keypath.py
+++ /dev/null
@@ -1,216 +0,0 @@
-# Copyright 2012, 2013 GRNET S.A. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or
-# without modification, are permitted provided that the following
-# conditions are met:
-#
-#   1. Redistributions of source code must retain the above
-#      copyright notice, this list of conditions and the following
-#      disclaimer.
-#
-#   2. Redistributions in binary form must reproduce the above
-#      copyright notice, this list of conditions and the following
-#      disclaimer in the documentation and/or other materials
-#      provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
-# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
-# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
-# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-#
-# The views and conclusions contained in the software and
-# documentation are those of the authors and should not be
-# interpreted as representing official policies, either expressed
-# or implied, of GRNET S.A.
-import copy
-
-
-def dict_merge(a, b):
-    """
-    http://www.xormedia.com/recursively-merge-dictionaries-in-python/
-    """
-    if not isinstance(b, dict):
-        return b
-    result = copy.deepcopy(a)
-    for k, v in b.iteritems():
-        if k in result and isinstance(result[k], dict):
-                result[k] = dict_merge(result[k], v)
-        else:
-            result[k] = copy.deepcopy(v)
-    return result
-
-
-def lookup_path(container, path, sep='.', createpath=False):
-    """
-    return (['a','b'],
-            [container['a'], container['a']['b']],
-            'c')  where path=sep.join(['a','b','c'])
-
-    """
-    names = path.split(sep)
-    dirnames = names[:-1]
-    basename = names[-1]
-
-    node = container
-    name_path = []
-    node_path = [node]
-    for name in dirnames:
-        name_path.append(name)
-        if name not in node:
-            if not createpath:
-                m = "'{0}': path not found".format(sep.join(name_path))
-                raise KeyError(m)
-            node[name] = {}
-        try:
-            node = node[name]
-        except TypeError as e:
-            m = "'{0}': cannot traverse path beyond this node: {1}"
-            m = m.format(sep.join(name_path), str(e))
-            raise ValueError(m)
-        node_path.append(node)
-
-    return name_path, node_path, basename
-
-
-def walk_paths(container):
-    for name, node in container.iteritems():
-        if not hasattr(node, 'items'):
-            yield [name], [node]
-        else:
-            for names, nodes in walk_paths(node):
-                yield [name] + names, [node] + nodes
-
-
-def list_paths(container, sep='.'):
-    """
-    >>> sorted(list_paths({'a': {'b': {'c': 'd'}}}))
-    [('a.b.c', 'd')]
-    >>> sorted(list_paths({'a': {'b': {'c': 'd'}, 'e': 3}}))
-    [('a.b.c', 'd'), ('a.e', 3)]
-    >>> sorted(list_paths({'a': {'b': {'c': 'd'}, 'e': {'f': 3}}}))
-    [('a.b.c', 'd'), ('a.e.f', 3)]
-    >>> list_paths({})
-    []
-
-    """
-    return [(sep.join(name_path), node_path[-1])
-            for name_path, node_path in walk_paths(container)]
-
-
-def del_path(container, path, sep='.', collect=True):
-    """
-    del container['a']['b']['c'] where path=sep.join(['a','b','c'])
-
-    >>> d = {'a': {'b': {'c': 'd'}}}; del_path(d, 'a.b.c'); d
-    {}
-    >>> d = {'a': {'b': {'c': 'd'}}}; del_path(d, 'a.b.c', collect=False); d
-    {'a': {'b': {}}}
-    >>> d = {'a': {'b': {'c': 'd'}}}; del_path(d, 'a.b.c.d')
-    Traceback (most recent call last):
-    ValueError: 'a.b.c': cannot traverse path beyond this node:\
- 'str' object does not support item deletion
-    """
-
-    name_path, node_path, basename = \
-        lookup_path(container, path, sep=sep, createpath=False)
-
-    lastnode = node_path.pop()
-    try:
-        if basename in lastnode:
-            del lastnode[basename]
-    except (TypeError, KeyError) as e:
-        m = "'{0}': cannot traverse path beyond this node: {1}"
-        m = m.format(sep.join(name_path), str(e))
-        raise ValueError(m)
-
-    if collect:
-        while node_path and not lastnode:
-            basename = name_path.pop()
-            lastnode = node_path.pop()
-            del lastnode[basename]
-
-
-def get_path(container, path, sep='.'):
-    """
-    return container['a']['b']['c'] where path=sep.join(['a','b','c'])
-
-    >>> get_path({'a': {'b': {'c': 'd'}}}, 'a.b.c.d')
-    Traceback (most recent call last):
-    ValueError: 'a.b.c.d': cannot traverse path beyond this node:\
- string indices must be integers, not str
-    >>> get_path({'a': {'b': {'c': 1}}}, 'a.b.c.d')
-    Traceback (most recent call last):
-    ValueError: 'a.b.c.d': cannot traverse path beyond this node:\
- 'int' object is unsubscriptable
-    >>> get_path({'a': {'b': {'c': 1}}}, 'a.b.c')
-    1
-    >>> get_path({'a': {'b': {'c': 1}}}, 'a.b')
-    {'c': 1}
-
-    """
-    name_path, node_path, basename = \
-        lookup_path(container, path, sep=sep, createpath=False)
-    name_path.append(basename)
-    node = node_path[-1]
-
-    try:
-        return node[basename]
-    except TypeError as e:
-        m = "'{0}': cannot traverse path beyond this node: {1}"
-        m = m.format(sep.join(name_path), str(e))
-        raise ValueError(m)
-    except KeyError as e:
-        m = "'{0}': path not found: {1}"
-        m = m.format(sep.join(name_path), str(e))
-        raise KeyError(m)
-
-
-def set_path(container, path, value, sep='.',
-             createpath=False, overwrite=True):
-    """
-    container['a']['b']['c'] = value where path=sep.join(['a','b','c'])
-
-    >>> set_path({'a': {'b': {'c': 'd'}}}, 'a.b.c.d', 1)
-    Traceback (most recent call last):
-    ValueError: 'a.b.c.d': cannot traverse path beyond this node:\
- 'str' object does not support item assignment
-    >>> set_path({'a': {'b': {'c': 'd'}}}, 'a.b.x.d', 1)
-    Traceback (most recent call last):
-    KeyError: "'a.b.x': path not found"
-    >>> set_path({'a': {'b': {'c': 'd'}}}, 'a.b.x.d', 1, createpath=True)
-
-    >>> set_path({'a': {'b': {'c': 'd'}}}, 'a.b.c', 1)
-
-    >>> set_path({'a': {'b': {'c': 'd'}}}, 'a.b.c', 1, overwrite=False)
-    Traceback (most recent call last):
-    ValueError: will not overwrite path 'a.b.c'
-
-    """
-    name_path, node_path, basename = \
-        lookup_path(container, path, sep=sep, createpath=createpath)
-    name_path.append(basename)
-    node = node_path[-1]
-
-    if basename in node and not overwrite:
-        m = "will not overwrite path '{0}'".format(path)
-        raise ValueError(m)
-
-    try:
-        node[basename] = value
-    except TypeError as e:
-        m = "'{0}': cannot traverse path beyond this node: {1}"
-        m = m.format(sep.join(name_path), str(e))
-        raise ValueError(m)
-
-
-if __name__ == '__main__':
-    import doctest
-    doctest.testmod()
diff --git a/astakosclient/astakosclient/services.py b/astakosclient/astakosclient/services.py
deleted file mode 100644
index 98fa6b144ba23e554ad269ab1c9af965f429f02b..0000000000000000000000000000000000000000
--- a/astakosclient/astakosclient/services.py
+++ /dev/null
@@ -1,88 +0,0 @@
-# Copyright (C) 2013 GRNET S.A. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or
-# without modification, are permitted provided that the following
-# conditions are met:
-#
-#   1. Redistributions of source code must retain the above
-#      copyright notice, this list of conditions and the following
-#      disclaimer.
-#
-#   2. Redistributions in binary form must reproduce the above
-#      copyright notice, this list of conditions and the following
-#      disclaimer in the documentation and/or other materials
-#      provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
-# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
-# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
-# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-#
-# The views and conclusions contained in the software and
-# documentation are those of the authors and should not be
-# interpreted as representing official policies, either expressed
-# or implied, of GRNET S.A.
-
-
-astakos_services = {
-    'astakos_account': {
-        'type': 'account',
-        'component': 'astakos',
-        'prefix': 'account',
-        'public': True,
-        'endpoints': [
-            {'versionId': 'v1.0',
-             'publicURL': None},
-        ],
-        'resources': {
-            'pending_app': {
-                'desc': "Number of pending project applications",
-                'name': "astakos.pending_app",
-                'service_type': "account",
-                'service_origin': "astakos_account",
-                'allow_in_projects': False},
-        },
-    },
-
-    'astakos_identity': {
-        'type': 'identity',
-        'component': 'astakos',
-        'prefix': 'identity',
-        'public': True,
-        'endpoints': [
-            {'versionId': 'v2.0',
-             'publicURL': None},
-        ],
-        'resources': {},
-    },
-
-    'astakos_weblogin': {
-        'type': 'astakos_weblogin',
-        'component': 'astakos',
-        'prefix': 'weblogin',
-        'public': True,
-        'endpoints': [
-            {'versionId': '',
-             'publicURL': None},
-        ],
-    },
-
-    'astakos_ui': {
-        'type': 'astakos_ui',
-        'component': 'astakos',
-        'prefix': 'ui',
-        'public': False,
-        'endpoints': [
-            {'versionId': '',
-             'publicURL': None},
-        ],
-    },
-}
diff --git a/astakosclient/astakosclient/utils.py b/astakosclient/astakosclient/utils.py
index 22919cc4a5f445864488654cf1ae74f14f6305ec..70cd87e82a81e405b58f917a651ec6c92874fed3 100644
--- a/astakosclient/astakosclient/utils.py
+++ b/astakosclient/astakosclient/utils.py
@@ -31,6 +31,10 @@
 # interpreted as representing official policies, either expressed
 # or implied, of GRNET S.A.
 
+"""
+Astakos Client utility module
+"""
+
 from httplib import HTTPConnection, HTTPSConnection
 from contextlib import closing
 
@@ -39,8 +43,10 @@ from objpool.http import PooledHTTPConnection
 from astakosclient.errors import AstakosClientException, BadValue
 
 
-def retry(func):
+def retry_dec(func):
+    """Class Method Decorator"""
     def decorator(self, *args, **kwargs):
+        """Retry `self.retry' times if connection fails"""
         attemps = 0
         while True:
             try:
@@ -64,13 +70,16 @@ def retry(func):
 def scheme_to_class(scheme, use_pool, pool_size):
     """Return the appropriate conn class for given scheme"""
     def _objpool(netloc):
+        """Helper function to return a PooledHTTPConnection object"""
         return PooledHTTPConnection(
             netloc=netloc, scheme=scheme, size=pool_size)
 
     def _http_connection(netloc):
+        """Helper function to return an HTTPConnection object"""
         return closing(HTTPConnection(netloc))
 
     def _https_connection(netloc):
+        """Helper function to return an HTTPSConnection object"""
         return closing(HTTPSConnection(netloc))
 
     if scheme == "http":
@@ -92,16 +101,22 @@ def parse_request(request, logger):
     try:
         return simplejson.dumps(request)
     except Exception as err:
-        m = "Cannot parse request \"%s\" with simplejson: %s" \
-            % (request, str(err))
-        logger.error(m)
-        raise BadValue(m)
+        msg = "Cannot parse request \"%s\" with simplejson: %s" \
+              % (request, str(err))
+        logger.error(msg)
+        raise BadValue(msg)
 
 
 def check_input(function_name, logger, **kwargs):
     """Check if given arguments are not None"""
     for i in kwargs:
         if kwargs[i] is None:
-            m = "in " + function_name + ": " + str(i) + " parameter not given"
-            logger.error(m)
-            raise BadValue(m)
+            msg = "in " + function_name + ": " + \
+                  str(i) + " parameter not given"
+            logger.error(msg)
+            raise BadValue(msg)
+
+
+def join_urls(url_a, url_b):
+    """Join_urls from synnefo.lib"""
+    return url_a.rstrip("/") + "/" + url_b.lstrip("/")