diff --git a/core/server/api/db.js b/core/server/api/db.js
index b41edcf0be..03c8abfd36 100644
--- a/core/server/api/db.js
+++ b/core/server/api/db.js
@@ -15,11 +15,11 @@ api.notifications    = require('./notifications');
 api.settings         = require('./settings');
 
 db = {
-    'exportContent': function () {
-        var self = this;
+    'exportContent': function (options) {
+        options = options || {};
 
         // Export data, otherwise send error 500
-        return canThis(self.user).exportContent.db().then(function () {
+        return canThis(options.context).exportContent.db().then(function () {
             return dataExport().then(function (exportedData) {
                 return when.resolve({ db: [exportedData] });
             }).otherwise(function (error) {
@@ -30,10 +30,10 @@ db = {
         });
     },
     'importContent': function (options) {
-        var databaseVersion,
-            self = this;
+        options = options || {};
+        var databaseVersion;
 
-        return canThis(self.user).importContent.db().then(function () {
+        return canThis(options.context).importContent.db().then(function () {
             if (!options.importfile || !options.importfile.path || options.importfile.name.indexOf('json') === -1) {
                 /**
                  * Notify of an error if it occurs
@@ -46,7 +46,7 @@ db = {
                 return when.reject(new errors.InternalServerError('Please select a .json file to import.'));
             }
 
-            return api.settings.read.call({ internal: true }, { key: 'databaseVersion' }).then(function (response) {
+            return api.settings.read({key: 'databaseVersion', context: { internal: true }}).then(function (response) {
                 var setting = response.settings[0];
 
                 return when(setting.value);
@@ -108,10 +108,10 @@ db = {
             return when.reject(new errors.NoPermissionError('You do not have permission to export data. (no rights)'));
         });
     },
-    'deleteAllContent': function () {
-        var self = this;
+    'deleteAllContent': function (options) {
+        options = options || {};
 
-        return canThis(self.user).deleteAllContent.db().then(function () {
+        return canThis(options.context).deleteAllContent.db().then(function () {
             return when(dataProvider.deleteAllContent())
                 .then(function () {
                     return when.resolve({ db: [] });
diff --git a/core/server/api/index.js b/core/server/api/index.js
index 1617dccb3e..8ae5353e70 100644
--- a/core/server/api/index.js
+++ b/core/server/api/index.js
@@ -4,20 +4,46 @@
 var _             = require('lodash'),
     when          = require('when'),
     config        = require('../config'),
+    // Include Endpoints
     db            = require('./db'),
-    settings      = require('./settings'),
+    mail          = require('./mail'),
     notifications = require('./notifications'),
     posts         = require('./posts'),
-    users         = require('./users'),
+    settings      = require('./settings'),
     tags          = require('./tags'),
     themes        = require('./themes'),
-    mail          = require('./mail'),
-    requestHandler,
-    init;
+    users         = require('./users'),
 
-// ## Request Handlers
+    http,
+    formatHttpErrors,
+    cacheInvalidationHeader,
+    locationHeader,
+    contentDispositionHeader,
+    init,
 
-function cacheInvalidationHeader(req, result) {
+/**
+ * ### Init
+ * Initialise the API - populate the settings cache
+ * @return {Promise(Settings)} Resolves to Settings Collection
+ */
+init = function () {
+    return settings.updateSettingsCache();
+};
+
+/**
+ * ### Cache Invalidation Header
+ * Calculate the header string for the X-Cache-Invalidate: header.
+ * The resulting string instructs any cache in front of the blog that request has occurred which invalidates any cached
+ * versions of the listed URIs.
+ *
+ * `/*` is used to mean the entire cache is invalid
+ *
+ * @private
+ * @param {Express.request} req Original HTTP Request
+ * @param {Object} result API method result
+ * @return {Promise(String)} Resolves to header string
+ */
+cacheInvalidationHeader = function (req, result) {
     var parsedUrl = req._parsedUrl.pathname.replace(/\/$/, '').split('/'),
         method = req.method,
         endpoint = parsedUrl[4],
@@ -54,15 +80,20 @@ function cacheInvalidationHeader(req, result) {
     }
 
     return when(cacheInvalidate);
-}
+};
 
-// if api request results in the creation of a new object, construct
-// a Location: header that points to the new resource.
-//
-// arguments: request object, result object from the api call
-// returns: a promise that will be fulfilled with the location of the
-// resource
-function locationHeader(req, result) {
+/**
+ * ### Location Header
+ *
+ * If the API request results in the creation of a new object, construct a Location: header which points to the new
+ * resource.
+ *
+ * @private
+ * @param {Express.request} req Original HTTP Request
+ * @param {Object} result API method result
+ * @return {Promise(String)} Resolves to header string
+ */
+locationHeader = function (req, result) {
     var apiRoot = config.urlFor('api'),
         location,
         post,
@@ -81,98 +112,137 @@ function locationHeader(req, result) {
     }
 
     return when(location);
-}
+};
 
-// create a header that invokes the 'Save As' dialog
-// in the browser when exporting the database to file.
-// The 'filename' parameter is governed by [RFC6266](http://tools.ietf.org/html/rfc6266#section-4.3).
-//
-// for encoding whitespace and non-ISO-8859-1 characters, you MUST
-// use the "filename*=" attribute, NOT "filename=". Ideally, both.
-// see: http://tools.ietf.org/html/rfc598
-// examples: http://tools.ietf.org/html/rfc6266#section-5
-//
-// we'll use ISO-8859-1 characters here to keep it simple.
-function dbExportSaveAsHeader() {
+/**
+ * ### Content Disposition Header
+ * create a header that invokes the 'Save As' dialog in the browser when exporting the database to file. The 'filename'
+ * parameter is governed by [RFC6266](http://tools.ietf.org/html/rfc6266#section-4.3).
+ *
+ * For encoding whitespace and non-ISO-8859-1 characters, you MUST use the "filename*=" attribute, NOT "filename=".
+ * Ideally, both. Examples: http://tools.ietf.org/html/rfc6266#section-5
+ *
+ * We'll use ISO-8859-1 characters here to keep it simple.
+ *
+ * @private
+ * @see http://tools.ietf.org/html/rfc598
+ * @return {string}
+ */
+contentDispositionHeader = function () {
     // replace ':' with '_' for OS that don't support it
     var now = (new Date()).toJSON().replace(/:/g, '_');
     return 'Attachment; filename="ghost-' + now + '.json"';
-}
+};
 
-// ### requestHandler
-// decorator for api functions which are called via an HTTP request
-// takes the API method and wraps it so that it gets data from the request and returns a sensible JSON response
-requestHandler = function (apiMethod) {
+
+/**
+ * ### Format HTTP Errors
+ * Converts the error response from the API into a format which can be returned over HTTP
+ *
+ * @private
+ * @param {Array} error
+ * @return {{errors: Array, statusCode: number}}
+ */
+formatHttpErrors = function (error) {
+    var statusCode = 500,
+        errors = [];
+
+    if (!_.isArray(error)) {
+        error = [].concat(error);
+    }
+
+    _.each(error, function (errorItem) {
+        var errorContent = {};
+
+        //TODO: add logic to set the correct status code
+        statusCode = errorItem.code || 500;
+
+        errorContent.message = _.isString(errorItem) ? errorItem :
+            (_.isObject(errorItem) ? errorItem.message : 'Unknown API Error');
+        errorContent.type = errorItem.type || 'InternalServerError';
+        errors.push(errorContent);
+    });
+
+    return {errors: errors, statusCode: statusCode};
+};
+
+/**
+ * ### HTTP
+ *
+ * Decorator for API functions which are called via an HTTP request. Takes the API method and wraps it so that it gets
+ * data from the request and returns a sensible JSON response.
+ *
+ * @public
+ * @param {Function} apiMethod API method to call
+ * @return {Function} middleware format function to be called by the route when a matching request is made
+ */
+http = function (apiMethod) {
     return function (req, res) {
-        var options = _.extend(req.body, req.files, req.query, req.params),
-            apiContext = {
-                user: (req.session && req.session.user) ? req.session.user : null
-            };
-
-        return apiMethod.call(apiContext, options).then(function (result) {
-            return cacheInvalidationHeader(req, result).then(function (header) {
-                if (header) {
-                    res.set({
-                        "X-Cache-Invalidate": header
-                    });
+        // We define 2 properties for using as arguments in API calls:
+        var object = req.body,
+            options = _.extend({}, req.files, req.query, req.params, {
+                context: {
+                    user: (req.session && req.session.user) ? req.session.user : null
                 }
-            })
-            .then(function () {
-                if (apiMethod === db.exportContent) {
-                    res.set({
-                        "Content-Disposition": dbExportSaveAsHeader()
-                    });
-                }
-            })
-            .then(function () {
-                return locationHeader(req, result).then(function (header) {
-                    if (header) {
-                        res.set({
-                            'Location': header
-                        });
-                    }
-
-                    res.json(result || {});
-                });
-            });
-        }, function (error) {
-            var errorCode,
-                errors = [];
-
-            if (!_.isArray(error)) {
-                error = [].concat(error);
-            }
-
-            _.each(error, function (errorItem) {
-                var errorContent = {};
-
-                //TODO: add logic to set the correct status code
-                errorCode = errorItem.code || 500;
-                
-                errorContent['message'] = _.isString(errorItem) ? errorItem : (_.isObject(errorItem) ? errorItem.message : 'Unknown API Error');
-                errorContent['type'] = errorItem.type || 'InternalServerError';
-                errors.push(errorContent);
             });
 
-            res.json(errorCode, {errors: errors});
-        });
+        // If this is a GET, or a DELETE, req.body should be null, so we only have options (route and query params)
+        // If this is a PUT, POST, or PATCH, req.body is an object
+        if (_.isEmpty(object)) {
+            object = options;
+            options = {};
+        }
+
+        return apiMethod(object, options)
+            // Handle adding headers
+            .then(function onSuccess(result) {
+                // Add X-Cache-Invalidate header
+                return cacheInvalidationHeader(req, result)
+                    .then(function addCacheHeader(header) {
+                        if (header) {
+                            res.set({'X-Cache-Invalidate': header});
+                        }
+
+                        // Add Location header
+                        return locationHeader(req, result);
+                    }).then(function addLocationHeader(header) {
+                        if (header) {
+                            res.set({'Location': header});
+                        }
+
+                        // Add Content-Disposition Header
+                        if (apiMethod === db.exportContent) {
+                            res.set({
+                                'Content-Disposition': contentDispositionHeader()
+                            });
+                        }
+                        // #### Success
+                        // Send a properly formatting HTTP response containing the data with correct headers
+                        res.json(result || {});
+                    });
+            }).catch(function onError(error) {
+                // #### Error
+                var httpErrors = formatHttpErrors(error);
+                // Send a properly formatted HTTP response containing the errors
+                res.json(httpErrors.statusCode, {errors: httpErrors.errors});
+            });
     };
 };
 
-init = function () {
-    return settings.updateSettingsCache();
-};
-
-// Public API
+/**
+ * ## Public API
+ */
 module.exports = {
-    posts: posts,
-    users: users,
-    tags: tags,
-    themes: themes,
-    notifications: notifications,
-    settings: settings,
+    // Extras
+    init: init,
+    http: http,
+    // API Endpoints
     db: db,
     mail: mail,
-    requestHandler: requestHandler,
-    init: init
+    notifications: notifications,
+    posts: posts,
+    settings: settings,
+    tags: tags,
+    themes: themes,
+    users: users
 };
diff --git a/core/server/api/notifications.js b/core/server/api/notifications.js
index 6a5360d299..63478103c3 100644
--- a/core/server/api/notifications.js
+++ b/core/server/api/notifications.js
@@ -4,7 +4,7 @@ var when               = require('when'),
 
     // Holds the persistent notifications
     notificationsStore = [],
-    // Holds the last used id 
+    // Holds the last used id
     notificationCounter = 0,
     notifications;
 
@@ -15,16 +15,15 @@ notifications = {
         return when({ 'notifications': notificationsStore });
     },
 
-    // #### Destroy
-
-    // **takes:** an identifier object ({id: id})
-    destroy: function destroy(i) {
+    destroy: function destroy(options) {
         var notification = _.find(notificationsStore, function (element) {
-            return element.id === parseInt(i.id, 10);
+            return element.id === parseInt(options.id, 10);
         });
 
         if (notification && !notification.dismissable) {
-            return when.reject(new errors.NoPermissionError('You do not have permission to dismiss this notification.'));
+            return when.reject(
+                new errors.NoPermissionError('You do not have permission to dismiss this notification.')
+            );
         }
 
         if (!notification) {
@@ -32,7 +31,7 @@ notifications = {
         }
 
         notificationsStore = _.reject(notificationsStore, function (element) {
-            return element.id === parseInt(i.id, 10);
+            return element.id === parseInt(options.id, 10);
         });
         // **returns:** a promise for the deleted object
         return when({notifications: [notification]});
@@ -44,17 +43,20 @@ notifications = {
         return when(notificationsStore);
     },
 
-    // #### Add
-
-    // **takes:** a notification object of the form
-    // ```
-    //  msg = {
-    //      type: 'error', // this can be 'error', 'success', 'warn' and 'info'
-    //      message: 'This is an error', // A string. Should fit in one line.
-    //      location: 'bottom', // A string where this notification should appear. can be 'bottom' or 'top'
-    //      dismissable: true // A Boolean. Whether the notification is dismissable or not. 
-    //  };
-    // ```
+    /**
+     * ### Add
+     *
+     *
+     * **takes:** a notification object of the form
+     * ```
+     *  msg = {
+     *      type: 'error', // this can be 'error', 'success', 'warn' and 'info'
+     *      message: 'This is an error', // A string. Should fit in one line.
+     *      location: 'bottom', // A string where this notification should appear. can be 'bottom' or 'top'
+     *      dismissable: true // A Boolean. Whether the notification is dismissable or not.
+     *  };
+     * ```
+     */
     add: function add(notification) {
 
         var defaults = {
@@ -64,7 +66,7 @@ notifications = {
         };
 
         notificationCounter = notificationCounter + 1;
-        
+
         notification = _.assign(defaults, notification, {
             id: notificationCounter
             //status: 'persistent'
diff --git a/core/server/api/posts.js b/core/server/api/posts.js
index 4f9caaa9f7..0a79ce3bfb 100644
--- a/core/server/api/posts.js
+++ b/core/server/api/posts.js
@@ -1,23 +1,20 @@
-var when                   = require('when'),
-    _                      = require('lodash'),
-    dataProvider           = require('../models'),
-    canThis                = require('../permissions').canThis,
-    errors                 = require('../errors'),
+// # Posts API
+var when            = require('when'),
+    _               = require('lodash'),
+    dataProvider    = require('../models'),
+    canThis         = require('../permissions').canThis,
+    errors          = require('../errors'),
+    utils           = require('./utils'),
 
-    allowedIncludes        = ['created_by', 'updated_by', 'published_by', 'author', 'tags', 'fields'],
+    docName         = 'posts',
+    allowedIncludes = ['created_by', 'updated_by', 'published_by', 'author', 'tags', 'fields'],
     posts;
 
-function checkPostData(postData) {
-    if (_.isEmpty(postData) || _.isEmpty(postData.posts) || _.isEmpty(postData.posts[0])) {
-        return when.reject(new errors.BadRequestError('No root key (\'posts\') provided.'));
-    }
-    return when.resolve(postData);
-}
-
+// ## Helpers
 function prepareInclude(include) {
     var index;
 
-    include = _.intersection(include.split(","), allowedIncludes);
+    include = _.intersection(include.split(','), allowedIncludes);
     index = include.indexOf('author');
 
     if (index !== -1) {
@@ -27,16 +24,20 @@ function prepareInclude(include) {
     return include;
 }
 
-// ## Posts
+// ## API Methods
 posts = {
 
-    // #### Browse
-    // **takes:** filter / pagination parameters
+    /**
+     * ### Browse
+     * Find a paginated set of posts
+     * @param {{context, page, limit, status, staticPages, tag}} options (optional)
+     * @returns {Promise(Posts)} Posts Collection with Meta
+     */
     browse: function browse(options) {
         options = options || {};
-        
+
         // only published posts if no user is present
-        if (!this.user) {
+        if (!(options.context && options.context.user)) {
             options.status = 'published';
         }
 
@@ -44,28 +45,30 @@ posts = {
             options.include = prepareInclude(options.include);
         }
 
-        // **returns:** a promise for a page of posts in a json object
         return dataProvider.Post.findPage(options);
     },
 
-    // #### Read
-    // **takes:** an identifier (id or slug?)
+    /**
+     * ### Read
+     * Find a post, by ID or Slug
+     * @param {{id_or_slug (required), context, status, include, ...}} options
+     * @return {Promise(Post)} Post
+     */
     read: function read(options) {
-        var include;
-        options = options || {};
+        var attrs = ['id', 'slug', 'status'],
+            data = _.pick(options, attrs);
+        options = _.omit(options, attrs);
 
         // only published posts if no user is present
-        if (!this.user) {
-            options.status = 'published';
+        if (!(options.context && options.context.user)) {
+            data.status = 'published';
         }
 
         if (options.include) {
-            include = prepareInclude(options.include);
-            delete options.include;
+            options.include = prepareInclude(options.include);
         }
 
-        // **returns:** a promise for a single post in a json object
-        return dataProvider.Post.findOne(options, {include: include}).then(function (result) {
+        return dataProvider.Post.findOne(data, options).then(function (result) {
             if (result) {
                 return { posts: [ result.toJSON() ]};
             }
@@ -75,21 +78,21 @@ posts = {
         });
     },
 
-    // #### Edit
-    // **takes:** a json object with all the properties which should be updated
-    edit: function edit(postData) {
-        // **returns:** a promise for the resulting post in a json object
-        var self = this,
-            include;
-
-        return canThis(this).edit.post(postData.id).then(function () {
-            return checkPostData(postData).then(function (checkedPostData) {
-
-                if (postData.include) {
-                    include = prepareInclude(postData.include);
+    /**
+     * ### Edit
+     * Update properties of a post
+     * @param {Post} object Post or specific properties to update
+     * @param {{id (required), context, include,...}} options
+     * @return {Promise(Post)} Edited Post
+     */
+    edit: function edit(object, options) {
+        return canThis(options.context).edit.post(options.id).then(function () {
+            return utils.checkObject(object, docName).then(function (checkedPostData) {
+                if (options.include) {
+                    options.include = prepareInclude(options.include);
                 }
 
-                return dataProvider.Post.edit(checkedPostData.posts[0], {user: self.user, include: include});
+                return dataProvider.Post.edit(checkedPostData.posts[0], options);
             }).then(function (result) {
                 if (result) {
                     var post = result.toJSON();
@@ -108,20 +111,23 @@ posts = {
         });
     },
 
-    // #### Add
-    // **takes:** a json object representing a post,
-    add: function add(postData) {
-        var self = this,
-            include;
+    /**
+     * ### Add
+     * Create a new post along with any tags
+     * @param {Post} object
+     * @param {{context, include,...}} options
+     * @return {Promise(Post)} Created Post
+     */
+    add: function add(object, options) {
+        options = options || {};
 
-        // **returns:** a promise for the resulting post in a json object
-        return canThis(this).create.post().then(function () {
-            return checkPostData(postData).then(function (checkedPostData) {
-                if (postData.include) {
-                    include = prepareInclude(postData.include);
+        return canThis(options.context).create.post().then(function () {
+            return utils.checkObject(object, docName).then(function (checkedPostData) {
+                if (options.include) {
+                    options.include = prepareInclude(options.include);
                 }
 
-                return dataProvider.Post.add(checkedPostData.posts[0], {user: self.user, include: include});
+                return dataProvider.Post.add(checkedPostData.posts[0], options);
             }).then(function (result) {
                 var post = result.toJSON();
 
@@ -136,15 +142,18 @@ posts = {
         });
     },
 
-    // #### Destroy
-    // **takes:** an identifier (id or slug?)
-    destroy: function destroy(args) {
-        var self = this;
-        // **returns:** a promise for a json response with the id of the deleted post
-        return canThis(this).remove.post(args.id).then(function () {
-            // TODO: Would it be good to get rid of .call()?
-            return posts.read.call({user: self.user}, {id : args.id, status: 'all'}).then(function (result) {
-                return dataProvider.Post.destroy(args.id).then(function () {
+
+    /**
+     * ### Destroy
+     * Delete a post, cleans up tag relations, but not unused tags
+     * @param {{id (required), context,...}} options
+     * @return {Promise(Post)} Deleted Post
+     */
+    destroy: function destroy(options) {
+        return canThis(options.context).remove.post(options.id).then(function () {
+            var readOptions = _.extend({}, options, {status: 'all'});
+            return posts.read(readOptions).then(function (result) {
+                return dataProvider.Post.destroy(options).then(function () {
                     var deletedObj = result;
 
                     if (deletedObj.posts) {
@@ -161,17 +170,21 @@ posts = {
         });
     },
 
-    // #### Generate slug
-    // **takes:** a string to generate the slug from
-    generateSlug: function generateSlug(args) {
-
-        return canThis(this).slug.post().then(function () {
-            return dataProvider.Base.Model.generateSlug(dataProvider.Post, args.title, {status: 'all'}).then(function (slug) {
-                if (slug) {
-                    return slug;
-                }
-                return when.reject(new errors.InternalServerError('Could not generate slug'));
-            });
+    /**
+     * ## Generate Slug
+     * Create a unique slug for a given post title
+     * @param {{title (required), transacting}} options
+     * @returns {Promise(String)} Unique string
+     */
+    generateSlug: function generateSlug(options) {
+        return canThis(options.context).slug.post().then(function () {
+            return dataProvider.Base.Model.generateSlug(dataProvider.Post, options.title, {status: 'all'})
+                .then(function (slug) {
+                    if (slug) {
+                        return slug;
+                    }
+                    return when.reject(new errors.InternalServerError('Could not generate slug'));
+                });
         }, function () {
             return when.reject(new errors.NoPermissionError('You do not have permission.'));
         });
diff --git a/core/server/api/settings.js b/core/server/api/settings.js
index fe06daed23..1af7ce8526 100644
--- a/core/server/api/settings.js
+++ b/core/server/api/settings.js
@@ -1,33 +1,38 @@
+// # Settings API
 var _            = require('lodash'),
     dataProvider = require('../models'),
     when         = require('when'),
     config       = require('../config'),
     canThis      = require('../permissions').canThis,
     errors       = require('../errors'),
+    utils        = require('./utils'),
+
+    docName      = 'settings',
     settings,
-    settingsFilter,
+
     updateSettingsCache,
-    readSettingsResult,
+    settingsFilter,
     filterPaths,
+    readSettingsResult,
     settingsResult,
-    // Holds cached settings
+    canEditAllSettings,
+
+    /**
+     * ## Cache
+     * Holds cached settings
+     * @private
+     * @type {{}}
+     */
     settingsCache = {};
 
-// ### Helpers
 
-// Filters an object based on a given filter object
-settingsFilter = function (settings, filter) {
-    return _.object(_.filter(_.pairs(settings), function (setting) {
-        if (filter) {
-            return _.some(filter.split(','), function (f) {
-                return setting[1].type === f;
-            });
-        }
-        return true;
-    }));
-};
-
-// Maintain the internal cache of the settings object
+/**
+ * ### Update Settings Cache
+ * Maintain the internal cache of the settings object
+ * @public
+ * @param settings
+ * @returns {Settings}
+ */
 updateSettingsCache = function (settings) {
     settings = settings || {};
 
@@ -47,6 +52,78 @@ updateSettingsCache = function (settings) {
         });
 };
 
+// ## Helpers
+
+/**
+ * ### Settings Filter
+ * Filters an object based on a given filter object
+ * @private
+ * @param settings
+ * @param filter
+ * @returns {*}
+ */
+settingsFilter = function (settings, filter) {
+    return _.object(_.filter(_.pairs(settings), function (setting) {
+        if (filter) {
+            return _.some(filter.split(','), function (f) {
+                return setting[1].type === f;
+            });
+        }
+        return true;
+    }));
+};
+
+/**
+ * ### Filter Paths
+ * Normalizes paths read by require-tree so that the apps and themes modules can use them. Creates an empty
+ * array (res), and populates it with useful info about the read packages like name, whether they're active
+ * (comparison with the second argument), and if they have a package.json, that, otherwise false
+ * @private
+ * @param   {object}            paths       as returned by require-tree()
+ * @param   {array/string}      active      as read from the settings object
+ * @returns {Array}                         of objects with useful info about apps / themes
+ */
+filterPaths = function (paths, active) {
+    var pathKeys = Object.keys(paths),
+        res = [],
+        item;
+
+    // turn active into an array (so themes and apps can be checked the same)
+    if (!Array.isArray(active)) {
+        active = [active];
+    }
+
+    _.each(pathKeys, function (key) {
+        //do not include hidden files or _messages
+        if (key.indexOf('.') !== 0 &&
+                key !== '_messages' &&
+                key !== 'README.md'
+                ) {
+            item = {
+                name: key
+            };
+            if (paths[key].hasOwnProperty('package.json')) {
+                item.package = paths[key]['package.json'];
+            } else {
+                item.package = false;
+            }
+
+            if (_.indexOf(active, key) !== -1) {
+                item.active = true;
+            }
+            res.push(item);
+        }
+    });
+    return res;
+};
+
+
+/**
+ * ### Read Settings Result
+ * @private
+ * @param settingsModels
+ * @returns {Settings}
+ */
 readSettingsResult = function (settingsModels) {
     var settings = _.reduce(settingsModels, function (memo, member) {
             if (!memo.hasOwnProperty(member.attributes.key)) {
@@ -82,79 +159,78 @@ readSettingsResult = function (settingsModels) {
     return settings;
 };
 
-
-// Normalizes paths read by require-tree so that the apps and themes modules can use them.
-// Creates an empty array (res), and populates it with useful info about the read packages
-// like name, whether they're active (comparison with the second argument), and if they
-// have a package.json, that, otherwise false
-// @param  {object}           paths       as returned by require-tree()
-// @param  {array/string}     active      as read from the settings object
-// @return {array}                        of objects with useful info about apps / themes
-
-filterPaths = function (paths, active) {
-    var pathKeys = Object.keys(paths),
-        res = [],
-        item;
-
-    // turn active into an array (so themes and apps can be checked the same)
-    if (!Array.isArray(active)) {
-        active = [active];
-    }
-
-    _.each(pathKeys, function (key) {
-        //do not include hidden files or _messages
-        if (key.indexOf('.') !== 0 &&
-                key !== '_messages' &&
-                key !== 'README.md'
-                ) {
-            item = {
-                name: key
-            };
-            if (paths[key].hasOwnProperty('package.json')) {
-                item.package = paths[key]['package.json'];
-            } else {
-                item.package = false;
-            }
-
-            if (_.indexOf(active, key) !== -1) {
-                item.active = true;
-            }
-            res.push(item);
-        }
-    });
-    return res;
-};
-
+/**
+ * ### Settings Result
+ * @private
+ * @param settings
+ * @param type
+ * @returns {{settings: *}}
+ */
 settingsResult = function (settings, type) {
     var filteredSettings = _.values(settingsFilter(settings, type)),
         result = {
-            settings: filteredSettings
+            settings: filteredSettings,
+            meta: {}
         };
 
     if (type) {
-        result.meta = {
-            filters: {
-                type: type
-            }
+        result.meta.filters = {
+            type: type
         };
     }
 
     return result;
 };
 
+/**
+ * ### Can Edit All Settings
+ * Check that this edit request is allowed for all settings requested to be updated
+ * @private
+ * @param settingsInfo
+ * @returns {*}
+ */
+canEditAllSettings = function (settingsInfo, options) {
+    var checks = _.map(settingsInfo, function (settingInfo) {
+        var setting = settingsCache[settingInfo.key];
+
+        if (!setting) {
+            return when.reject(new errors.NotFoundError('Unable to find setting: ' + settingInfo.key));
+        }
+
+        if (setting.type === 'core' && !(options.context && options.context.internal)) {
+            return when.reject(
+                new errors.NoPermissionError('Attempted to access core setting from external request')
+            );
+        }
+
+        return canThis(options.context).edit.setting(settingInfo.key);
+    });
+
+    return when.all(checks);
+};
+
+// ## API Methods
 settings = {
-    // #### Browse
 
-    // **takes:** options object
+    /**
+     * ### Browse
+     * @param options
+     * @returns {*}
+     */
     browse: function browse(options) {
-        var self = this;
+        options = options || {};
 
-        // **returns:** a promise for a settings json object
-        return canThis(this).browse.setting().then(function () {
-            var result = settingsResult(settingsCache, options.type);
+        var result = settingsResult(settingsCache, options.type);
 
+        // If there is no context, return only blog settings
+        if (!options.context) {
+            return when(_.filter(result.settings, function (setting) { return setting.type === 'blog'; }));
+        }
+
+        // Otherwise return whatever this context is allowed to browse
+        return canThis(options.context).browse.setting().then(function () {
             // Omit core settings unless internal request
-            if (!self.internal) {
+            if (!options.context.internal) {
                 result.settings = _.filter(result.settings, function (setting) { return setting.type !== 'core'; });
             }
 
@@ -162,90 +238,88 @@ settings = {
         });
     },
 
-    // #### Read
-
-    // **takes:** either a json object containing a key, or a single key string
+    /**
+     * ### Read
+     * @param options
+     * @returns {*}
+     */
     read: function read(options) {
         if (_.isString(options)) {
             options = { key: options };
         }
 
-        var self = this;
+        var setting = settingsCache[options.key],
+            result = {};
 
-        return canThis(this).read.setting(options.key).then(function () {
-            var setting = settingsCache[options.key],
-                result = {};
+        if (!setting) {
+            return when.reject(new errors.NotFoundError('Unable to find setting: ' + options.key));
+        }
 
-            if (!setting) {
-                return when.reject(new errors.NotFoundError('Unable to find setting: ' + options.key));
-            }
+        result[options.key] = setting;
 
-            if (!self.internal && setting.type === 'core') {
-                return when.reject(new errors.NoPermissionError('Attempted to access core setting on external request'));
-            }
+        if (setting.type === 'core' && !(options.context && options.context.internal)) {
+            return when.reject(
+                new errors.NoPermissionError('Attempted to access core setting from external request')
+            );
+        }
 
-            result[options.key] = setting;
+        if (setting.type === 'blog') {
+            return when(settingsResult(result));
+        }
 
+        return canThis(options.context).read.setting(options.key).then(function () {
             return settingsResult(result);
+        }, function () {
+            return when.reject(new errors.NoPermissionError('You do not have permission to read settings.'));
         });
     },
 
-    // #### Edit
-
-     // **takes:** either a json object representing a collection of settings, or a key and value pair
-    edit: function edit(key, value) {
+    /**
+     * ### Edit
+     * Update properties of a post
+     * @param {{settings: }} object Setting or a single string name
+     * @param {{id (required), include,...}} options (optional) or a single string value
+     * @return {Promise(Setting)} Edited Setting
+     */
+    edit: function edit(object, options) {
+        options = options || {};
         var self = this,
-            type,
-            canEditAllSettings = function (settingsInfo) {
-                var checks = _.map(settingsInfo, function (settingInfo) {
-                    var setting = settingsCache[settingInfo.key];
+            type;
 
-                    if (!setting) {
-                        return when.reject(new errors.NotFoundError('Unable to find setting: ' + settingInfo.key));
-                    }
-
-                    if (!self.internal && setting.type === 'core') {
-                        return when.reject(new errors.NoPermissionError('Attempted to access core setting on external request'));
-                    }
-
-                    return canThis(self).edit.setting(settingInfo.key);
-                });
-
-                return when.all(checks);
-            };
-
-        if (!_.isString(value)) {
-            value = JSON.stringify(value);
-        }
-
-        // Allow shorthand syntax
-        if (_.isString(key)) {
-            key = { settings: [{ key: key, value: value }]};
+        // Allow shorthand syntax where a single key and value are passed to edit instead of object and options
+        if (_.isString(object)) {
+            object = { settings: [{ key: object, value: options }]};
         }
 
         //clean data
-        type = _.find(key.settings, function (setting) { return setting.key === 'type'; });
+        _.each(object.settings, function (setting) {
+            if (!_.isString(setting.value)) {
+                setting.value = JSON.stringify(setting.value);
+            }
+        });
+
+        type = _.find(object.settings, function (setting) { return setting.key === 'type'; });
         if (_.isObject(type)) {
             type = type.value;
         }
 
-        key = _.reject(key.settings, function (setting) {
+        object.settings = _.reject(object.settings, function (setting) {
             return setting.key === 'type' || setting.key === 'availableThemes' || setting.key === 'availableApps';
         });
 
-        return canEditAllSettings(key).then(function () {
-            return dataProvider.Settings.edit(key, {user: self.user});
-        }).then(function (result) {
-            var readResult = readSettingsResult(result);
+        return canEditAllSettings(object.settings, options).then(function () {
+            return utils.checkObject(object, docName).then(function (checkedData) {
+                options.user = self.user;
+                return dataProvider.Settings.edit(checkedData.settings, options);
+            }).then(function (result) {
+                var readResult = readSettingsResult(result);
 
-            return updateSettingsCache(readResult).then(function () {
-                return config.theme.update(settings, config().url);
-            }).then(function () {
-                return settingsResult(readResult, type);
+                return updateSettingsCache(readResult).then(function () {
+                    return config.theme.update(settings, config().url);
+                }).then(function () {
+                    return settingsResult(readResult, type);
+                });
             });
-        }).catch(function (error) {
-            // Pass along API error
-            return when.reject(error);
         });
     }
 };
diff --git a/core/server/api/tags.js b/core/server/api/tags.js
index 5cd7c4d502..f56b205a51 100644
--- a/core/server/api/tags.js
+++ b/core/server/api/tags.js
@@ -3,11 +3,8 @@ var dataProvider = require('../models'),
 
 
 tags = {
-    // #### Browse
-    // **takes:** Nothing yet
-    browse: function browse() {
-        // **returns:** a promise for all tags which have previously been used in a json object
-        return dataProvider.Tag.findAll().then(function (result) {
+    browse: function browse(options) {
+        return dataProvider.Tag.findAll(options).then(function (result) {
             return { tags: result.toJSON() };
         });
     }
diff --git a/core/server/api/themes.js b/core/server/api/themes.js
index f4f8e91ab7..6df7c629f4 100644
--- a/core/server/api/themes.js
+++ b/core/server/api/themes.js
@@ -10,11 +10,12 @@ var when               = require('when'),
 // ## Themes
 themes = {
 
-    browse: function browse() {
-        // **returns:** a promise for a collection of themes in a json object
-        return canThis(this).browse.theme().then(function () {
+    browse: function browse(options) {
+        options = options || {};
+
+        return canThis(options.context).browse.theme().then(function () {
             return when.all([
-                settings.read.call({ internal: true }, 'activeTheme'),
+                settings.read({key: 'activeTheme', context: {internal: true}}),
                 config().paths.availableThemes
             ]).then(function (result) {
                 var activeTheme = result[0].settings[0].value,
@@ -49,19 +50,18 @@ themes = {
         });
     },
 
-    edit: function edit(themeData) {
-        var self = this,
-            themeName;
+    edit: function edit(object, options) {
+        var themeName;
 
         // Check whether the request is properly formatted.
-        if (!_.isArray(themeData.themes)) {
+        if (!_.isArray(object.themes)) {
             return when.reject({type: 'BadRequest', message: 'Invalid request.'});
         }
 
-        themeName = themeData.themes[0].uuid;
+        themeName = object.themes[0].uuid;
 
-        return canThis(this).edit.theme().then(function () {
-            return themes.browse.call(self).then(function (availableThemes) {
+        return canThis(options.context).edit.theme().then(function () {
+            return themes.browse(options).then(function (availableThemes) {
                 var theme;
 
                 // Check if the theme exists
@@ -73,8 +73,10 @@ themes = {
                     return when.reject(new errors.BadRequestError('Theme does not exist.'));
                 }
 
-                // Activate the theme 
-                return settings.edit.call({ internal: true }, 'activeTheme', themeName).then(function () {
+                // Activate the theme
+                return settings.edit(
+                    {settings: [{ key: 'activeTheme', value: themeName }]}, {context: {internal: true }}
+                ).then(function () {
                     theme.active = true;
                     return { themes: [theme]};
                 });
diff --git a/core/server/api/users.js b/core/server/api/users.js
index f4585d08d1..79dcfd622f 100644
--- a/core/server/api/users.js
+++ b/core/server/api/users.js
@@ -1,27 +1,26 @@
-var when               = require('when'),
-    _                  = require('lodash'),
-    dataProvider       = require('../models'),
-    settings           = require('./settings'),
-    canThis            = require('../permissions').canThis,
-    errors             = require('../errors'),
-    ONE_DAY            = 86400000,
+var when            = require('when'),
+    _               = require('lodash'),
+    dataProvider    = require('../models'),
+    settings        = require('./settings'),
+    canThis         = require('../permissions').canThis,
+    errors          = require('../errors'),
+    utils           = require('./utils'),
+
+    docName         = 'users',
+    ONE_DAY         = 86400000,
     users;
 
-
-function checkUserData(userData) {
-    if (_.isEmpty(userData) || _.isEmpty(userData.users) || _.isEmpty(userData.users[0])) {
-        return when.reject(new errors.BadRequestError('No root key (\'users\') provided.'));
-    }
-    return when.resolve(userData);
-}
-// ## Users
 users = {
 
-    // #### Browse
-    // **takes:** options object
+    /**
+     * ## Browse
+     * Fetch all users
+     * @param {object} options (optional)
+     * @returns {Promise(Users)} Users Collection
+     */
     browse: function browse(options) {
-        // **returns:** a promise for a collection of users in a json object
-        return canThis(this).browse.user().then(function () {
+        options = options || {};
+        return canThis(options.context).browse.user().then(function () {
             return dataProvider.User.findAll(options).then(function (result) {
                 return { users: result.toJSON() };
             });
@@ -30,15 +29,17 @@ users = {
         });
     },
 
-    // #### Read
-    // **takes:** an identifier (id or slug?)
-    read: function read(args) {
-        // **returns:** a promise for a single user in a json object
-        if (args.id === 'me') {
-            args = {id: this.user};
+    read: function read(options) {
+        var attrs = ['id'],
+            data = _.pick(options, attrs);
+
+        options = _.omit(options, attrs);
+
+        if (data.id === 'me' && options.context && options.context.user) {
+            data.id = options.context.user;
         }
 
-        return dataProvider.User.findOne(args).then(function (result) {
+        return dataProvider.User.findOne(data, options).then(function (result) {
             if (result) {
                 return { users: [result.toJSON()] };
             }
@@ -47,14 +48,15 @@ users = {
         });
     },
 
-    // #### Edit
-    // **takes:** a json object representing a user
-    edit: function edit(userData) {
-        // **returns:** a promise for the resulting user in a json object
-        var self = this;
-        return canThis(this).edit.user(userData.users[0].id).then(function () {
-            return checkUserData(userData).then(function (checkedUserData) {
-                return dataProvider.User.edit(checkedUserData.users[0], {user: self.user});
+    edit: function edit(object, options) {
+        if (options.id === 'me' && options.context && options.context.user) {
+            options.id = options.context.user;
+        }
+
+        return canThis(options.context).edit.user(options.id).then(function () {
+            return utils.checkObject(object, docName).then(function (checkedUserData) {
+
+                return dataProvider.User.edit(checkedUserData.users[0], options);
             }).then(function (result) {
                 if (result) {
                     return { users: [result.toJSON()]};
@@ -66,19 +68,17 @@ users = {
         });
     },
 
-    // #### Add
-    // **takes:** a json object representing a user
-    add: function add(userData) {
-        // **returns:** a promise for the resulting user in a json object
-        var self = this;
-        return canThis(this).add.user().then(function () {
-            return checkUserData(userData).then(function (checkedUserData) {
-                // if the user is created by users.register(), use id: 1
-                // as the creator for now
-                if (self.internal) {
-                    self.user = 1;
+    add: function add(object, options) {
+        options = options || {};
+
+        return canThis(options.context).add.user().then(function () {
+            return utils.checkObject(object, docName).then(function (checkedUserData) {
+                // if the user is created by users.register(), use id: 1 as the creator for now
+                if (options.context.internal) {
+                    options.context.user = 1;
                 }
-                return dataProvider.User.add(checkedUserData.users[0], {user: self.user});
+
+                return dataProvider.User.add(checkedUserData.users[0], options);
             }).then(function (result) {
                 if (result) {
                     return { users: [result.toJSON()]};
@@ -89,47 +89,37 @@ users = {
         });
     },
 
-    // #### Register
-    // **takes:** a json object representing a user
-    register: function register(userData) {
-        // TODO: if we want to prevent users from being created with the signup form
-        // this is the right place to do it
-        return users.add.call({internal: true}, userData);
+    register: function register(object) {
+        // TODO: if we want to prevent users from being created with the signup form this is the right place to do it
+        return users.add(object, {context: {internal: true}});
     },
 
-    // #### Check
-    // Checks a password matches the given email address
 
-    // **takes:** a json object representing a user
-    check: function check(userData) {
-        // **returns:** on success, returns a promise for the resulting user in a json object
-        return dataProvider.User.check(userData);
+    check: function check(object) {
+        return dataProvider.User.check(object);
     },
 
-    // #### Change Password
-    // **takes:** a json object representing a user
-    changePassword: function changePassword(userData) {
-        // **returns:** on success, returns a promise for the resulting user in a json object
-        return dataProvider.User.changePassword(userData);
+    changePassword: function changePassword(object) {
+        return dataProvider.User.changePassword(object);
     },
 
     generateResetToken: function generateResetToken(email) {
         var expires = Date.now() + ONE_DAY;
-        return settings.read.call({ internal: true }, 'dbHash').then(function (response) {
+        return settings.read({context: {internal: true}, key: 'dbHash'}).then(function (response) {
             var dbHash = response.settings[0].value;
             return dataProvider.User.generateResetToken(email, expires, dbHash);
         });
     },
 
     validateToken: function validateToken(token) {
-        return settings.read.call({ internal: true }, 'dbHash').then(function (response) {
+        return settings.read({context: {internal: true}, key: 'dbHash'}).then(function (response) {
             var dbHash = response.settings[0].value;
             return dataProvider.User.validateToken(token, dbHash);
         });
     },
 
     resetPassword: function resetPassword(token, newPassword, ne2Password) {
-        return settings.read.call({ internal: true }, 'dbHash').then(function (response) {
+        return settings.read({context: {internal: true}, key: 'dbHash'}).then(function (response) {
             var dbHash = response.settings[0].value;
             return dataProvider.User.resetPassword(token, newPassword, ne2Password, dbHash);
         });
diff --git a/core/server/api/utils.js b/core/server/api/utils.js
new file mode 100644
index 0000000000..a8fc211b78
--- /dev/null
+++ b/core/server/api/utils.js
@@ -0,0 +1,14 @@
+var when    = require('when'),
+    _       = require('lodash'),
+    utils;
+
+utils = {
+    checkObject: function (object, docName) {
+        if (_.isEmpty(object) || _.isEmpty(object[docName]) || _.isEmpty(object[docName][0])) {
+            return when.reject({type: 'BadRequest', message: 'No root key (\'' + docName + '\') provided.'});
+        }
+        return when.resolve(object);
+    }
+};
+
+module.exports = utils;
\ No newline at end of file
diff --git a/core/server/apps/index.js b/core/server/apps/index.js
index 82013eee9b..9ed75d47be 100644
--- a/core/server/apps/index.js
+++ b/core/server/apps/index.js
@@ -9,7 +9,7 @@ var _           = require('lodash'),
 
 
 function getInstalledApps() {
-    return api.settings.read.call({ internal: true }, 'installedApps').then(function (response) {
+    return api.settings.read({context: {internal: true}, key: 'installedApps'}).then(function (response) {
         var installed = response.settings[0];
 
         installed.value = installed.value || '[]';
@@ -28,7 +28,7 @@ function saveInstalledApps(installedApps) {
     return getInstalledApps().then(function (currentInstalledApps) {
         var updatedAppsInstalled = _.uniq(installedApps.concat(currentInstalledApps));
 
-        return api.settings.edit.call({internal: true}, 'installedApps', updatedAppsInstalled);
+        return api.settings.edit({context: {internal: true}, key: 'installedApps'}, updatedAppsInstalled);
     });
 }
 
@@ -38,7 +38,7 @@ module.exports = {
 
         try {
             // We have to parse the value because it's a string
-            api.settings.read.call({ internal: true }, 'activeApps').then(function (response) {
+            api.settings.read({context: {internal: true}, key: 'activeApps'}).then(function (response) {
                 var aApps = response.settings[0];
 
                 appsToLoad = JSON.parse(aApps.value) || [];
diff --git a/core/server/apps/proxy.js b/core/server/apps/proxy.js
index 97b1424750..1d4d397be0 100644
--- a/core/server/apps/proxy.js
+++ b/core/server/apps/proxy.js
@@ -39,7 +39,13 @@ var generateProxyFunctions = function (name, permissions) {
 
             return _.reduce(apiMethods, function (memo, apiMethod, methodName) {
                 memo[methodName] = function () {
-                    return apiMethod.apply(_.clone(appContext), _.toArray(arguments));
+                    var args = _.toArray(arguments),
+                        options = args[args.length - 1];
+
+                    if (_.isObject(options)) {
+                        options.context = _.clone(appContext);
+                    }
+                    return apiMethod.apply({}, args);
                 };
 
                 return memo;
@@ -57,10 +63,18 @@ var generateProxyFunctions = function (name, permissions) {
             registerAsync: checkRegisterPermissions('helpers', helpers.registerAsyncThemeHelper.bind(helpers))
         },
         api: {
-            posts: passThruAppContextToApi('posts', _.pick(api.posts, 'browse', 'read', 'edit', 'add', 'destroy')),
-            tags: passThruAppContextToApi('tags', _.pick(api.tags, 'browse')),
-            notifications: passThruAppContextToApi('notifications', _.pick(api.notifications, 'browse', 'add', 'destroy')),
-            settings: passThruAppContextToApi('settings', _.pick(api.settings, 'browse', 'read', 'edit'))
+            posts: passThruAppContextToApi('posts',
+                _.pick(api.posts, 'browse', 'read', 'edit', 'add', 'destroy')
+            ),
+            tags: passThruAppContextToApi('tags',
+                _.pick(api.tags, 'browse')
+            ),
+            notifications: passThruAppContextToApi('notifications',
+                _.pick(api.notifications, 'browse', 'add', 'destroy')
+            ),
+            settings: passThruAppContextToApi('settings',
+                _.pick(api.settings, 'browse', 'read', 'edit')
+            )
         }
     };
 
diff --git a/core/server/config/theme.js b/core/server/config/theme.js
index d64a9e89db..23686ac396 100644
--- a/core/server/config/theme.js
+++ b/core/server/config/theme.js
@@ -19,10 +19,10 @@ function theme() {
 function update(settings, configUrl) {
     // TODO: Pass the context into this method instead of hard coding internal: true?
     return when.all([
-        settings.read.call({ internal: true }, 'title'),
-        settings.read.call({ internal: true }, 'description'),
-        settings.read.call({ internal: true }, 'logo'),
-        settings.read.call({ internal: true }, 'cover')
+        settings.read('title'),
+        settings.read('description'),
+        settings.read('logo'),
+        settings.read('cover')
     ]).then(function (globals) {
         // normalise the URL by removing any trailing slash
         themeConfig.url = configUrl.replace(/\/$/, '');
diff --git a/core/server/config/url.js b/core/server/config/url.js
index 252f36bf7d..293b3b1ea0 100644
--- a/core/server/config/url.js
+++ b/core/server/config/url.js
@@ -148,9 +148,9 @@ function urlFor(context, data, absolute) {
 // - post - a json object representing a post
 // - absolute (optional, default:false) - boolean whether or not the url should be absolute
 function urlForPost(settings, post, absolute) {
-    return settings.read.call({ internal: true }, 'permalinks').then(function (response) {
+    return settings.read('permalinks').then(function (response) {
         var permalinks = response.settings[0];
-        
+
         return urlFor('post', {post: post, permalinks: permalinks}, absolute);
     });
 }
diff --git a/core/server/controllers/admin.js b/core/server/controllers/admin.js
index d8a1fb13f4..46992b6d4e 100644
--- a/core/server/controllers/admin.js
+++ b/core/server/controllers/admin.js
@@ -129,7 +129,7 @@ adminControllers = {
         },
         // frontend route for downloading a file
         exportContent: function (req, res) {
-            api.db.exportContent.call({user: req.session.user}).then(function (exportData) {
+            api.db.exportContent({context: {user: req.session.user}}).then(function (exportData) {
                 // send a file to the client
                 res.set('Content-Disposition', 'attachment; filename="GhostData.json"');
                 res.json(exportData);
@@ -260,10 +260,9 @@ adminControllers = {
                 password: password
             }];
 
-        api.users.register({users: users}).then(function (apiResp) {
-            var user = apiResp.users[0];
-
-            api.settings.edit.call({user: 1}, 'email', email).then(function () {
+        api.users.register({users: users}).then(function (response) {
+            var user = response.users[0];
+            api.settings.edit({settings: [{key: 'email', value: email}]}, {context: {user: 1}}).then(function () {
                 var message = {
                         to: email,
                         subject: 'Your New Ghost Blog',
diff --git a/core/server/controllers/frontend.js b/core/server/controllers/frontend.js
index 11a288b155..fddbdcd3ad 100644
--- a/core/server/controllers/frontend.js
+++ b/core/server/controllers/frontend.js
@@ -22,7 +22,7 @@ var moment      = require('moment'),
     staticPostPermalink = new Route(null, '/:slug/:edit?');
 
 function getPostPage(options) {
-    return api.settings.read.call({ internal: true }, 'postsPerPage').then(function (response) {
+    return api.settings.read('postsPerPage').then(function (response) {
         var postPP = response.settings[0],
             postsPerPage = parseInt(postPP.value, 10);
 
@@ -123,7 +123,7 @@ frontendControllers = {
 
             // Render the page of posts
             filters.doFilter('prePostsRender', page.posts).then(function (posts) {
-                api.settings.read.call({ internal: true }, 'activeTheme').then(function (response) {
+                api.settings.read({key: 'activeTheme', context: {internal: true}}).then(function (response) {
                     var activeTheme = response.settings[0],
                         paths = config().paths.availableThemes[activeTheme.value],
                         view = paths.hasOwnProperty('tag.hbs') ? 'tag' : 'index',
@@ -148,7 +148,7 @@ frontendControllers = {
             editFormat,
             usingStaticPermalink = false;
 
-        api.settings.read.call({ internal: true }, 'permalinks').then(function (response) {
+        api.settings.read('permalinks').then(function (response) {
             var permalink = response.settings[0],
                 postLookup;
 
@@ -203,7 +203,7 @@ frontendControllers = {
                 setReqCtx(req, post);
 
                 filters.doFilter('prePostsRender', post).then(function (post) {
-                    api.settings.read.call({ internal: true }, 'activeTheme').then(function (response) {
+                    api.settings.read({key: 'activeTheme', context: {internal: true}}).then(function (response) {
                         var activeTheme = response.settings[0],
                             paths = config().paths.availableThemes[activeTheme.value],
                             view = template.getThemeViewForPost(paths, post);
@@ -282,9 +282,9 @@ frontendControllers = {
         }
 
         return when.settle([
-            api.settings.read.call({ internal: true }, 'title'),
-            api.settings.read.call({ internal: true }, 'description'),
-            api.settings.read.call({ internal: true }, 'permalinks')
+            api.settings.read('title'),
+            api.settings.read('description'),
+            api.settings.read('permalinks')
         ]).then(function (result) {
 
             var options = {};
diff --git a/core/server/data/import/000.js b/core/server/data/import/000.js
index 66110fb990..6ffcea0ef7 100644
--- a/core/server/data/import/000.js
+++ b/core/server/data/import/000.js
@@ -112,7 +112,7 @@ function importUsers(ops, tableData, transaction) {
     // don't override the users credentials
     tableData = stripProperties(['id', 'email', 'password'], tableData);
     tableData[0].id = 1;
-    ops.push(models.User.edit(tableData[0], {user: 1, transacting: transaction})
+    ops.push(models.User.edit(tableData[0], {id: 1, user: 1, transacting: transaction})
         // add pass-through error handling so that bluebird doesn't think we've dropped it
         .otherwise(function (error) { return when.reject(error); }));
 }
@@ -157,9 +157,9 @@ function importApps(ops, tableData, transaction) {
 //     var appsData = tableData.apps,
 //         appSettingsData = tableData.app_settings,
 //         appName;
-// 
+//
 //     appSettingsData = stripProperties(['id'], appSettingsData);
-// 
+//
 //     _.each(appSettingsData, function (appSetting) {
 //         // Find app to attach settings to
 //         appName = _.find(appsData, function (app) {
diff --git a/core/server/helpers/index.js b/core/server/helpers/index.js
index a96018e5ef..b8aedd6580 100644
--- a/core/server/helpers/index.js
+++ b/core/server/helpers/index.js
@@ -389,7 +389,7 @@ coreHelpers.body_class = function (options) {
         classes.push('page');
     }
 
-    return api.settings.read.call({ internal: true }, 'activeTheme').then(function (response) {
+    return api.settings.read({context: {internal: true}, key: 'activeTheme'}).then(function (response) {
         var activeTheme = response.settings[0],
             paths = config().paths.availableThemes[activeTheme.value],
             view;
@@ -532,8 +532,8 @@ coreHelpers.meta_description = function (options) {
 coreHelpers.e = function (key, defaultString, options) {
     var output;
     when.all([
-        api.settings.read.call({ internal: true }, 'defaultLang'),
-        api.settings.read.call({ internal: true }, 'forceI18n')
+        api.settings.read('defaultLang'),
+        api.settings.read('forceI18n')
     ]).then(function (values) {
         if (values[0].settings.value === 'en' &&
                 _.isEmpty(options.hash) &&
diff --git a/core/server/index.js b/core/server/index.js
index 3cd9c7650d..8acbd25e7f 100644
--- a/core/server/index.js
+++ b/core/server/index.js
@@ -52,7 +52,7 @@ function doFirstRun() {
 }
 
 function initDbHashAndFirstRun() {
-    return api.settings.read.call({ internal: true }, 'dbHash').then(function (response) {
+    return api.settings.read({key: 'dbHash', context: {internal: true}}).then(function (response) {
         var hash = response.settings[0].value,
             initHash;
 
@@ -60,10 +60,11 @@ function initDbHashAndFirstRun() {
 
         if (dbHash === null) {
             initHash = uuid.v4();
-            return api.settings.edit.call({ internal: true }, 'dbHash', initHash).then(function (response) {
-                dbHash = response.settings[0].value;
-                return dbHash;
-            }).then(doFirstRun);
+            return api.settings.edit({settings: [{key: 'dbHash', value: initHash}]}, {context: {internal: true}})
+                .then(function (response) {
+                    dbHash = response.settings[0].value;
+                    return dbHash;
+                }).then(doFirstRun);
         }
 
         return dbHash;
diff --git a/core/server/mail.js b/core/server/mail.js
index fccd4e4e4f..e07447a4a6 100644
--- a/core/server/mail.js
+++ b/core/server/mail.js
@@ -106,7 +106,7 @@ GhostMailer.prototype.send = function (payload) {
         return when.reject(new Error('Email Error: Incomplete message data.'));
     }
 
-    return api.settings.read.call({ internal: true }, 'email').then(function (response) {
+    return api.settings.read('email').then(function (response) {
 
         var email = response.settings[0],
             to = message.to || email.value;
diff --git a/core/server/middleware/index.js b/core/server/middleware/index.js
index c0eb3787ad..ada3465597 100644
--- a/core/server/middleware/index.js
+++ b/core/server/middleware/index.js
@@ -39,7 +39,7 @@ function ghostLocals(req, res, next) {
     if (res.isAdmin) {
         res.locals.csrfToken = req.csrfToken();
         when.all([
-            api.users.read.call({user: req.session.user}, {id: req.session.user}),
+            api.users.read({id: req.session.user}, {context: {user: req.session.user}}),
             api.notifications.browse()
         ]).then(function (values) {
             var currentUser = values[0].users[0],
@@ -150,9 +150,9 @@ function manageAdminAndTheme(req, res, next) {
         expressServer.enable(expressServer.get('activeTheme'));
         expressServer.disable('admin');
     }
-    api.settings.read.call({ internal: true }, 'activeTheme').then(function (response) {
+    api.settings.read({context: {internal: true}, key: 'activeTheme'}).then(function (response) {
         var activeTheme = response.settings[0];
-        
+
         // Check if the theme changed
         if (activeTheme.value !== expressServer.get('activeTheme')) {
             // Change theme
diff --git a/core/server/middleware/middleware.js b/core/server/middleware/middleware.js
index 648db3cf13..37bc060175 100644
--- a/core/server/middleware/middleware.js
+++ b/core/server/middleware/middleware.js
@@ -170,7 +170,7 @@ var middleware = {
 
     // to allow unit testing
     forwardToExpressStatic: function (req, res, next) {
-        api.settings.read.call({ internal: true }, 'activeTheme').then(function (response) {
+        api.settings.read({context: {internal: true}, key: 'activeTheme'}).then(function (response) {
             var activeTheme = response.settings[0];
             // For some reason send divides the max age number by 1000
             express['static'](path.join(config().paths.themePath, activeTheme.value), {maxAge: ONE_HOUR_MS})(req, res, next);
diff --git a/core/server/models/base.js b/core/server/models/base.js
index 942142525b..7b5fbc3ed8 100644
--- a/core/server/models/base.js
+++ b/core/server/models/base.js
@@ -63,18 +63,20 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
     },
 
     creating: function (newObj, attr, options) {
+        var user = options.context && options.context.user ? options.context.user : 1;
         if (!this.get('created_by')) {
-            this.set('created_by', options.user);
+            this.set('created_by', user);
         }
     },
 
     saving: function (newObj, attr, options) {
+        var user = options.context && options.context.user ? options.context.user : 1;
         // Remove any properties which don't belong on the model
         this.attributes = this.pick(this.permittedAttributes());
         // Store the previous attributes so we can tell what was updated later
         this._updatedAttributes = newObj.previousAttributes();
 
-        this.set('updated_by', options.user);
+        this.set('updated_by', user);
     },
 
     // Base prototype properties will go here
@@ -153,8 +155,7 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
     }
 
 }, {
-
-     // ## Model Data Functions
+    // ## Data Utility Functions
 
     /**
      * Returns an array of keys permitted in every method's `options` hash.
@@ -191,6 +192,8 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
         return filteredOptions;
     },
 
+     // ## Model Data Functions
+
     /**
      * ### Find All
      * Naive find all fetches all the data for a particular model
@@ -219,6 +222,7 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
     findOne: function (data, options) {
         data = this.filterData(data);
         options = this.filterOptions(options, 'findOne');
+        // We pass include to forge so that toJSON has access
         return this.forge(data, {include: options.include}).fetch(options);
     },
 
@@ -230,9 +234,11 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
      * @return {Promise(ghostBookshelf.Model)} Edited Model
      */
     edit: function (data, options) {
+        var id = options.id;
         data = this.filterData(data);
         options = this.filterOptions(options, 'edit');
-        return this.forge({id: data.id}).fetch(options).then(function (object) {
+
+        return this.forge({id: id}).fetch(options).then(function (object) {
             if (object) {
                 return object.save(data, options);
             }
@@ -250,11 +256,8 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
         data = this.filterData(data);
         options = this.filterOptions(options, 'add');
         var instance = this.forge(data);
-        // We allow you to disable timestamps
-        // when importing posts so that
-        // the new posts `updated_at` value
-        // is the same as the import json blob.
-        // More details refer to https://github.com/TryGhost/Ghost/issues/1696
+        // We allow you to disable timestamps when importing posts so that the new posts `updated_at` value is the same
+        // as the import json blob. More details refer to https://github.com/TryGhost/Ghost/issues/1696
         if (options.importing) {
             instance.hasTimestamps = false;
         }
@@ -264,13 +267,13 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
     /**
      * ### Destroy
      * Naive destroy
-     * @param {Object} data
      * @param {Object} options (optional)
      * @return {Promise(ghostBookshelf.Model)} Empty Model
      */
-    destroy: function (data, options) {
+    destroy: function (options) {
+        var id = options.id;
         options = this.filterOptions(options, 'destroy');
-        return this.forge({id: data}).destroy(options);
+        return this.forge({id: id}).destroy(options);
     },
 
     /**
diff --git a/core/server/models/index.js b/core/server/models/index.js
index 64ef0f7dd2..2d4fd774a4 100644
--- a/core/server/models/index.js
+++ b/core/server/models/index.js
@@ -25,12 +25,12 @@ module.exports = {
 
         return self.Post.findAll().then(function (posts) {
             return when.all(_.map(posts.toJSON(), function (post) {
-                return self.Post.destroy(post.id);
+                return self.Post.destroy({id: post.id});
             }));
         }).then(function () {
             return self.Tag.findAll().then(function (tags) {
                 return when.all(_.map(tags.toJSON(), function (tag) {
-                    return self.Tag.destroy(tag.id);
+                    return self.Tag.destroy({id: tag.id});
                 }));
             });
         });
diff --git a/core/server/models/post.js b/core/server/models/post.js
index 58ffe7648b..d4ab8c0a5c 100644
--- a/core/server/models/post.js
+++ b/core/server/models/post.js
@@ -44,7 +44,8 @@ Post = ghostBookshelf.Model.extend({
         /*jshint unused:false*/
         var self = this,
             tagsToCheck,
-            i;
+            i,
+            user = options.context && options.context.user ? options.context.user : 1;
 
         options = options || {};
         // keep tags for 'saved' event and deduplicate upper/lowercase tags
@@ -75,7 +76,7 @@ Post = ghostBookshelf.Model.extend({
                 this.set('published_at', new Date());
             }
             // This will need to go elsewhere in the API layer.
-            this.set('published_by', options.user);
+            this.set('published_by', user);
         }
 
         if (this.hasChanged('slug') || !this.get('slug')) {
@@ -93,9 +94,11 @@ Post = ghostBookshelf.Model.extend({
         /*jshint unused:false*/
         options = options || {};
 
+        var user = options.context && options.context.user ? options.context.user : 1;
+
         // set any dynamic default properties
         if (!this.get('author_id')) {
-            this.set('author_id', options.user);
+            this.set('author_id', user);
         }
 
         ghostBookshelf.Model.prototype.creating.call(this, newPage, attr, options);
@@ -105,7 +108,7 @@ Post = ghostBookshelf.Model.extend({
      * ### updateTags
      * Update tags that are attached to a post.  Create any tags that don't already exist.
      * @param {Object} newPost
-     * @param {Object} attr 
+     * @param {Object} attr
      * @param {Object} options
      * @return {Promise(ghostBookshelf.Models.Post)} Updated Post model
      */
@@ -243,8 +246,14 @@ Post = ghostBookshelf.Model.extend({
         return filteredData;
     },
 
-    // #### findAll
-    // Extends base model findAll to eager-fetch author and user relationships.
+    // ## Model Data Functions
+
+    /**
+     * ### Find All
+     *
+     * @param options
+     * @returns {*}
+     */
     findAll:  function (options) {
         options = options || {};
         options.withRelated = _.union([ 'tags', 'fields' ], options.include);
@@ -252,24 +261,24 @@ Post = ghostBookshelf.Model.extend({
     },
 
 
-     // #### findPage
-     // Find results by page - returns an object containing the
-     // information about the request (page, limit), along with the
-     // info needed for pagination (pages, total).
-
-     // **response:**
-
-     //     {
-     //         posts: [
-     //         {...}, {...}, {...}
-     //     ],
-     //     page: __,
-     //     limit: __,
-     //     pages: __,
-     //     total: __
-     //     }
-
-    /*
+    /**
+     * #### findPage
+     * Find results by page - returns an object containing the
+     * information about the request (page, limit), along with the
+     * info needed for pagination (pages, total).
+     *
+     * **response:**
+     *
+     *     {
+     *         posts: [
+     *         {...}, {...}, {...}
+     *     ],
+     *     page: __,
+     *     limit: __,
+     *     pages: __,
+     *     total: __
+     *     }
+     *
      * @params {Object} options
      */
     findPage: function (options) {
@@ -377,22 +386,23 @@ Post = ghostBookshelf.Model.extend({
                     meta = {},
                     data = {};
 
-                pagination['page'] = parseInt(options.page, 10);
-                pagination['limit'] = options.limit;
-                pagination['pages'] = calcPages === 0 ? 1 : calcPages;
-                pagination['total'] = totalPosts;
-                pagination['next'] = null;
-                pagination['prev'] = null;
+                pagination.page = parseInt(options.page, 10);
+                pagination.limit = options.limit;
+                pagination.pages = calcPages === 0 ? 1 : calcPages;
+                pagination.total = totalPosts;
+                pagination.next = null;
+                pagination.prev = null;
 
+                // Pass include to each model so that toJSON works correctly
                 if (options.include) {
                     _.each(postCollection.models, function (item) {
                         item.include = options.include;
                     });
                 }
 
-                data['posts'] = postCollection.toJSON();
-                data['meta'] = meta;
-                meta['pagination'] = pagination;
+                data.posts = postCollection.toJSON();
+                data.meta = meta;
+                meta.pagination = pagination;
 
                 if (pagination.pages > 1) {
                     if (pagination.page === 1) {
@@ -406,9 +416,9 @@ Post = ghostBookshelf.Model.extend({
                 }
 
                 if (tagInstance) {
-                    meta['filters'] = {};
+                    meta.filters = {};
                     if (!tagInstance.isNew()) {
-                        meta.filters['tags'] = [tagInstance.toJSON()];
+                        meta.filters.tags = [tagInstance.toJSON()];
                     }
                 }
 
@@ -417,59 +427,76 @@ Post = ghostBookshelf.Model.extend({
             .catch(errors.logAndThrowError);
     },
 
-    //    #### findOne
-    //    Extends base model read to eager-fetch author and user relationships.
-    findOne: function (args, options) {
+    /**
+     * ### Find One
+     * @extends ghostBookshelf.Model.findOne to handle post status
+     * **See:** [ghostBookshelf.Model.findOne](base.js.html#Find%20One)
+     */
+    findOne: function (data, options) {
         options = options || {};
 
-        args = _.extend({
+        data = _.extend({
             status: 'published'
-        }, args || {});
+        }, data || {});
 
-        if (args.status === 'all') {
-            delete args.status;
+        if (data.status === 'all') {
+            delete data.status;
         }
 
         // Add related objects
         options.withRelated = _.union([ 'tags', 'fields' ], options.include);
 
-        return ghostBookshelf.Model.findOne.call(this, args, options);
+        return ghostBookshelf.Model.findOne.call(this, data, options);
     },
 
-    add: function (newPostData, options) {
+    /**
+     * ### Edit
+     * @extends ghostBookshelf.Model.edit to handle returning the full object and manage _updatedAttributes
+     * **See:** [ghostBookshelf.Model.edit](base.js.html#edit)
+     */
+    edit: function (data, options) {
         var self = this;
         options = options || {};
 
-        return ghostBookshelf.Model.add.call(this, newPostData, options).then(function (post) {
-            return self.findOne({status: 'all', id: post.id}, options);
-        });
-    },
-    edit: function (editedPost, options) {
-        var self = this;
-        options = options || {};
-        return ghostBookshelf.Model.edit.call(this, editedPost, options).then(function (post) {
-            if (post) {
-                return self.findOne({status: 'all', id: post.id}, options)
-                    .then(function (found) {
+        return ghostBookshelf.Model.edit.call(this, data, options).then(function (post) {
+            return self.findOne({status: 'all', id: options.id}, options)
+                .then(function (found) {
+                    if (found) {
                         // Pass along the updated attributes for checking status changes
                         found._updatedAttributes = post._updatedAttributes;
                         return found;
-                    });
-            }
+                    }
+                });
         });
     },
-    destroy: function (_identifier, options) {
+
+    /**
+     * ### Add
+     * @extends ghostBookshelf.Model.add to handle returning the full object
+     * **See:** [ghostBookshelf.Model.add](base.js.html#add)
+     */
+    add: function (data, options) {
+        var self = this;
+        options = options || {};
+
+        return ghostBookshelf.Model.add.call(this, data, options).then(function (post) {
+            return self.findOne({status: 'all', id: post.id}, options);
+        });
+    },
+
+    /**
+     * ### Destroy
+     * @extends ghostBookshelf.Model.destroy to clean up tag relations
+     * **See:** [ghostBookshelf.Model.destroy](base.js.html#destroy)
+     */
+    destroy: function (options) {
+        var id = options.id;
         options = this.filterOptions(options, 'destroy');
 
-        return this.forge({id: _identifier}).fetch({withRelated: ['tags']}).then(function destroyTags(post) {
-            var tagIds = _.pluck(post.related('tags').toJSON(), 'id');
-            if (tagIds) {
-                return post.tags().detach(tagIds).then(function destroyPost() {
-                    return post.destroy(options);
-                });
-            }
-
-            return post.destroy(options);
+        return this.forge({id: id}).fetch({withRelated: ['tags']}).then(function destroyTagsAndPost(post) {
+            return post.related('tags').detach().then(function () {
+                return post.destroy(options);
+            });
         });
     },
 
diff --git a/core/server/models/role.js b/core/server/models/role.js
index d6b2eb93c6..f1d0c24665 100644
--- a/core/server/models/role.js
+++ b/core/server/models/role.js
@@ -37,7 +37,7 @@ Role = ghostBookshelf.Model.extend({
         }
 
         return options;
-    },
+    }
 });
 
 Roles = ghostBookshelf.Collection.extend({
diff --git a/core/server/models/session.js b/core/server/models/session.js
index 81dfcee78f..94216ecdc7 100644
--- a/core/server/models/session.js
+++ b/core/server/models/session.js
@@ -19,7 +19,7 @@ Session = ghostBookshelf.Model.extend({
         /*jshint unused:false*/
         // Remove any properties which don't belong on the model
         this.attributes = this.pick(this.permittedAttributes());
-    },
+    }
 
 }, {
     destroyAll:  function (options) {
diff --git a/core/server/models/settings.js b/core/server/models/settings.js
index 53319ea9f2..3fe34612d8 100644
--- a/core/server/models/settings.js
+++ b/core/server/models/settings.js
@@ -80,23 +80,23 @@ Settings = ghostBookshelf.Model.extend({
         return options;
     },
 
-    findOne: function (_key) {
+    findOne: function (options) {
         // Allow for just passing the key instead of attributes
-        if (!_.isObject(_key)) {
-            _key = { key: _key };
+        if (!_.isObject(options)) {
+            options = { key: options };
         }
-        return when(ghostBookshelf.Model.findOne.call(this, _key));
+        return when(ghostBookshelf.Model.findOne.call(this, options));
     },
 
-    edit: function (_data, options) {
+    edit: function (data, options) {
         var self = this;
         options = this.filterOptions(options, 'edit');
 
-        if (!Array.isArray(_data)) {
-            _data = [_data];
+        if (!Array.isArray(data)) {
+            data = [data];
         }
 
-        return when.map(_data, function (item) {
+        return when.map(data, function (item) {
             // Accept an array of models as input
             if (item.toJSON) { item = item.toJSON(); }
             if (!(_.isString(item.key) && item.key.length > 0)) {
diff --git a/core/server/models/tag.js b/core/server/models/tag.js
index 7ed3af746b..ad89d02932 100644
--- a/core/server/models/tag.js
+++ b/core/server/models/tag.js
@@ -57,7 +57,7 @@ Tag = ghostBookshelf.Model.extend({
         }
 
         return options;
-    },
+    }
 });
 
 Tags = ghostBookshelf.Collection.extend({
diff --git a/core/server/models/user.js b/core/server/models/user.js
index 6651aca802..af7fc3351c 100644
--- a/core/server/models/user.js
+++ b/core/server/models/user.js
@@ -106,16 +106,20 @@ User = ghostBookshelf.Model.extend({
     },
 
     /**
+     * ## Add
      * Naive user add
-     * @param {object} _user
-     *
      * Hashes the password provided before saving to the database.
+     *
+     * @param {object} data
+     * @param {object} options
+     * @extends ghostBookshelf.Model.add to manage all aspects of user signup
+     * **See:** [ghostBookshelf.Model.add](base.js.html#Add)
      */
-    add: function (_user, options) {
+    add: function (data, options) {
 
         var self = this,
             // Clone the _user so we don't expose the hashed password unnecessarily
-            userData = this.filterData(_user);
+            userData = this.filterData(data);
 
         options = this.filterOptions(options, 'add');
 
@@ -133,7 +137,7 @@ User = ghostBookshelf.Model.extend({
             }
         }).then(function () {
             // Generate a new password hash
-            return generatePasswordHash(_user.password);
+            return generatePasswordHash(data.password);
         }).then(function (hash) {
             // Assign the hashed password
             userData.password = hash;
@@ -143,6 +147,7 @@ User = ghostBookshelf.Model.extend({
             // Save the user with the hashed password
             return ghostBookshelf.Model.add.call(self, userData, options);
         }).then(function (addedUser) {
+
             // Assign the userData to our created user so we can pass it back
             userData = addedUser;
             // Add this user to the admin role (assumes admin = role_id: 1)
diff --git a/core/server/permissions/index.js b/core/server/permissions/index.js
index 5d4dccf545..b8998b1b35 100644
--- a/core/server/permissions/index.js
+++ b/core/server/permissions/index.js
@@ -35,16 +35,11 @@ function parseContext(context) {
         parsed.internal = true;
     }
 
-    // @TODO: Refactor canThis() references to pass { user: id } explicitly instead of primitives.
-    if (context && context.id) {
-        // Handle passing of just user.id string
-        parsed.user = context.id;
-    } else if (_.isNumber(context)) {
-        // Handle passing of just user id number
-        parsed.user = context;
-    } else if (_.isObject(context)) {
-        // Otherwise, use the new hotness { user: id, app: id } format
+    if (context && context.user) {
         parsed.user = context.user;
+    }
+
+    if (context && context.app) {
         parsed.app = context.app;
     }
 
diff --git a/core/server/routes/admin.js b/core/server/routes/admin.js
index ed4ec99d9d..b354f9a284 100644
--- a/core/server/routes/admin.js
+++ b/core/server/routes/admin.js
@@ -3,9 +3,11 @@ var admin       = require('../controllers/admin'),
     middleware  = require('../middleware').middleware,
 
     ONE_HOUR_S  = 60 * 60,
-    ONE_YEAR_S  = 365 * 24 * ONE_HOUR_S;
+    ONE_YEAR_S  = 365 * 24 * ONE_HOUR_S,
 
-module.exports = function (server) {
+    adminRoutes;
+
+adminRoutes = function (server) {
     // Have ember route look for hits first
     // to prevent conflicts with pre-existing routes
     server.get('/ghost/ember/*', admin.index);
@@ -65,4 +67,6 @@ module.exports = function (server) {
         res.redirect(subdir + '/ghost/');
     });
     server.get('/ghost/', admin.indexold);
-};
\ No newline at end of file
+};
+
+module.exports = adminRoutes;
\ No newline at end of file
diff --git a/core/server/routes/api.js b/core/server/routes/api.js
index d82431e9c7..76e7686c52 100644
--- a/core/server/routes/api.js
+++ b/core/server/routes/api.js
@@ -1,37 +1,43 @@
+// # API routes
 var middleware  = require('../middleware').middleware,
-    api         = require('../api');
+    api         = require('../api'),
+    apiRoutes;
 
-module.exports = function (server) {
-    // ### API routes
-    // #### Posts
-    server.get('/ghost/api/v0.1/posts', api.requestHandler(api.posts.browse));
-    server.post('/ghost/api/v0.1/posts', api.requestHandler(api.posts.add));
-    server.get('/ghost/api/v0.1/posts/:id', api.requestHandler(api.posts.read));
-    server.put('/ghost/api/v0.1/posts/:id', api.requestHandler(api.posts.edit));
-    server.del('/ghost/api/v0.1/posts/:id', api.requestHandler(api.posts.destroy));
-    server.get('/ghost/api/v0.1/posts/slug/:title', middleware.authAPI, api.requestHandler(api.posts.generateSlug));
-    // #### Settings
-    server.get('/ghost/api/v0.1/settings/', api.requestHandler(api.settings.browse));
-    server.get('/ghost/api/v0.1/settings/:key/', api.requestHandler(api.settings.read));
-    server.put('/ghost/api/v0.1/settings/', api.requestHandler(api.settings.edit));
-    // #### Users
-    server.get('/ghost/api/v0.1/users/', api.requestHandler(api.users.browse));
-    server.get('/ghost/api/v0.1/users/:id/', api.requestHandler(api.users.read));
-    server.put('/ghost/api/v0.1/users/:id/', api.requestHandler(api.users.edit));
-    // #### Tags
-    server.get('/ghost/api/v0.1/tags/', api.requestHandler(api.tags.browse));
-    // #### Themes
-    server.get('/ghost/api/v0.1/themes/', api.requestHandler(api.themes.browse));
-    server.put('/ghost/api/v0.1/themes/:name', api.requestHandler(api.themes.edit));
-    // #### Notifications
-    server.del('/ghost/api/v0.1/notifications/:id', api.requestHandler(api.notifications.destroy));
-    server.post('/ghost/api/v0.1/notifications/', api.requestHandler(api.notifications.add));
-    server.get('/ghost/api/v0.1/notifications/', api.requestHandler(api.notifications.browse));
-    // #### Import/Export
-    server.get('/ghost/api/v0.1/db/', api.requestHandler(api.db.exportContent));
-    server.post('/ghost/api/v0.1/db/', middleware.busboy, api.requestHandler(api.db.importContent));
-    server.del('/ghost/api/v0.1/db/', api.requestHandler(api.db.deleteAllContent));
-    // #### Mail
-    server.post('/ghost/api/v0.1/mail', api.requestHandler(api.mail.send));
-    server.post('/ghost/api/v0.1/mail/test', api.requestHandler(api.mail.sendTest));
-};
\ No newline at end of file
+apiRoutes = function (server) {
+    // ## Posts
+    server.get('/ghost/api/v0.1/posts', api.http(api.posts.browse));
+    server.post('/ghost/api/v0.1/posts', api.http(api.posts.add));
+    server.get('/ghost/api/v0.1/posts/:id(\\d+)', api.http(api.posts.read));
+    server.get('/ghost/api/v0.1/posts/:slug([a-z-]+)', api.http(api.posts.read));
+    server.put('/ghost/api/v0.1/posts/:id', api.http(api.posts.edit));
+    server.del('/ghost/api/v0.1/posts/:id', api.http(api.posts.destroy));
+    server.get('/ghost/api/v0.1/posts/slug/:title', api.http(api.posts.generateSlug));
+    // ## Settings
+    server.get('/ghost/api/v0.1/settings/', api.http(api.settings.browse));
+    server.get('/ghost/api/v0.1/settings/:key/', api.http(api.settings.read));
+    server.put('/ghost/api/v0.1/settings/', api.http(api.settings.edit));
+    // ## Users
+    server.get('/ghost/api/v0.1/users/', api.http(api.users.browse));
+    server.get('/ghost/api/v0.1/users/:id/', api.http(api.users.read));
+    server.put('/ghost/api/v0.1/users/:id/', api.http(api.users.edit));
+    // ## Tags
+    server.get('/ghost/api/v0.1/tags/', api.http(api.tags.browse));
+    // ## Themes
+    server.get('/ghost/api/v0.1/themes/', api.http(api.themes.browse));
+    server.put('/ghost/api/v0.1/themes/:name', api.http(api.themes.edit));
+    // ## Notifications
+    server.del('/ghost/api/v0.1/notifications/:id', api.http(api.notifications.destroy));
+    server.post('/ghost/api/v0.1/notifications/', api.http(api.notifications.add));
+    server.get('/ghost/api/v0.1/notifications/', api.http(api.notifications.browse));
+    server.post('/ghost/api/v0.1/notifications/', api.http(api.notifications.add));
+    server.del('/ghost/api/v0.1/notifications/:id', api.http(api.notifications.destroy));
+    // ## DB
+    server.get('/ghost/api/v0.1/db/', api.http(api.db.exportContent));
+    server.post('/ghost/api/v0.1/db/', middleware.busboy, api.http(api.db.importContent));
+    server.del('/ghost/api/v0.1/db/', api.http(api.db.deleteAllContent));
+    // ## Mail
+    server.post('/ghost/api/v0.1/mail', api.http(api.mail.send));
+    server.post('/ghost/api/v0.1/mail/test', api.http(api.mail.sendTest));
+};
+
+module.exports = apiRoutes;
\ No newline at end of file
diff --git a/core/server/routes/frontend.js b/core/server/routes/frontend.js
index 8eb09590d1..91b471abc3 100644
--- a/core/server/routes/frontend.js
+++ b/core/server/routes/frontend.js
@@ -2,9 +2,11 @@ var frontend    = require('../controllers/frontend'),
     config      = require('../config'),
 
     ONE_HOUR_S  = 60 * 60,
-    ONE_YEAR_S  = 365 * 24 * ONE_HOUR_S;
+    ONE_YEAR_S  = 365 * 24 * ONE_HOUR_S,
 
-module.exports = function (server) {
+    frontendRoutes;
+
+frontendRoutes = function (server) {
     var subdir = config().paths.subdir;
 
     // ### Frontend routes
@@ -24,6 +26,6 @@ module.exports = function (server) {
     server.get('/page/:page/', frontend.homepage);
     server.get('/', frontend.homepage);
     server.get('*', frontend.single);
+};
 
-
-};
\ No newline at end of file
+module.exports = frontendRoutes;
\ No newline at end of file
diff --git a/core/server/update-check.js b/core/server/update-check.js
index d0f0cf4d1b..b1a3818d0c 100644
--- a/core/server/update-check.js
+++ b/core/server/update-check.js
@@ -50,9 +50,9 @@ function updateCheckData() {
         ops = [],
         mailConfig = config().mail;
 
-    ops.push(api.settings.read.call({ internal: true }, 'dbHash').otherwise(errors.rejectError));
-    ops.push(api.settings.read.call({ internal: true }, 'activeTheme').otherwise(errors.rejectError));
-    ops.push(api.settings.read.call({ internal: true }, 'activeApps')
+    ops.push(api.settings.read({context: {internal: true}, key: 'dbHash'}).otherwise(errors.rejectError));
+    ops.push(api.settings.read({context: {internal: true}, key: 'activeTheme'}).otherwise(errors.rejectError));
+    ops.push(api.settings.read({context: {internal: true}, key: 'activeApps'})
         .then(function (response) {
             var apps = response.settings[0];
             try {
@@ -64,7 +64,7 @@ function updateCheckData() {
             return _.reduce(apps, function (memo, item) { return memo === '' ? memo + item : memo + ', ' + item; }, '');
         }).otherwise(errors.rejectError));
     ops.push(api.posts.browse().otherwise(errors.rejectError));
-    ops.push(api.users.browse.call({user: 1}).otherwise(errors.rejectError));
+    ops.push(api.users.browse({context: {user: 1}}).otherwise(errors.rejectError));
     ops.push(nodefn.call(exec, 'npm -v').otherwise(errors.rejectError));
 
     data.ghost_version   = currentVersion;
@@ -142,13 +142,21 @@ function updateCheckRequest() {
 // 1. Updates the time we can next make a check
 // 2. Checks if the version in the response is new, and updates the notification setting
 function updateCheckResponse(response) {
-    var ops = [];
+    var ops = [],
+        internalContext = {context: {internal: true}};
 
-    ops.push(api.settings.edit.call({internal: true}, 'nextUpdateCheck', response.next_check)
-        .otherwise(errors.rejectError));
-
-    ops.push(api.settings.edit.call({internal: true}, 'displayUpdateNotification', response.version)
-                .otherwise(errors.rejectError));
+    ops.push(
+        api.settings.edit(
+            {settings: [{key: 'nextUpdateCheck', value: response.next_check}]},
+            internalContext
+        )
+        .otherwise(errors.rejectError),
+        api.settings.edit(
+            {settings: [{key: 'displayUpdateNotification', value: response.version}]},
+            internalContext
+        )
+        .otherwise(errors.rejectError)
+    );
 
     return when.settle(ops).then(function (descriptors) {
         descriptors.forEach(function (d) {
@@ -171,7 +179,7 @@ function updateCheck() {
         // No update check
         deferred.resolve();
     } else {
-        api.settings.read.call({ internal: true }, 'nextUpdateCheck').then(function (result) {
+        api.settings.read({context: {internal: true}, key: 'nextUpdateCheck'}).then(function (result) {
             var nextUpdateCheck = result.settings[0];
 
             if (nextUpdateCheck && nextUpdateCheck.value && nextUpdateCheck.value > moment().unix()) {
@@ -191,7 +199,7 @@ function updateCheck() {
 }
 
 function showUpdateNotification() {
-    return api.settings.read.call({ internal: true }, 'displayUpdateNotification').then(function (response) {
+    return api.settings.read({context: {internal: true}, key: 'displayUpdateNotification'}).then(function (response) {
         var display = response.settings[0];
 
         // Version 0.4 used boolean to indicate the need for an update. This special case is
diff --git a/core/test/blanket_coverage.js b/core/test/blanket_coverage.js
index fac2487ffd..e00d312d0d 100644
--- a/core/test/blanket_coverage.js
+++ b/core/test/blanket_coverage.js
@@ -1,9 +1,11 @@
+// Posts
 var blanket = require("blanket")({
     "pattern": ["/core/server/", "/core/clientold/", "/core/shared/"],
     "data-cover-only": ["/core/server/", "/core/clientold/", "/core/shared/"]
 }),
     requireDir = require("require-dir");
 
+
 requireDir("./unit");
 requireDir("./integration");
 requireDir("./functional/routes");
diff --git a/core/test/functional/routes/api/posts_test.js b/core/test/functional/routes/api/posts_test.js
index b3d890bb36..cf0000d949 100644
--- a/core/test/functional/routes/api/posts_test.js
+++ b/core/test/functional/routes/api/posts_test.js
@@ -61,7 +61,7 @@ describe('Post API', function () {
                                                 if (err) {
                                                     return done(err);
                                                 }
-                                                
+
                                                 csrfToken = res.text.match(pattern_meta)[1];
                                                 done();
                                             });
@@ -124,7 +124,7 @@ describe('Post API', function () {
                     testUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
                     done();
                 });
-                
+
         });
 
         // Test bits of the API we don't use in the app yet to ensure the API behaves properly
@@ -193,7 +193,7 @@ describe('Post API', function () {
 
     // ## Read
     describe('Read', function () {
-        it('can retrieve a post', function (done) {
+        it('can retrieve a post by id', function (done) {
             request.get(testUtils.API.getApiQuery('posts/1/'))
                 .end(function (err, res) {
                     if (err) {
@@ -207,6 +207,32 @@ describe('Post API', function () {
                     jsonResponse.should.exist;
                     jsonResponse.posts.should.exist;
                     testUtils.API.checkResponse(jsonResponse.posts[0], 'post');
+                    jsonResponse.posts[0].id.should.equal(1);
+                    jsonResponse.posts[0].page.should.eql(0);
+                    _.isBoolean(jsonResponse.posts[0].featured).should.eql(true);
+                    _.isBoolean(jsonResponse.posts[0].page).should.eql(true);
+                    jsonResponse.posts[0].author.should.be.a.Number;
+                    jsonResponse.posts[0].created_by.should.be.a.Number;
+                    jsonResponse.posts[0].tags[0].should.be.a.Number;
+                    done();
+                });
+        });
+
+        it('can retrieve a post by slug', function (done) {
+            request.get(testUtils.API.getApiQuery('posts/welcome-to-ghost/'))
+                .end(function (err, res) {
+                    if (err) {
+                        return done(err);
+                    }
+
+                    res.should.have.status(200);
+                    should.not.exist(res.headers['x-cache-invalidate']);
+                    res.should.be.json;
+                    var jsonResponse = res.body;
+                    jsonResponse.should.exist;
+                    jsonResponse.posts.should.exist;
+                    testUtils.API.checkResponse(jsonResponse.posts[0], 'post');
+                    jsonResponse.posts[0].slug.should.equal('welcome-to-ghost');
                     jsonResponse.posts[0].page.should.eql(0);
                     _.isBoolean(jsonResponse.posts[0].featured).should.eql(true);
                     _.isBoolean(jsonResponse.posts[0].page).should.eql(true);
@@ -477,7 +503,6 @@ describe('Post API', function () {
                 });
         });
 
-
         it('can change a static page to a post', function (done) {
             request.get(testUtils.API.getApiQuery('posts/7/'))
                 .end(function (err, res) {
@@ -501,11 +526,11 @@ describe('Post API', function () {
                             }
 
                             var putBody = res.body;
+
                             _.has(res.headers, 'x-cache-invalidate').should.equal(false);
                             res.should.be.json;
                             putBody.should.exist;
                             putBody.posts[0].page.should.eql(changedValue);
-
                             testUtils.API.checkResponse(putBody.posts[0], 'post');
                             done();
                         });
@@ -605,10 +630,6 @@ describe('Post API', function () {
                 });
         });
 
-    });
-
-    // ## delete
-    describe('Delete', function () {
         it('can\'t edit non existent post', function (done) {
             request.get(testUtils.API.getApiQuery('posts/1/'))
                 .end(function (err, res) {
@@ -630,7 +651,6 @@ describe('Post API', function () {
                                 return done(err);
                             }
 
-                            var putBody = res.body;
                             _.has(res.headers, 'x-cache-invalidate').should.equal(false);
                             res.should.be.json;
                             jsonResponse = res.body;
@@ -641,6 +661,10 @@ describe('Post API', function () {
                 });
         });
 
+    });
+
+    // ## delete
+    describe('Delete', function () {
         it('can delete a post', function (done) {
             var deletePostId = 1;
             request.del(testUtils.API.getApiQuery('posts/' + deletePostId + '/'))
@@ -829,9 +853,5 @@ describe('Post API', function () {
                         });
                 });
         });
-
-
     });
-
-
 });
diff --git a/core/test/functional/routes/api/settings_test.js b/core/test/functional/routes/api/settings_test.js
index 51708fd4cc..3988cacfab 100644
--- a/core/test/functional/routes/api/settings_test.js
+++ b/core/test/functional/routes/api/settings_test.js
@@ -214,7 +214,7 @@ describe('Settings API', function () {
                     newValue = 'new value';
                 jsonResponse.should.exist;
                 should.exist(jsonResponse.settings);
-                jsonResponse.settings.push({ key: 'testvalue', value: newValue });
+                jsonResponse.settings = [{ key: 'testvalue', value: newValue }];
 
                 request.put(testUtils.API.getApiQuery('settings/'))
                     .set('X-CSRF-Token', csrfToken)
diff --git a/core/test/functional/routes/api/users_test.js b/core/test/functional/routes/api/users_test.js
index 7a7be8ee99..49f59da95c 100644
--- a/core/test/functional/routes/api/users_test.js
+++ b/core/test/functional/routes/api/users_test.js
@@ -42,7 +42,7 @@ describe('User API', function () {
                             pattern_meta.should.exist;
                             csrfToken = res.text.match(pattern_meta)[1];
 
-                            process.nextTick(function() {
+                            process.nextTick(function () {
                                 request.post('/ghost/signin/')
                                     .set('X-CSRF-Token', csrfToken)
                                     .send({email: user.email, password: user.password})
@@ -144,13 +144,16 @@ describe('User API', function () {
                 }
 
                 var jsonResponse = res.body,
-                    changedValue = 'joe-bloggs.ghost.org';
+                    changedValue = 'joe-bloggs.ghost.org',
+                    dataToSend;
                 jsonResponse.users[0].should.exist;
-                jsonResponse.users[0].website = changedValue;
+                testUtils.API.checkResponse(jsonResponse.users[0], 'user');
+
+                dataToSend = { users: [{website: changedValue}]};
 
                 request.put(testUtils.API.getApiQuery('users/me/'))
                     .set('X-CSRF-Token', csrfToken)
-                    .send(jsonResponse)
+                    .send(dataToSend)
                     .expect(200)
                     .end(function (err, res) {
                         if (err) {
@@ -162,7 +165,7 @@ describe('User API', function () {
                         res.should.be.json;
                         putBody.users[0].should.exist;
                         putBody.users[0].website.should.eql(changedValue);
-
+                        putBody.users[0].email.should.eql(jsonResponse.users[0].email);
                         testUtils.API.checkResponse(putBody.users[0], 'user');
                         done();
                     });
@@ -195,6 +198,4 @@ describe('User API', function () {
 
             });
     });
-
-
 });
\ No newline at end of file
diff --git a/core/test/integration/api/api_db_spec.js b/core/test/integration/api/api_db_spec.js
index 51d43b03f3..9ac5d2c628 100644
--- a/core/test/integration/api/api_db_spec.js
+++ b/core/test/integration/api/api_db_spec.js
@@ -37,7 +37,7 @@ describe('DB API', function () {
 
     it('delete all content', function (done) {
         permissions.init().then(function () {
-            return dbAPI.deleteAllContent.call({user: 1});
+            return dbAPI.deleteAllContent({context: {user: 1}});
         }).then(function (result) {
             should.exist(result.db);
             result.db.should.be.instanceof(Array);
@@ -61,12 +61,12 @@ describe('DB API', function () {
 
     it('delete all content is denied', function (done) {
         permissions.init().then(function () {
-            return dbAPI.deleteAllContent.call({user: 2});
+            return dbAPI.deleteAllContent({context: {user: 2}});
         }).then(function (){
             done(new Error("Delete all content is not denied for editor."));
         }, function (error) {
             error.type.should.eql('NoPermissionError');
-            return dbAPI.deleteAllContent.call({user: 3});
+            return dbAPI.deleteAllContent({context: {user: 3}});
         }).then(function (){
             done(new Error("Delete all content is not denied for author."));
         }, function (error) {
@@ -82,12 +82,12 @@ describe('DB API', function () {
 
     it('export content is denied', function (done) {
         permissions.init().then(function () {
-            return dbAPI.exportContent.call({user: 2});
+            return dbAPI.exportContent({context: {user: 2}});
         }).then(function (){
             done(new Error("Export content is not denied for editor."));
         }, function (error) {
             error.type.should.eql('NoPermissionError');
-            return dbAPI.exportContent.call({user: 3});
+            return dbAPI.exportContent({context: {user: 3}});
         }).then(function (){
             done(new Error("Export content is not denied for author."));
         }, function (error) {
@@ -103,12 +103,12 @@ describe('DB API', function () {
 
     it('import content is denied', function (done) {
         permissions.init().then(function () {
-            return dbAPI.importContent.call({user: 2});
+            return dbAPI.importContent({context: {user: 2}});
         }).then(function (result){
             done(new Error("Import content is not denied for editor."));
         }, function (error) {
             error.type.should.eql('NoPermissionError');
-            return dbAPI.importContent.call({user: 3});
+            return dbAPI.importContent({context: {user: 3}});
         }).then(function (result){
             done(new Error("Import content is not denied for author."));
         }, function (error) {
diff --git a/core/test/integration/api/api_posts_spec.js b/core/test/integration/api/api_posts_spec.js
index 290d4d2774..3203520021 100644
--- a/core/test/integration/api/api_posts_spec.js
+++ b/core/test/integration/api/api_posts_spec.js
@@ -1,4 +1,4 @@
-/*globals describe, before, beforeEach, afterEach, it */
+ /*globals describe, before, beforeEach, afterEach, it */
 var testUtils = require('../../utils'),
     should    = require('should'),
 
diff --git a/core/test/integration/api/api_settings_spec.js b/core/test/integration/api/api_settings_spec.js
index 402e30564d..931181a17a 100644
--- a/core/test/integration/api/api_settings_spec.js
+++ b/core/test/integration/api/api_settings_spec.js
@@ -17,7 +17,14 @@ describe('Settings API', function () {
             internal: true
         },
         callApiWithContext = function (context, method) {
-            return SettingsAPI[method].apply(context, _.toArray(arguments).slice(2));
+            var args = _.toArray(arguments),
+                options = args[args.length - 1];
+
+            if (_.isObject(options)) {
+                options.context = _.clone(context);
+            }
+
+            return SettingsAPI[method].apply({}, args.slice(2));
         },
         getErrorDetails = function (done) {
             return function (err) {
@@ -58,7 +65,7 @@ describe('Settings API', function () {
     });
 
     it('can browse', function (done) {
-        return callApiWithContext(defaultContext, 'browse', 'blog').then(function (results) {
+        return callApiWithContext(defaultContext, 'browse', {}).then(function (results) {
             should.exist(results);
             testUtils.API.checkResponse(results, 'settings');
             results.settings.length.should.be.above(0);
@@ -71,8 +78,23 @@ describe('Settings API', function () {
         }).catch(getErrorDetails(done));
     });
 
-    it('returns core settings for internal requests when browsing', function (done){
-        return callApiWithContext(internalContext, 'browse', 'blog').then(function (results) {
+
+    it('can browse by type', function (done) {
+        return callApiWithContext(defaultContext, 'browse', {type: 'blog'}).then(function (results) {
+            should.exist(results);
+            testUtils.API.checkResponse(results, 'settings');
+            results.settings.length.should.be.above(0);
+            testUtils.API.checkResponse(results.settings[0], 'setting');
+
+            // Check for a core setting
+            should.not.exist(_.find(results.settings, function (setting) { return setting.type === 'core'; }));
+
+            done();
+        }).catch(getErrorDetails(done));
+    });
+
+    it('returns core settings for internal requests when browsing', function (done) {
+        return callApiWithContext(internalContext, 'browse', {}).then(function (results) {
             should.exist(results);
             testUtils.API.checkResponse(results, 'settings');
             results.settings.length.should.be.above(0);
@@ -82,11 +104,11 @@ describe('Settings API', function () {
             should.exist(_.find(results.settings, function (setting) { return setting.type === 'core'; }));
 
             done();
-        }).catch(getErrorDetails(done)); 
+        }).catch(getErrorDetails(done));
     });
 
-    it('can read by string', function (done) {
-        return callApiWithContext(defaultContext, 'read', 'title').then(function (response) {
+    it('can read blog settings by string', function (done) {
+        return SettingsAPI.read('title').then(function (response) {
             should.exist(response);
             testUtils.API.checkResponse(response, 'settings');
             response.settings.length.should.equal(1);
@@ -97,19 +119,17 @@ describe('Settings API', function () {
     });
 
     it('cannot read core settings if not an internal request', function (done) {
-        return callApiWithContext(defaultContext, 'read', 'databaseVersion').then(function (response) {
+        return callApiWithContext(defaultContext, 'read',  {key: 'databaseVersion'}).then(function (response) {
             done(new Error('Allowed to read databaseVersion with external request'));
-        }).catch(function (err) {
-            should.exist(err);
-
-            err.message.should.equal('Attempted to access core setting on external request');
-
+        }).catch(function (error) {
+            should.exist(error);
+            error.type.should.eql('NoPermissionError');
             done();
         });
     });
 
     it('can read core settings if an internal request', function (done) {
-        return callApiWithContext(internalContext, 'read', 'databaseVersion').then(function (response) {
+        return callApiWithContext(internalContext, 'read', {key: 'databaseVersion'}).then(function (response) {
             should.exist(response);
             testUtils.API.checkResponse(response, 'settings');
             response.settings.length.should.equal(1);
@@ -131,37 +151,40 @@ describe('Settings API', function () {
     });
 
     it('can edit', function (done) {
-        return callApiWithContext(defaultContext, 'edit', { settings: [{ key: 'title', value: 'UpdatedGhost'}]}).then(function (response) {
-            should.exist(response);
-            testUtils.API.checkResponse(response, 'settings');
-            response.settings.length.should.equal(1);
-            testUtils.API.checkResponse(response.settings[0], 'setting');
+        return callApiWithContext(defaultContext, 'edit', {settings: [{ key: 'title', value: 'UpdatedGhost'}]}, {})
+            .then(function (response) {
+                should.exist(response);
+                testUtils.API.checkResponse(response, 'settings');
+                response.settings.length.should.equal(1);
+                testUtils.API.checkResponse(response.settings[0], 'setting');
 
-            done();
-        }).catch(done);
-    });
-
-    it('can edit, by key/value', function (done) {
-        return callApiWithContext(defaultContext, 'edit', 'title', 'UpdatedGhost').then(function (response) {
-            should.exist(response);
-            testUtils.API.checkResponse(response, 'settings');
-            response.settings.length.should.equal(1);
-            testUtils.API.checkResponse(response.settings[0], 'setting');
-
-            done();
-        }).catch(getErrorDetails(done));
+                done();
+            }).catch(done);
     });
 
     it('cannot edit a core setting if not an internal request', function (done) {
-        return callApiWithContext(defaultContext, 'edit', 'databaseVersion', '999').then(function (response) {
-            done(new Error('Allowed to edit a core setting as external request'));
-        }).catch(function (err) {
-            should.exist(err);
+        return callApiWithContext(defaultContext, 'edit', {settings: [{ key: 'databaseVersion', value: '999'}]}, {})
+            .then(function () {
+                done(new Error('Allowed to edit a core setting as external request'));
+            }).catch(function (err) {
+                should.exist(err);
 
-            err.message.should.equal('Attempted to access core setting on external request');
+                err.type.should.eql('NoPermissionError');
 
-            done();
-        });
+                done();
+            });
+    });
+
+    it('can edit a core setting with an internal request', function (done) {
+        return callApiWithContext(internalContext, 'edit', {settings: [{ key: 'databaseVersion', value: '999'}]}, {})
+            .then(function (response) {
+                should.exist(response);
+                testUtils.API.checkResponse(response, 'settings');
+                response.settings.length.should.equal(1);
+                testUtils.API.checkResponse(response.settings[0], 'setting');
+
+                done();
+            }).catch(done);
     });
 
     it('ensures values are stringified before saving to database', function (done) {
diff --git a/core/test/integration/api/api_themes_spec.js b/core/test/integration/api/api_themes_spec.js
index 4e925845c6..2ba55929bc 100644
--- a/core/test/integration/api/api_themes_spec.js
+++ b/core/test/integration/api/api_themes_spec.js
@@ -8,7 +8,7 @@ var _             = require('lodash'),
 
     // Stuff we are testing
     permissions   = require('../../../server/permissions'),
-    settings      = require('../../../server/api/settings'),
+    SettingsAPI      = require('../../../server/api/settings'),
     ThemeAPI      = rewire('../../../server/api/themes');
 
 describe('Themes API', function () {
@@ -26,12 +26,15 @@ describe('Themes API', function () {
         testUtils.initData().then(function () {
             return testUtils.insertDefaultFixtures();
         }).then(function () {
+            return SettingsAPI.updateSettingsCache();
+        }).then(function () {
+
             return permissions.init();
         }).then(function () {
             sandbox = sinon.sandbox.create();
 
             // Override settings.read for activeTheme
-            settingsReadStub = sandbox.stub(settings, 'read', function () {
+            settingsReadStub = sandbox.stub(SettingsAPI, 'read', function () {
                 return when({ settings: [{value: 'casper'}] });
             });
 
@@ -67,14 +70,14 @@ describe('Themes API', function () {
         _.extend(configStub, config);
         ThemeAPI.__set__('config', configStub);
 
-        ThemeAPI.browse.call({user: 1}).then(function (result) {
+        ThemeAPI.browse({context: {user: 1}}).then(function (result) {
             should.exist(result);
             result.themes.length.should.be.above(0);
             testUtils.API.checkResponse(result.themes[0], 'theme');
             done();
-        }, function (error) {
+        }).catch(function (error) {
             done(new Error(JSON.stringify(error)));
-        })
+        });
     });
 
     it('can edit', function (done) {
@@ -84,15 +87,15 @@ describe('Themes API', function () {
         _.extend(configStub, config);
         ThemeAPI.__set__('config', configStub);
 
-        ThemeAPI.edit.call({user: 1}, {themes: [{uuid: 'rasper', active: true }]}).then(function (result) {
+        ThemeAPI.edit({themes: [{uuid: 'rasper', active: true }]}, {context: {user: 1}}).then(function (result) {
             should.exist(result);
             should.exist(result.themes);
             result.themes.length.should.be.above(0);
             testUtils.API.checkResponse(result.themes[0], 'theme');
             result.themes[0].uuid.should.equal('rasper');
             done();
-        }, function (error) {
+        }).catch(function (error) {
             done(new Error(JSON.stringify(error)));
-        })
+        });
     })
 });
\ No newline at end of file
diff --git a/core/test/integration/api/api_users_spec.js b/core/test/integration/api/api_users_spec.js
index 0ff3f5bb1d..87bfbf0975 100644
--- a/core/test/integration/api/api_users_spec.js
+++ b/core/test/integration/api/api_users_spec.js
@@ -2,6 +2,8 @@
 var testUtils = require('../../utils'),
     should    = require('should'),
 
+    permissions   = require('../../../server/permissions'),
+
     // Stuff we are testing
     UsersAPI      = require('../../../server/api/users');
 
@@ -22,11 +24,12 @@ describe('Users API', function () {
     describe('No User', function () {
         beforeEach(function (done) {
             testUtils.initData().then(function () {
+                return permissions.init();
+            }).then(function () {
                 done();
             }).catch(done);
         });
 
-
         it('can add with internal user', function (done) {
             UsersAPI.register({ users: [{
                 'name': 'Hello World',
@@ -52,13 +55,15 @@ describe('Users API', function () {
                 return testUtils.insertEditorUser();
             }).then(function () {
                 return testUtils.insertAuthorUser();
+            }).then(function () {
+                return permissions.init();
             }).then(function () {
                 done();
             }).catch(done);
         });
 
         it('admin can browse', function (done) {
-            UsersAPI.browse.call({user: 1}).then(function (results) {
+            UsersAPI.browse({context: {user: 1}}).then(function (results) {
                 should.exist(results);
                 testUtils.API.checkResponse(results, 'users');
                 should.exist(results.users);
@@ -71,7 +76,7 @@ describe('Users API', function () {
         });
 
         it('editor can browse', function (done) {
-            UsersAPI.browse.call({user: 2}).then(function (results) {
+            UsersAPI.browse({context: {user: 2}}).then(function (results) {
                 should.exist(results);
                 testUtils.API.checkResponse(results, 'users');
                 should.exist(results.users);
@@ -84,7 +89,7 @@ describe('Users API', function () {
         });
 
         it('author can browse', function (done) {
-            UsersAPI.browse.call({user: 3}).then(function (results) {
+            UsersAPI.browse({context: {user: 3}}).then(function (results) {
                 should.exist(results);
                 testUtils.API.checkResponse(results, 'users');
                 should.exist(results.users);
@@ -105,7 +110,7 @@ describe('Users API', function () {
         });
 
         it('admin can read', function (done) {
-            UsersAPI.read.call({user: 1}, {id: 1}).then(function (results) {
+            UsersAPI.read({id: 1, context: {user: 1}}).then(function (results) {
                 should.exist(results);
                 testUtils.API.checkResponse(results, 'users');
                 results.users[0].id.should.eql(1);
@@ -115,7 +120,7 @@ describe('Users API', function () {
         });
 
         it('editor can read', function (done) {
-            UsersAPI.read.call({user: 2}, {id: 1}).then(function (results) {
+            UsersAPI.read({id: 1, context: {user: 2}}).then(function (results) {
                 should.exist(results);
                 testUtils.API.checkResponse(results, 'users');
                 results.users[0].id.should.eql(1);
@@ -125,7 +130,7 @@ describe('Users API', function () {
         });
 
         it('author can read', function (done) {
-            UsersAPI.read.call({user: 3}, {id: 1}).then(function (results) {
+            UsersAPI.read({id: 1, context: {user: 3}}).then(function (results) {
                 should.exist(results);
                 testUtils.API.checkResponse(results, 'users');
                 results.users[0].id.should.eql(1);
@@ -145,7 +150,7 @@ describe('Users API', function () {
         });
 
         it('admin can edit', function (done) {
-            UsersAPI.edit.call({user: 1}, {users: [{id: 1, name: 'Joe Blogger'}]}).then(function (response) {
+            UsersAPI.edit({users: [{name: 'Joe Blogger'}]}, {id: 1, context: {user: 1}}).then(function (response) {
                 should.exist(response);
                 testUtils.API.checkResponse(response, 'users');
                 response.users.should.have.length(1);
@@ -157,7 +162,7 @@ describe('Users API', function () {
         });
 
         it('editor can edit', function (done) {
-            UsersAPI.edit.call({user: 2}, {users: [{id: 1, name: 'Joe Blogger'}]}).then(function (response) {
+            UsersAPI.edit({users: [{name: 'Joe Blogger'}]}, {id: 1, context: {user: 2}}).then(function (response) {
                 should.exist(response);
                 testUtils.API.checkResponse(response, 'users');
                 response.users.should.have.length(1);
@@ -170,13 +175,13 @@ describe('Users API', function () {
 
         it('author can edit only self', function (done) {
             // Test author cannot edit admin user
-            UsersAPI.edit.call({user: 3}, {users: [{id: 1, name: 'Joe Blogger'}]}).then(function () {
+            UsersAPI.edit({users: [{name: 'Joe Blogger'}]}, {id: 1, context: {user: 3}}).then(function () {
                 done(new Error('Author should not be able to edit account which is not their own'));
             }).catch(function (error) {
-                error.code.should.eql(403);
+                error.type.should.eql('NoPermissionError');
             }).finally(function () {
                 // Next test that author CAN edit self
-                return UsersAPI.edit.call({user: 3}, {users: [{id: 3, name: 'Timothy Bogendath'}]})
+                return UsersAPI.edit({users: [{name: 'Timothy Bogendath'}]}, {id: 3, context: {user: 3}})
                     .then(function (response) {
                         should.exist(response);
                         testUtils.API.checkResponse(response, 'users');
diff --git a/core/test/integration/model/model_apps_spec.js b/core/test/integration/model/model_apps_spec.js
index 38a990d416..a5e9d78ffa 100644
--- a/core/test/integration/model/model_apps_spec.js
+++ b/core/test/integration/model/model_apps_spec.js
@@ -86,19 +86,20 @@ describe('App Model', function () {
         }).catch(done);
     });
 
-    it("can delete", function (done) {
-        AppModel.findOne({id: 1}).then(function (foundApp) {
+    it("can destroy", function (done) {
+        var firstApp = {id: 1};
+
+        AppModel.findOne(firstApp).then(function (foundApp) {
             should.exist(foundApp);
+            foundApp.attributes.id.should.equal(firstApp.id);
 
-            return AppModel.destroy(1);
-        }).then(function () {
-            return AppModel.findAll();
-        }).then(function (foundApp) {
-            var hasRemovedId = foundApp.any(function (foundApp) {
-                return foundApp.id === 1;
-            });
+            return AppModel.destroy(firstApp);
+        }).then(function (response) {
+            response.toJSON().should.be.empty;
 
-            hasRemovedId.should.equal(false);
+            return AppModel.findOne(firstApp);
+        }).then(function (newResults) {
+            should.equal(newResults, null);
 
             done();
         }).catch(done);
diff --git a/core/test/integration/model/model_permissions_spec.js b/core/test/integration/model/model_permissions_spec.js
index 370a3e571d..192b9e4038 100644
--- a/core/test/integration/model/model_permissions_spec.js
+++ b/core/test/integration/model/model_permissions_spec.js
@@ -80,19 +80,19 @@ describe('Permission Model', function () {
         }).catch(done);
     });
 
-    it('can delete', function (done) {
-        PermissionModel.findOne({id: 1}).then(function (foundPermission) {
+    it('can destroy', function (done) {
+        var firstPermission = {id: 1};
+
+        PermissionModel.findOne(firstPermission).then(function (foundPermission) {
             should.exist(foundPermission);
+            foundPermission.attributes.id.should.equal(firstPermission.id);
 
-            return PermissionModel.destroy(1);
-        }).then(function () {
-            return PermissionModel.findAll();
-        }).then(function (foundPermissions) {
-            var hasRemovedId = foundPermissions.any(function (permission) {
-                return permission.id === 1;
-            });
-
-            hasRemovedId.should.equal(false);
+            return PermissionModel.destroy(firstPermission);
+        }).then(function (response) {
+            response.toJSON().should.be.empty;
+            return PermissionModel.findOne(firstPermission);
+        }).then(function (newResults) {
+            should.equal(newResults, null);
 
             done();
         }).catch(done);
diff --git a/core/test/integration/model/model_posts_spec.js b/core/test/integration/model/model_posts_spec.js
index 78d44e5310..800606269d 100644
--- a/core/test/integration/model/model_posts_spec.js
+++ b/core/test/integration/model/model_posts_spec.js
@@ -35,19 +35,80 @@ describe('Post Model', function () {
         }).catch(done);
     });
 
+    function checkFirstPostData(firstPost) {
+        should.not.exist(firstPost.author_id);
+        firstPost.author.should.be.an.Object;
+        firstPost.fields.should.be.an.Array;
+        firstPost.tags.should.be.an.Array;
+        firstPost.author.name.should.equal(DataGenerator.Content.users[0].name);
+        firstPost.fields[0].key.should.equal(DataGenerator.Content.app_fields[0].key);
+        firstPost.created_by.should.be.an.Object;
+        firstPost.updated_by.should.be.an.Object;
+        firstPost.published_by.should.be.an.Object;
+        firstPost.created_by.name.should.equal(DataGenerator.Content.users[0].name);
+        firstPost.updated_by.name.should.equal(DataGenerator.Content.users[0].name);
+        firstPost.published_by.name.should.equal(DataGenerator.Content.users[0].name);
+        firstPost.tags[0].name.should.equal('Getting Started');
+    }
+
     it('can findAll', function (done) {
         PostModel.findAll().then(function (results) {
             should.exist(results);
             results.length.should.be.above(1);
 
-            // should be in published_at, DESC order
-            // model and API differ here - need to fix
-            //results.models[0].attributes.published_at.should.be.above(results.models[1].attributes.published_at);
+            done();
+        }).catch(done);
+    });
+
+    it('can findAll, returning all related data', function (done) {
+        var firstPost;
+
+        PostModel.findAll({include: ['author_id', 'fields', 'tags', 'created_by', 'updated_by', 'published_by']})
+            .then(function (results) {
+                should.exist(results);
+                results.length.should.be.above(0);
+                firstPost = results.models[0].toJSON();
+
+                checkFirstPostData(firstPost);
+
+                done();
+            }).catch(done);
+    });
+
+    it('can findPage (default)', function (done) {
+        PostModel.findPage().then(function (results) {
+            should.exist(results);
+
+            results.meta.pagination.page.should.equal(1);
+            results.meta.pagination.limit.should.equal(15);
+            results.meta.pagination.pages.should.equal(1);
+            results.posts.length.should.equal(5);
 
             done();
         }).catch(done);
     });
 
+    it('can findPage, returning all related data', function (done) {
+        var firstPost;
+
+        PostModel.findPage({include: ['author_id', 'fields', 'tags', 'created_by', 'updated_by', 'published_by']})
+            .then(function (results) {
+                should.exist(results);
+
+                results.meta.pagination.page.should.equal(1);
+                results.meta.pagination.limit.should.equal(15);
+                results.meta.pagination.pages.should.equal(1);
+                results.posts.length.should.equal(5);
+
+                firstPost = results.posts[0];
+
+                checkFirstPostData(firstPost);
+
+                done();
+            }).catch(done);
+    });
+
+
     it('can findOne', function (done) {
         var firstPost;
 
@@ -66,50 +127,31 @@ describe('Post Model', function () {
         }).catch(done);
     });
 
-    it('can findAll, returning author and field data', function (done) {
-        var firstPost;
-
-        PostModel.findAll({include: ['author_id', 'fields']}).then(function (results) {
-            should.exist(results);
-            results.length.should.be.above(0);
-            firstPost = results.models[0].toJSON();
-
-            should.not.exist(firstPost.author_id);
-            firstPost.author.should.be.an.Object;
-            firstPost.fields.should.be.an.Array;
-            firstPost.author.name.should.equal(DataGenerator.Content.users[0].name);
-            firstPost.fields[0].key.should.equal(DataGenerator.Content.app_fields[0].key);
-
-            done();
-        }).catch(done);
-    });
-
-    it('can findOne, returning author and field data', function (done) {
+    it('can findOne, returning all related data', function (done) {
         var firstPost;
         // TODO: should take author :-/
-        PostModel.findOne({}, {include: ['author_id', 'fields']}).then(function (result) {
-            should.exist(result);
-            firstPost = result.toJSON();
+        PostModel.findOne({}, {include: ['author_id', 'fields', 'tags', 'created_by', 'updated_by', 'published_by']})
+            .then(function (result) {
+                should.exist(result);
+                firstPost = result.toJSON();
 
-            should.not.exist(firstPost.author_id);
-            firstPost.author.should.be.an.Object;
-            firstPost.fields.should.be.an.Array;
-            firstPost.author.name.should.equal(testUtils.DataGenerator.Content.users[0].name);
-            firstPost.fields[0].key.should.equal(DataGenerator.Content.app_fields[0].key);
+                checkFirstPostData(firstPost);
 
-            done();
-        }).catch(done);
+                done();
+            }).catch(done);
     });
 
     it('can edit', function (done) {
-        var firstPost;
+        var firstPost = 1;
 
-        PostModel.findAll().then(function (results) {
+        PostModel.findOne({id: firstPost}).then(function (results) {
+            var post;
             should.exist(results);
-            results.length.should.be.above(0);
-            firstPost = results.models[0];
+            post = results.toJSON();
+            post.id.should.equal(firstPost);
+            post.title.should.not.equal('new title');
 
-            return PostModel.edit({id: firstPost.id, title: 'new title'});
+            return PostModel.edit({title: 'new title'}, {id: firstPost});
         }).then(function (edited) {
             should.exist(edited);
             edited.attributes.title.should.equal('new title');
@@ -118,6 +160,7 @@ describe('Post Model', function () {
         }).catch(done);
     });
 
+
     it('can add, defaults are all correct', function (done) {
         var createdPostUpdatedDate,
             newPost = testUtils.DataGenerator.forModel.posts[2],
@@ -330,18 +373,32 @@ describe('Post Model', function () {
             }).catch(done);
     });
 
-    it('can delete', function (done) {
-        var firstPostId;
-        PostModel.findAll().then(function (results) {
-            should.exist(results);
-            results.length.should.be.above(0);
-            firstPostId = results.models[0].id;
+    it('can destroy', function (done) {
+        // We're going to try deleting post id 1 which also has tag id 1
+        var firstItemData = {id: 1};
 
-            return PostModel.destroy(firstPostId);
-        }).then(function () {
-            return PostModel.findOne({id: firstPostId});
+        // Test that we have the post we expect, with exactly one tag
+        PostModel.findOne(firstItemData).then(function (results) {
+            var post;
+            should.exist(results);
+            post = results.toJSON();
+            post.id.should.equal(firstItemData.id);
+            post.tags.should.have.length(1);
+            post.tags[0].should.equal(firstItemData.id);
+
+            // Destroy the post
+            return PostModel.destroy(firstItemData);
+        }).then(function (response) {
+            var deleted = response.toJSON();
+
+            deleted.tags.should.be.empty;
+            should.equal(deleted.author, undefined);
+
+            // Double check we can't find the post again
+            return PostModel.findOne(firstItemData);
         }).then(function (newResults) {
             should.equal(newResults, null);
+
             done();
         }).catch(done);
     });
@@ -372,6 +429,7 @@ describe('Post Model', function () {
             paginationResult.meta.pagination.pages.should.equal(2);
             paginationResult.posts.length.should.equal(30);
 
+            // Test both boolean formats
             return PostModel.findPage({limit: 10, staticPages: true});
         }).then(function (paginationResult) {
             paginationResult.meta.pagination.page.should.equal(1);
@@ -379,10 +437,26 @@ describe('Post Model', function () {
             paginationResult.meta.pagination.pages.should.equal(1);
             paginationResult.posts.length.should.equal(1);
 
+            // Test both boolean formats
+            return PostModel.findPage({limit: 10, staticPages: '1'});
+        }).then(function (paginationResult) {
+            paginationResult.meta.pagination.page.should.equal(1);
+            paginationResult.meta.pagination.limit.should.equal(10);
+            paginationResult.meta.pagination.pages.should.equal(1);
+            paginationResult.posts.length.should.equal(1);
+
             return PostModel.findPage({limit: 10, page: 2, status: 'all'});
         }).then(function (paginationResult) {
             paginationResult.meta.pagination.pages.should.equal(11);
 
+            done();
+        }).catch(done);
+    });
+    it('can findPage for tag, with various options', function (done) {
+        testUtils.insertMorePosts().then(function () {
+
+            return testUtils.insertMorePostsTags();
+        }).then(function () {
             // Test tag filter
             return PostModel.findPage({page: 1, tag: 'bacon'});
         }).then(function (paginationResult) {
diff --git a/core/test/integration/model/model_roles_spec.js b/core/test/integration/model/model_roles_spec.js
index e26191fd89..d39ffe7036 100644
--- a/core/test/integration/model/model_roles_spec.js
+++ b/core/test/integration/model/model_roles_spec.js
@@ -79,19 +79,19 @@ describe('Role Model', function () {
         }).catch(done);
     });
 
-    it('can delete', function (done) {
-        RoleModel.findOne({id: 1}).then(function (foundRole) {
+    it('can destroy', function (done) {
+        var firstRole = {id: 1};
+
+        RoleModel.findOne(firstRole).then(function (foundRole) {
             should.exist(foundRole);
+            foundRole.attributes.id.should.equal(firstRole.id);
 
-            return RoleModel.destroy(1);
-        }).then(function (destResp) {
-            return RoleModel.findAll();
-        }).then(function (foundRoles) {
-            var hasRemovedId = foundRoles.any(function (role) {
-                return role.id === 1;
-            });
-
-            hasRemovedId.should.equal(false);
+            return RoleModel.destroy(firstRole);
+        }).then(function (response) {
+            response.toJSON().should.be.empty;
+            return  RoleModel.findOne(firstRole);
+        }).then(function (newResults) {
+            should.equal(newResults, null);
 
             done();
         }).catch(done);
diff --git a/core/test/integration/model/model_settings_spec.js b/core/test/integration/model/model_settings_spec.js
index eac7361963..93be546d91 100644
--- a/core/test/integration/model/model_settings_spec.js
+++ b/core/test/integration/model/model_settings_spec.js
@@ -153,39 +153,23 @@ describe('Settings Model', function () {
             }).catch(done);
         });
 
-        it('can delete', function (done) {
-            var settingId;
-
-            SettingsModel.findAll().then(function (results) {
+        it('can destroy', function (done) {
+            // dont't use id 1, since it will delete databaseversion
+            var settingToDestroy = {id: 2};
 
+            SettingsModel.findOne(settingToDestroy).then(function (results) {
                 should.exist(results);
+                results.attributes.id.should.equal(settingToDestroy.id);
 
-                results.length.should.be.above(0);
-
-                // dont't use results.models[0], since it will delete databaseversion
-                // which is used for testUtils.reset()
-                settingId = results.models[1].id;
-
-                return SettingsModel.destroy(settingId);
-
-            }).then(function () {
-
-                return SettingsModel.findAll();
+                return SettingsModel.destroy(settingToDestroy);
+            }).then(function (response) {
+                response.toJSON().should.be.empty;
 
+                return SettingsModel.findOne(settingToDestroy);
             }).then(function (newResults) {
-
-                var ids, hasDeletedId;
-
-                ids = _.pluck(newResults.models, 'id');
-
-                hasDeletedId = _.any(ids, function (id) {
-                    return id === settingId;
-                });
-
-                hasDeletedId.should.equal(false);
+                should.equal(newResults, null);
 
                 done();
-
             }).catch(done);
         });
     });
diff --git a/core/test/integration/model/model_users_spec.js b/core/test/integration/model/model_users_spec.js
index fbca93a2f2..e9bb65aecc 100644
--- a/core/test/integration/model/model_users_spec.js
+++ b/core/test/integration/model/model_users_spec.js
@@ -144,7 +144,7 @@ describe('User Model', function run() {
         it('sets last login time on successful login', function (done) {
             var userData = testUtils.DataGenerator.forModel.users[0];
 
-            UserModel.check({email: userData.email, pw:userData.password}).then(function (activeUser) {
+            UserModel.check({email: userData.email, pw: userData.password}).then(function (activeUser) {
                 should.exist(activeUser.get('last_login'));
                 done();
             }).catch(done);
@@ -175,19 +175,13 @@ describe('User Model', function run() {
             var firstUser;
 
             UserModel.findAll().then(function (results) {
-
                 should.exist(results);
-
                 results.length.should.be.above(0);
-
                 firstUser = results.models[0];
 
                 return UserModel.findOne({email: firstUser.attributes.email});
-
             }).then(function (found) {
-
                 should.exist(found);
-
                 found.attributes.name.should.equal(firstUser.attributes.name);
 
                 done();
@@ -197,22 +191,18 @@ describe('User Model', function run() {
         });
 
         it('can edit', function (done) {
-            var firstUser;
-
-            UserModel.findAll().then(function (results) {
+            var firstUser = 1;
 
+            UserModel.findOne({id: firstUser}).then(function (results) {
+                var user;
                 should.exist(results);
+                user = results.toJSON();
+                user.id.should.equal(firstUser);
+                should.equal(user.website, null);
 
-                results.length.should.be.above(0);
-
-                firstUser = results.models[0];
-
-                return UserModel.edit({id: firstUser.id, website: "some.newurl.com"});
-
+                return UserModel.edit({website: 'some.newurl.com'}, {id: firstUser});
             }).then(function (edited) {
-
                 should.exist(edited);
-
                 edited.attributes.website.should.equal('some.newurl.com');
 
                 done();
@@ -220,41 +210,43 @@ describe('User Model', function run() {
             }).catch(done);
         });
 
-        it('can delete', function (done) {
-            var firstUserId;
+        it('can destroy', function (done) {
+            var firstUser = {id: 1};
 
-            UserModel.findAll().then(function (results) {
+            // Test that we have the user we expect
+            UserModel.findOne(firstUser).then(function (results) {
 
+                var user;
                 should.exist(results);
+                user = results.toJSON();
+                user.id.should.equal(firstUser.id);
 
-                results.length.should.be.above(0);
-
-                firstUserId = results.models[0].id;
-
-                return UserModel.destroy(firstUserId);
-
-            }).then(function () {
-
-                return UserModel.findAll();
+                // Destroy the user
+                return UserModel.destroy(firstUser);
+            }).then(function (response) {
+                response.toJSON().should.be.empty;
 
+                // Double check we can't find the user again
+                return UserModel.findOne(firstUser);
             }).then(function (newResults) {
-                var ids, hasDeletedId;
+                should.equal(newResults, null);
 
-                if (newResults.length < 1) {
-                    // Bug out if we only had one user and deleted it.
-                    return done();
-                }
-
-                ids = _.pluck(newResults.models, "id");
-                hasDeletedId = _.any(ids, function (id) {
-                    return id === firstUserId;
-                });
-
-                hasDeletedId.should.equal(false);
                 done();
-
             }).catch(done);
         });
+    });
+
+    describe('Password Reset', function () {
+
+       beforeEach(function (done) {
+           testUtils.initData()
+               .then(function () {
+                   return when(testUtils.insertDefaultUser());
+               })
+               .then(function () {
+                   done();
+               }).catch(done);
+       });
 
         it('can generate reset token', function (done) {
             // Expires in one minute
diff --git a/core/test/unit/frontend_spec.js b/core/test/unit/frontend_spec.js
index 48a840c2ae..118632a13f 100644
--- a/core/test/unit/frontend_spec.js
+++ b/core/test/unit/frontend_spec.js
@@ -44,7 +44,7 @@ describe('Frontend Controller', function () {
             });
 
             apiSettingsStub = sandbox.stub(api.settings, 'read');
-            apiSettingsStub.withArgs('postsPerPage').returns(when({ 
+            apiSettingsStub.withArgs('postsPerPage').returns(when({
                 settings: [{
                     'key': 'postsPerPage',
                     'value': 6
@@ -181,7 +181,7 @@ describe('Frontend Controller', function () {
                     done(new Error(msg));
                 };
             };
- 
+
         beforeEach(function () {
             sandbox.stub(api.posts, 'browse', function (args) {
                 return when({
@@ -197,23 +197,23 @@ describe('Frontend Controller', function () {
                     }
                 });
             });
- 
+
             apiSettingsStub = sandbox.stub(api.settings, 'read');
- 
-            apiSettingsStub.withArgs('activeTheme').returns(when({
+
+            apiSettingsStub.withArgs(sinon.match.has('key', 'activeTheme')).returns(when({
                 settings: [{
                     'key': 'activeTheme',
                     'value': 'casper'
                 }]
             }));
- 
+
             apiSettingsStub.withArgs('postsPerPage').returns(when({
                 settings: [{
                     'key': 'postsPerPage',
                     'value': '10'
                 }]
             }));
- 
+
             frontend.__set__('config',  sandbox.stub().returns({
                 'paths': {
                     'subdir': '',
@@ -229,15 +229,18 @@ describe('Frontend Controller', function () {
                 }
             }));
         });
-        
+
         describe('custom tag template', function () {
- 
+
             beforeEach(function () {
                 apiSettingsStub.withArgs('permalinks').returns(when({
-                    value: '/tag/:slug/'
+                    settings: [{
+                        key: 'permalinks',
+                        value: '/tag/:slug/'
+                    }]
                 }));
             });
- 
+
             it('it will render custom tag template if it exists', function (done) {
                 var req = {
                         path: '/tag/' + mockTags[0].slug,
@@ -250,7 +253,7 @@ describe('Frontend Controller', function () {
                             done();
                         }
                     };
- 
+
                 frontend.tag(req, res, failTest(done));
             });
         });
@@ -422,7 +425,7 @@ describe('Frontend Controller', function () {
 
             apiSettingsStub = sandbox.stub(api.settings, 'read');
 
-            apiSettingsStub.withArgs('activeTheme').returns(when({
+            apiSettingsStub.withArgs(sinon.match.has('key', 'activeTheme')).returns(when({
                 settings: [{
                     'key': 'activeTheme',
                     'value': 'casper'
@@ -451,7 +454,7 @@ describe('Frontend Controller', function () {
             describe('custom page templates', function () {
                 beforeEach(function () {
                     apiSettingsStub.withArgs('permalinks').returns(when({
-                            settings: [{
+                        settings: [{
                             value: '/:slug/'
                         }]
                     }));
@@ -547,8 +550,8 @@ describe('Frontend Controller', function () {
                 beforeEach(function () {
                     apiSettingsStub.withArgs('permalinks').returns(when({
                         settings: [{
-                        value: '/:year/:month/:day/:slug/'
-                        }] 
+                            value: '/:year/:month/:day/:slug/'
+                        }]
                     }));
                 });
 
@@ -621,7 +624,7 @@ describe('Frontend Controller', function () {
                     apiSettingsStub.withArgs('permalinks').returns(when({
                         settings: [{
                             value: '/:slug'
-                        }] 
+                        }]
                     }));
                 });
 
@@ -694,7 +697,7 @@ describe('Frontend Controller', function () {
                     apiSettingsStub.withArgs('permalinks').returns(when({
                         settings: [{
                             value: '/:year/:month/:day/:slug'
-                        }] 
+                        }]
                     }));
                 });
 
@@ -784,7 +787,7 @@ describe('Frontend Controller', function () {
                     apiSettingsStub.withArgs('permalinks').returns(when({
                         settings: [{
                             value: '/:year/:slug'
-                        }] 
+                        }]
                     }));
                 });
 
diff --git a/core/test/unit/permissions_spec.js b/core/test/unit/permissions_spec.js
index 413c9959dc..11db5ae4f6 100644
--- a/core/test/unit/permissions_spec.js
+++ b/core/test/unit/permissions_spec.js
@@ -24,16 +24,6 @@ describe('Permissions', function () {
             }).catch(done);
     });
 
-    beforeEach(function (done) {
-        sandbox = sinon.sandbox.create();
-        testUtils.initData()
-            .then(testUtils.insertDefaultUser)
-            .then(testUtils.insertDefaultApp)
-            .then(function () {
-                done();
-            }).catch(done);
-    });
-
     afterEach(function (done) {
         sandbox.restore();
         testUtils.clearData()
@@ -60,21 +50,7 @@ describe('Permissions', function () {
             { act: "remove", obj: "user" }
         ],
         currTestPermId = 1,
-        // currTestUserId = 1,
-        // createTestUser = function (email) {
-        //     if (!email) {
-        //         currTestUserId += 1;
-        //         email = "test" + currTestPermId + "@test.com";
-        //     }
 
-        //     var newUser = {
-        //         id: currTestUserId,
-        //         email: email,
-        //         password: "testing123"
-        //     };
-
-        //     return UserProvider.add(newUser);
-        // },
         createPermission = function (name, act, obj) {
             if (!name) {
                 currTestPermId += 1;
@@ -97,347 +73,374 @@ describe('Permissions', function () {
             return when.all(createActions);
         };
 
-    it('can load an actions map from existing permissions', function (done) {
+    describe('Init Permissions', function () {
 
-        createTestPermissions()
-            .then(permissions.init)
-            .then(function (actionsMap) {
-                should.exist(actionsMap);
+        beforeEach(function (done) {
+            sandbox = sinon.sandbox.create();
+            testUtils.initData()
+                .then(testUtils.insertDefaultUser)
+                .then(testUtils.insertDefaultApp)
+                .then(function () {
+                    done();
+                }).catch(done);
+        });
 
-                actionsMap.edit.sort().should.eql(['post', 'tag', 'user', 'page', 'theme', 'setting'].sort());
+        it('can load an actions map from existing permissions', function (done) {
+            createTestPermissions()
+                .then(permissions.init)
+                .then(function (actionsMap) {
+                    should.exist(actionsMap);
 
-                actionsMap.should.equal(permissions.actionsMap);
+                    actionsMap.edit.sort().should.eql(['post', 'tag', 'user', 'page', 'theme', 'setting'].sort());
+
+                    actionsMap.should.equal(permissions.actionsMap);
+
+                    done();
+                }).catch(done);
+        });
+
+        it('can add user to role', function (done) {
+            var existingUserRoles;
+
+            UserProvider.findOne({id: 1}, { withRelated: ['roles'] }).then(function (foundUser) {
+                var testRole = new Models.Role({
+                    name: 'testrole1',
+                    description: 'testrole1 description'
+                });
+
+                should.exist(foundUser);
+
+                should.exist(foundUser.roles());
+
+                existingUserRoles = foundUser.related('roles').length;
+
+                return testRole.save(null, {user: 1}).then(function () {
+                    return foundUser.roles().attach(testRole);
+                });
+            }).then(function () {
+                return UserProvider.findOne({id: 1}, { withRelated: ['roles'] });
+            }).then(function (updatedUser) {
+                should.exist(updatedUser);
+
+                updatedUser.related('roles').length.should.equal(existingUserRoles + 1);
 
                 done();
             }).catch(done);
-    });
-
-    it('can add user to role', function (done) {
-        var existingUserRoles;
-
-        UserProvider.findOne({id: 1}, { withRelated: ['roles'] }).then(function (foundUser) {
-            var testRole = new Models.Role({
-                name: 'testrole1',
-                description: 'testrole1 description'
-            });
-
-            should.exist(foundUser);
-
-            should.exist(foundUser.roles());
-
-            existingUserRoles = foundUser.related('roles').length;
-
-            return testRole.save(null, {user: 1}).then(function () {
-                return foundUser.roles().attach(testRole);
-            });
-        }).then(function () {
-            return UserProvider.findOne({id: 1}, { withRelated: ['roles'] });
-        }).then(function (updatedUser) {
-            should.exist(updatedUser);
-
-            updatedUser.related('roles').length.should.equal(existingUserRoles + 1);
-
-            done();
-        }).catch(done);
-    });
-
-    it('can add user permissions', function (done) {
-        UserProvider.findOne({id: 1}, { withRelated: ['permissions']}).then(function (testUser) {
-            var testPermission = new Models.Permission({
-                name: "test edit posts",
-                action_type: 'edit',
-                object_type: 'post'
-            });
-
-            testUser.related('permissions').length.should.equal(0);
-
-            return testPermission.save(null, {user: 1}).then(function () {
-                return testUser.permissions().attach(testPermission);
-            });
-        }).then(function () {
-            return UserProvider.findOne({id: 1}, { withRelated: ['permissions']});
-        }).then(function (updatedUser) {
-            should.exist(updatedUser);
-
-            updatedUser.related('permissions').length.should.equal(1);
-
-            done();
-        }).catch(done);
-    });
-
-    it('can add role permissions', function (done) {
-        var testRole = new Models.Role({
-            name: "test2",
-            description: "test2 description"
         });
 
-        testRole.save(null, {user: 1})
-            .then(function () {
-                return testRole.load('permissions');
-            })
-            .then(function () {
-                var rolePermission = new Models.Permission({
+        it('can add user permissions', function (done) {
+            UserProvider.findOne({id: 1}, { withRelated: ['permissions']}).then(function (testUser) {
+                var testPermission = new Models.Permission({
                     name: "test edit posts",
                     action_type: 'edit',
                     object_type: 'post'
                 });
 
-                testRole.related('permissions').length.should.equal(0);
+                testUser.related('permissions').length.should.equal(0);
 
-                return rolePermission.save(null, {user: 1}).then(function () {
-                    return testRole.permissions().attach(rolePermission);
+                return testPermission.save(null, {user: 1}).then(function () {
+                    return testUser.permissions().attach(testPermission);
                 });
-            })
-            .then(function () {
-                return Models.Role.findOne({id: testRole.id}, { withRelated: ['permissions']});
-            })
-            .then(function (updatedRole) {
-                should.exist(updatedRole);
-
-                updatedRole.related('permissions').length.should.equal(1);
-
-                done();
-            }).catch(done);
-    });
-
-    it('does not allow edit post without permission', function (done) {
-        var fakePage = {
-                id: 1
-            };
-
-        createTestPermissions()
-            .then(permissions.init)
-            .then(function () {
-                return UserProvider.findOne({id: 1});
-            })
-            .then(function (foundUser) {
-                var canThisResult = permissions.canThis(foundUser);
-
-                should.exist(canThisResult.edit);
-                should.exist(canThisResult.edit.post);
-
-                return canThisResult.edit.page(fakePage);
-            })
-            .then(function () {
-                errors.logError(new Error("Allowed edit post without permission"));
-            }).catch(done);
-    });
-
-    it('allows edit post with permission', function (done) {
-        var fakePost = {
-                id: "1"
-            };
-
-        createTestPermissions()
-            .then(permissions.init)
-            .then(function () {
-                return UserProvider.findOne({id: 1});
-            })
-            .then(function (foundUser) {
-                var newPerm = new Models.Permission({
-                    name: "test3 edit post",
-                    action_type: "edit",
-                    object_type: "post"
-                });
-
-                return newPerm.save(null, {user: 1}).then(function () {
-                    return foundUser.permissions().attach(newPerm);
-                });
-            })
-            .then(function () {
+            }).then(function () {
                 return UserProvider.findOne({id: 1}, { withRelated: ['permissions']});
-            })
-            .then(function (updatedUser) {
+            }).then(function (updatedUser) {
+                should.exist(updatedUser);
 
-                // TODO: Verify updatedUser.related('permissions') has the permission?
-                var canThisResult = permissions.canThis(updatedUser.id);
+                updatedUser.related('permissions').length.should.equal(1);
 
-                should.exist(canThisResult.edit);
-                should.exist(canThisResult.edit.post);
-
-                return canThisResult.edit.post(fakePost);
-            })
-            .then(function () {
                 done();
             }).catch(done);
-    });
+        });
 
-    it('can use permissable function on Model to allow something', function (done) {
-        var testUser,
-            permissableStub = sandbox.stub(PostProvider, 'permissable', function () {
-                return when.resolve();
+        it('can add role permissions', function (done) {
+            var testRole = new Models.Role({
+                name: "test2",
+                description: "test2 description"
             });
 
-        testUtils.insertAuthorUser()
-            .then(function () {
-                return UserProvider.findAll();
-            })
-            .then(function (foundUser) {
-                testUser = foundUser.models[1];
+            testRole.save(null, {user: 1})
+                .then(function () {
+                    return testRole.load('permissions');
+                })
+                .then(function () {
+                    var rolePermission = new Models.Permission({
+                        name: "test edit posts",
+                        action_type: 'edit',
+                        object_type: 'post'
+                    });
 
-                return permissions.canThis(testUser).edit.post(123);
-            })
-            .then(function () {
-                permissableStub.restore();
-                permissableStub.calledWith(123, { user: testUser.id, app: null, internal: false }).should.equal(true);
+                    testRole.related('permissions').length.should.equal(0);
 
-                done();
-            })
-            .catch(function () {
-                permissableStub.restore();
-                errors.logError(new Error("Did not allow testUser"));
+                    return rolePermission.save(null, {user: 1}).then(function () {
+                        return testRole.permissions().attach(rolePermission);
+                    });
+                })
+                .then(function () {
+                    return Models.Role.findOne({id: testRole.id}, { withRelated: ['permissions']});
+                })
+                .then(function (updatedRole) {
+                    should.exist(updatedRole);
+
+                    updatedRole.related('permissions').length.should.equal(1);
+
+                    done();
+                }).catch(done);
+        });
 
-                done();
-            });
     });
 
-    it('can use permissable function on Model to forbid something', function (done) {
-        var testUser,
-            permissableStub = sandbox.stub(PostProvider, 'permissable', function () {
-                return when.reject();
-            });
+    describe('With Permissions', function () {
 
-        testUtils.insertAuthorUser()
-            .then(function () {
-                return UserProvider.findAll();
-            })
-            .then(function (foundUser) {
-                testUser = foundUser.models[1];
+        beforeEach(function (done) {
+            sandbox = sinon.sandbox.create();
+            testUtils.initData()
+                .then(testUtils.insertDefaultUser)
+                .then(testUtils.insertDefaultApp)
+                .then(function () {
+                    done();
+                }).catch(done);
+        });
 
-                return permissions.canThis(testUser).edit.post(123);
-            })
-            .then(function () {
-                permissableStub.restore();
 
-                done(new Error("Allowed testUser to edit post"));
-            })
-            .catch(function () {
-                permissableStub.restore();
-                permissableStub.calledWith(123, { user: testUser.id, app: null, internal: false }).should.equal(true);
-                done();
-            });
-    });
+        it('does not allow edit post without permission', function (done) {
+            var fakePage = {
+                    id: 1
+                };
 
-    it("can get effective user permissions", function (done) {
-        effectivePerms.user(1).then(function (effectivePermissions) {
-            should.exist(effectivePermissions);
+            createTestPermissions()
+                .then(permissions.init)
+                .then(function () {
+                    return UserProvider.findOne({id: 1});
+                })
+                .then(function (foundUser) {
+                    var canThisResult = permissions.canThis(foundUser);
 
-            effectivePermissions.length.should.be.above(0);
+                    should.exist(canThisResult.edit);
+                    should.exist(canThisResult.edit.post);
 
-            done();
-        }).catch(done);
-    });
+                    return canThisResult.edit.page(fakePage);
+                })
+                .then(function () {
+                    errors.logError(new Error("Allowed edit post without permission"));
+                }).catch(done);
+        });
 
-    it('can check an apps effective permissions', function (done) {
-        effectivePerms.app('Kudos')
-            .then(function (effectivePermissions) {
+        it('allows edit post with permission', function (done) {
+            var fakePost = {
+                    id: "1"
+                };
+
+            createTestPermissions()
+                .then(permissions.init)
+                .then(function () {
+                    return UserProvider.findOne({id: 1});
+                })
+                .then(function (foundUser) {
+                    var newPerm = new Models.Permission({
+                        name: "test3 edit post",
+                        action_type: "edit",
+                        object_type: "post"
+                    });
+
+                    return newPerm.save(null, {user: 1}).then(function () {
+                        return foundUser.permissions().attach(newPerm);
+                    });
+                })
+                .then(function () {
+                    return UserProvider.findOne({id: 1}, { withRelated: ['permissions']});
+                })
+                .then(function (updatedUser) {
+
+                    // TODO: Verify updatedUser.related('permissions') has the permission?
+                    var canThisResult = permissions.canThis(updatedUser.id);
+
+                    should.exist(canThisResult.edit);
+                    should.exist(canThisResult.edit.post);
+
+                    return canThisResult.edit.post(fakePost);
+                })
+                .then(function () {
+                    done();
+                }).catch(done);
+        });
+
+        it('can use permissable function on Model to allow something', function (done) {
+            var testUser,
+                permissableStub = sandbox.stub(PostProvider, 'permissable', function () {
+                    return when.resolve();
+                });
+
+            testUtils.insertAuthorUser()
+                .then(function () {
+                    return UserProvider.findAll();
+                })
+                .then(function (foundUser) {
+                    testUser = foundUser.models[1];
+
+                    return permissions.canThis({user: testUser.id}).edit.post(123);
+                })
+                .then(function () {
+                    permissableStub.restore();
+                    permissableStub.calledWith(123, { user: testUser.id, app: null, internal: false }).should.equal(true);
+
+                    done();
+                })
+                .catch(function () {
+                    permissableStub.restore();
+                    errors.logError(new Error("Did not allow testUser"));
+
+                    done();
+                });
+        });
+
+        it('can use permissable function on Model to forbid something', function (done) {
+            var testUser,
+                permissableStub = sandbox.stub(PostProvider, 'permissable', function () {
+                    return when.reject();
+                });
+
+            testUtils.insertAuthorUser()
+                .then(function () {
+                    return UserProvider.findAll();
+                })
+                .then(function (foundUser) {
+                    testUser = foundUser.models[1];
+
+                    return permissions.canThis({user: testUser.id}).edit.post(123);
+                })
+                .then(function () {
+
+                    permissableStub.restore();
+                    done(new Error("Allowed testUser to edit post"));
+                })
+                .catch(function () {
+                    permissableStub.calledWith(123, { user: testUser.id, app: null, internal: false }).should.equal(true);
+                    permissableStub.restore();
+                    done();
+                });
+        });
+
+        it("can get effective user permissions", function (done) {
+            effectivePerms.user(1).then(function (effectivePermissions) {
                 should.exist(effectivePermissions);
 
                 effectivePermissions.length.should.be.above(0);
 
                 done();
-            })
-            .catch(done);
-    });
+            }).catch(done);
+        });
 
-    it('does not allow an app to edit a post without permission', function (done) {
-        // Change the author of the post so the author override doesn't affect the test
-        PostProvider.edit({id: 1, 'author_id': 2})
-            .then(function (updatedPost) {
-                // Add user permissions
-                return UserProvider.findOne({id: 1})
-                    .then(function (foundUser) {
-                        var newPerm = new Models.Permission({
-                            name: "app test edit post",
-                            action_type: "edit",
-                            object_type: "post"
-                        });
+        it('can check an apps effective permissions', function (done) {
+            effectivePerms.app('Kudos')
+                .then(function (effectivePermissions) {
+                    should.exist(effectivePermissions);
 
-                        return newPerm.save(null, {user: 1}).then(function () {
-                            return foundUser.permissions().attach(newPerm).then(function () {
-                                return when.all([updatedPost, foundUser]);
+                    effectivePermissions.length.should.be.above(0);
+
+                    done();
+                })
+                .catch(done);
+        });
+
+        it('does not allow an app to edit a post without permission', function (done) {
+            // Change the author of the post so the author override doesn't affect the test
+            PostProvider.edit({'author_id': 2}, {id: 1})
+                .then(function (updatedPost) {
+                    // Add user permissions
+                    return UserProvider.findOne({id: 1})
+                        .then(function (foundUser) {
+                            var newPerm = new Models.Permission({
+                                name: "app test edit post",
+                                action_type: "edit",
+                                object_type: "post"
+                            });
+
+                            return newPerm.save(null, {user: 1}).then(function () {
+                                return foundUser.permissions().attach(newPerm).then(function () {
+                                    return when.all([updatedPost, foundUser]);
+                                });
                             });
                         });
-                    });
-            })
-            .then(function (results) {
-                var updatedPost = results[0],
-                    updatedUser = results[1];
+                })
+                .then(function (results) {
+                    var updatedPost = results[0],
+                        updatedUser = results[1];
 
-                return permissions.canThis({ user: updatedUser.id })
-                    .edit
-                    .post(updatedPost.id)
-                    .then(function () {
-                        return results;
-                    })
-                    .catch(function (err) {
-                        done(new Error("Did not allow user 1 to edit post 1"));
-                    });
-            })
-            .then(function (results) {
-                var updatedPost = results[0],
-                    updatedUser = results[1];
+                    return permissions.canThis({ user: updatedUser.id })
+                        .edit
+                        .post(updatedPost.id)
+                        .then(function () {
+                            return results;
+                        })
+                        .catch(function (err) {
+                            done(new Error("Did not allow user 1 to edit post 1"));
+                        });
+                })
+                .then(function (results) {
+                    var updatedPost = results[0],
+                        updatedUser = results[1];
 
-                // Confirm app cannot edit it.
-                return permissions.canThis({ app: 'Hemingway', user: updatedUser.id })
-                    .edit
-                    .post(updatedPost.id)
-                    .then(function () {
-                        done(new Error("Allowed an edit of post 1"));
-                    })
-                    .catch(function () {
-                        done();
-                    });
-            }).catch(done);
-    });
+                    // Confirm app cannot edit it.
+                    return permissions.canThis({ app: 'Hemingway', user: updatedUser.id })
+                        .edit
+                        .post(updatedPost.id)
+                        .then(function () {
+                            done(new Error("Allowed an edit of post 1"));
+                        })
+                        .catch(function () {
+                            done();
+                        });
+                }).catch(done);
+        });
 
-    it('allows an app to edit a post with permission', function (done) {
-        permissions.canThis({ app: 'Kudos', user: 1 })
-            .edit
-            .post(1)
-            .then(function () {
-                done();
-            })
-            .catch(function () {
-                done(new Error("Allowed an edit of post 1"));
-            });
-    });
+        it('allows an app to edit a post with permission', function (done) {
+            permissions.canThis({ app: 'Kudos', user: 1 })
+                .edit
+                .post(1)
+                .then(function () {
+                    done();
+                })
+                .catch(function () {
+                    done(new Error("Allowed an edit of post 1"));
+                });
+        });
 
-    it('checks for null context passed and rejects', function (done) {
-        permissions.canThis(undefined)
-            .edit
-            .post(1)
-            .then(function () {
-                done(new Error("Should not allow editing post"));
-            })
-            .catch(function () {
-                done();
-            });
-    });
+        it('checks for null context passed and rejects', function (done) {
+            permissions.canThis(undefined)
+                .edit
+                .post(1)
+                .then(function () {
+                    done(new Error("Should not allow editing post"));
+                })
+                .catch(function () {
+                    done();
+                });
+        });
 
-    it('allows \'internal\' to be passed for internal requests', function (done) {
-        // Using tag here because post implements the custom permissable interface
-        permissions.canThis('internal')
-            .edit
-            .tag(1)
-            .then(function () {
-                done();
-            })
-            .catch(function () {
-                done(new Error("Should allow editing post with 'internal'"));
-            });
-    });
+        it('allows \'internal\' to be passed for internal requests', function (done) {
+            // Using tag here because post implements the custom permissable interface
+            permissions.canThis('internal')
+                .edit
+                .tag(1)
+                .then(function () {
+                    done();
+                })
+                .catch(function () {
+                    done(new Error("Should allow editing post with 'internal'"));
+                });
+        });
 
-    it('allows { internal: true } to be passed for internal requests', function (done) {
-        // Using tag here because post implements the custom permissable interface
-        permissions.canThis({ internal: true })
-            .edit
-            .tag(1)
-            .then(function () {
-                done();
-            })
-            .catch(function () {
-                done(new Error("Should allow editing post with { internal: true }"));
-            });
+        it('allows { internal: true } to be passed for internal requests', function (done) {
+            // Using tag here because post implements the custom permissable interface
+            permissions.canThis({ internal: true })
+                .edit
+                .tag(1)
+                .then(function () {
+                    done();
+                })
+                .catch(function () {
+                    done(new Error("Should allow editing post with { internal: true }"));
+                });
+        });
     });
 });
\ No newline at end of file
diff --git a/core/test/utils/api.js b/core/test/utils/api.js
index 780ecdd46b..b0321cd406 100644
--- a/core/test/utils/api.js
+++ b/core/test/utils/api.js
@@ -10,7 +10,7 @@ var url = require('url'),
         post: ['id', 'uuid', 'title', 'slug', 'markdown', 'html', 'meta_title', 'meta_description',
             'featured', 'image', 'status', 'language', 'created_at', 'created_by', 'updated_at',
             'updated_by', 'published_at', 'published_by', 'page', 'author', 'tags', 'fields'],
-        settings: ['settings'],
+        settings: ['settings', 'meta'],
         setting: ['id', 'uuid', 'key', 'value', 'type', 'created_at', 'created_by', 'updated_at', 'updated_by'],
         tag: ['id', 'uuid', 'name', 'slug', 'description', 'parent',
             'meta_title', 'meta_description', 'created_at', 'created_by', 'updated_at', 'updated_by'],