Create v2 management API
diff --git a/conf.d/management_apis.conf b/conf.d/management_apis.conf
index 14b1a4e..6f1e953 100644
--- a/conf.d/management_apis.conf
+++ b/conf.d/management_apis.conf
@@ -33,23 +33,30 @@
     access_log /var/log/api-gateway/access.log main;
     error_log /var/log/api-gateway/management.log debug;
 
-    location ~ /v1/apis/?(?<api_id>[^/]*)?/?(?<query>[^/]*)? {
+    location ~ /(?<version>v2)/(?<tenantId>[^/]*)/apis/?(?<api_id>[^/]*)?/?(?<query>[^/]*)? {
         access_by_lua_block {
-            local apis = require("management/apis")
+            local apis = require("management/routes/apis")
+            apis.requestHandler()
+        }
+    }
+
+    location ~ /(?<version>v1)/apis/?(?<api_id>[^/]*)?/?(?<query>[^/]*)? {
+        access_by_lua_block {
+            local apis = require("management/routes/apis")
             apis.requestHandler()
         }
     }
 
     location ~ /v1/tenants/?(?<tenant_id>[^/]*)?/?(?<query>[^/]*)? {
         access_by_lua_block {
-            local tenants = require("management/tenants")
+            local tenants = require("management/routes/tenants")
             tenants.requestHandler()
         }
     }
 
     location /v1/subscriptions {
          access_by_lua_block {
-             local subscriptions = require("management/subscriptions")
+             local subscriptions = require("management/routes/subscriptions")
              subscriptions.requestHandler()
          }
     }
diff --git a/scripts/lua/lib/redis.lua b/scripts/lua/lib/redis.lua
index 826f19e..36fa4f7 100644
--- a/scripts/lua/lib/redis.lua
+++ b/scripts/lua/lib/redis.lua
@@ -116,7 +116,7 @@
   if not ok then
     request.err(500, utils.concatStrings({"Failed to save the API: ", err}))
   end
-  return apiObj
+  return cjson.decode(apiObj)
 end
 
 --- Get all APIs from redis
@@ -371,4 +371,39 @@
   request.success(200,  "Status: Gateway ready.")
 end
 
+-----------------------------
+-------- v2 Swagger ---------
+-----------------------------
+
+function _M.addSwagger(red, id, swagger)
+  swagger = cjson.encode(swagger)
+  local ok, err = red:hset("swagger", id, swagger)
+  if not ok then
+    request.err(500, utils.concatStrings({"Failed to add swagger: ", err}))
+  end
+  return cjson.decode(swagger)
+end
+
+function _M.getSwagger(red, id)
+  local swagger, err = red:hget("swagger", id)
+  if not swagger then
+    request.err(500, utils.concatStrings({"Failed to add swagger: ", err}))
+  end
+  if swagger == ngx.null then
+    return nil
+  end
+  return cjson.decode(swagger)
+end
+
+function _M.deleteSwagger(red, id)
+  local existing = _M.getSwagger(red, id)
+  if existing == nil then
+    request.err(404, 'Swagger doesn\'t exist')
+  end
+  local ok, err = red:hdel("swagger", id)
+  if not ok then
+    request.err(500, utils.concatStrings({"Failed to delete swagger: ", err}))
+  end
+end
+
 return _M
diff --git a/scripts/lua/lib/request.lua b/scripts/lua/lib/request.lua
index 9a61a3e..f28e708 100644
--- a/scripts/lua/lib/request.lua
+++ b/scripts/lua/lib/request.lua
@@ -23,6 +23,7 @@
 -- @author Alex Song (songs)
 
 local utils = require "lib/utils"
+local cjson = require "cjson"
 
 local _Request = {}
 
@@ -30,17 +31,24 @@
 -- @param code error code
 -- @param msg error message
 function err(code, msg)
+  ngx.header.content_type = "application/json; charset=utf-8"
   ngx.status = code
-  ngx.say(utils.concatStrings({"Error: ", msg}))
+  local errObj = cjson.encode({
+    status = code,
+    message = utils.concatStrings({"Error: ", msg})
+  })
+  ngx.say(errObj)
   ngx.exit(ngx.status)
 end
 
 --- Function to call when request is successful
 -- @param code status code
--- @param obj object to return
+-- @param obj JSON encoded object to return
 function success(code, obj)
   ngx.status = code
-  ngx.say(obj)
+  if obj ~= nil then
+    ngx.say(obj)
+  end
   ngx.exit(ngx.status)
 end
 
diff --git a/scripts/lua/management/apis.lua b/scripts/lua/management/apis.lua
deleted file mode 100644
index 53a0806..0000000
--- a/scripts/lua/management/apis.lua
+++ /dev/null
@@ -1,383 +0,0 @@
--- Copyright (c) 2016 IBM. All rights reserved.
---
---   Permission is hereby granted, free of charge, to any person obtaining a
---   copy of this software and associated documentation files (the "Software"),
---   to deal in the Software without restriction, including without limitation
---   the rights to use, copy, modify, merge, publish, distribute, sublicense,
---   and/or sell copies of the Software, and to permit persons to whom the
---   Software is furnished to do so, subject to the following conditions:
---
---   The above copyright notice and this permission notice shall be included in
---   all copies or substantial portions of the Software.
---
---   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
---   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
---   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
---   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
---   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
---   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
---   DEALINGS IN THE SOFTWARE.
-
---- @module apis
--- Management interface for apis for the gateway
-
-local cjson = require "cjson"
-local redis = require "lib/redis"
-local utils = require "lib/utils"
-local request = require "lib/request"
-local resources = require "management/resources"
-
-local MANAGEDURL_HOST = os.getenv("PUBLIC_MANAGEDURL_HOST")
-MANAGEDURL_HOST = (MANAGEDURL_HOST ~= nil and MANAGEDURL_HOST ~= '') and MANAGEDURL_HOST or "0.0.0.0"
-local MANAGEDURL_PORT = os.getenv("PUBLIC_MANAGEDURL_PORT")
-MANAGEDURL_PORT = (MANAGEDURL_PORT ~= nil and MANAGEDURL_PORT ~= '') and MANAGEDURL_PORT or "8080"
-local REDIS_HOST = os.getenv("REDIS_HOST")
-local REDIS_PORT = os.getenv("REDIS_PORT")
-local REDIS_PASS = os.getenv("REDIS_PASS")
-
-local _M = {}
-
---- Request handler for routing API calls appropriately
-function _M.requestHandler()
-  local requestMethod = ngx.req.get_method()
-  if requestMethod == "GET" then
-    getAPIs()
-  elseif requestMethod == "PUT" then
-    addAPI()
-  elseif requestMethod == "POST" then
-    addAPI()
-  elseif requestMethod == "DELETE" then
-    deleteAPI()
-  else
-    ngx.status = 400
-    ngx.say("Invalid verb")
-  end
-end
-
---- Add an api to the Gateway
--- PUT /v1/apis
--- body:
--- {
---    "name": *(String) name of API
---    "basePath": *(String) base path for api
---    "tenantId": *(String) tenant id
---    "resources": *(String) resources to add
--- }
-function addAPI()
-  -- Open connection to redis or use one from connection pool
-  local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 10000)
-  -- Check for api id from uri and use existingAPI if it already exists in redis
-  local existingAPI = checkForExistingAPI(red)
-  -- Read in the PUT JSON Body
-  ngx.req.read_body()
-  local args = ngx.req.get_body_data()
-  if not args then
-    request.err(400, "Missing request body")
-  end
-  -- Convert json into Lua table
-  local decoded = cjson.decode(args)
-  -- Check for api id in JSON body
-  if existingAPI == nil and decoded.id ~= nil then
-    existingAPI = redis.getAPI(red, decoded.id)
-    if existingAPI == nil then
-      request.err(404, utils.concatStrings({"Unknown API id ", decoded.id}))
-    end
-  end
-  -- Error checking
-  local fields = {"name", "basePath", "tenantId", "resources"}
-  for k, v in pairs(fields) do
-    local res, err = isValid(red, v, decoded[v])
-    if res == false then
-      request.err(err.statusCode, err.message)
-    end
-  end
-  -- Format basePath
-  local basePath = decoded.basePath:sub(1,1) == '/' and decoded.basePath:sub(2) or decoded.basePath
-  basePath = basePath:sub(-1) == '/' and basePath:sub(1, -2) or basePath
-  -- Create managedUrl object
-  local uuid = existingAPI ~= nil and existingAPI.id or utils.uuid()
-  local managedUrl = utils.concatStrings({"http://", MANAGEDURL_HOST, ":", MANAGEDURL_PORT, "/api/", decoded.tenantId})
-  if basePath:sub(1,1) ~= '' then
-    managedUrl = utils.concatStrings({managedUrl, "/", basePath})
-  end
-  local tenantObj = redis.getTenant(red, decoded.tenantId)
-  local managedUrlObj = {
-    id = uuid,
-    name = decoded.name,
-    basePath = utils.concatStrings({"/", basePath}),
-    tenantId = decoded.tenantId,
-    tenantNamespace = tenantObj.namespace,
-    tenantInstance = tenantObj.instance,
-    cors = decoded.cors, 
-    resources = decoded.resources,
-    managedUrl = managedUrl
-  }
-  -- Add API object to redis
-  managedUrlObj = redis.addAPI(red, uuid, managedUrlObj, existingAPI)
-  -- Add resources to redis
-  for path, resource in pairs(decoded.resources) do
-    local gatewayPath = utils.concatStrings({basePath, path})
-    gatewayPath = (gatewayPath:sub(1,1) == '/') and gatewayPath:sub(2) or gatewayPath
-    resources.addResource(red, resource, gatewayPath, tenantObj)
-  end
-  redis.close(red)
-  -- Return managed url object
-  ngx.header.content_type = "application/json; charset=utf-8"
-  request.success(200, managedUrlObj)
-end
-
---- Check for api id from uri and use existing API if it already exists in redis
--- @param red Redis client instance
-function checkForExistingAPI(red)
-  local id = ngx.var.api_id
-  local existing
-  -- Get object from redis
-  if id ~= nil and id ~= '' then
-    existing = redis.getAPI(red, id)
-    if existing == nil then
-      request.err(404, utils.concatStrings({"Unknown API id ", id}))
-    end
-  end
-  return existing
-end
-
---- Check JSON body fields for errors
--- @param red Redis client instance
--- @param field name of field
--- @param object field object
-function isValid(red, field, object)
-  -- Check that field exists in body
-  if not object then
-    return false, { statusCode = 400, message = utils.concatStrings({"Missing field '", field, "' in request body."}) }
-  end
-  -- Additional check for basePath
-  if field == "basePath" then
-    local basePath = object
-    if string.match(basePath, "'") then
-      return false, { statusCode = 400, message = "basePath contains illegal character \"'\"." }
-    end
-  end
-  -- Additional check for tenantId
-  if field == "tenantId" then
-    local tenant = redis.getTenant(red, object)
-    if tenant == nil then
-      return false, { statusCode = 404, message = utils.concatStrings({"Unknown tenant id ", object }) }
-    end
-  end
-  if field == "resources" then
-    local res, err = checkResources(object)
-    if res ~= nil and res == false then
-      return res, err
-    end
-  end
-  -- All error checks passed
-  return true
-end
-
---- Error checking for resources
--- @param resources resources object
-function checkResources(resources)
-  if next(resources) == nil then
-    return false, { statusCode = 400, message = "Empty resources object." }
-  end
-  for path, resource in pairs(resources) do
-    -- Check resource path for illegal characters
-    if string.match(path, "'") then
-      return false, { statusCode = 400, message = "resource path contains illegal character \"'\"." }
-    end
-    -- Check that resource path begins with slash
-    if path:sub(1,1) ~= '/' then
-      return false, { statusCode = 400, message = "Resource path must begin with '/'." }
-    end
-    -- Check operations object
-    local res, err = checkOperations(resource.operations)
-    if res ~= nil and res == false then
-      return res, err
-    end
-  end
-end
-
---- Error checking for operations
--- @param operations operations object
-function checkOperations(operations)
-  if not operations or next(operations) == nil then
-    return false, { statusCode = 400, message = "Missing or empty field 'operations' or in resource path object." }
-  end
-  local allowedVerbs = {GET=true, POST=true, PUT=true, DELETE=true, PATCH=true, HEAD=true, OPTIONS=true}
-  for verb, verbObj in pairs(operations) do
-    if allowedVerbs[verb:upper()] == nil then
-      return false, { statusCode = 400, message = utils.concatStrings({"Resource verb '", verb, "' not supported."}) }
-    end
-    -- Check required fields
-    local requiredFields = {"backendMethod", "backendUrl"}
-    for k, v in pairs(requiredFields) do
-      if verbObj[v] == nil then
-        return false, { statusCode = 400, message = utils.concatStrings({"Missing field '", v, "' for '", verb, "' operation."}) }
-      end
-      if v == "backendMethod" then
-        local backendMethod = verbObj[v]
-        if allowedVerbs[backendMethod:upper()] == nil then
-          return false, { statusCode = 400, message = utils.concatStrings({"backendMethod '", backendMethod, "' not supported."}) }
-        end
-      end
-    end
-    -- Check optional fields
-    local res, err = checkOptionalPolicies(verbObj.policies, verbObj.security)
-    if res ~= nil and res == false then
-      return res, err
-    end
-  end
-end
-
---- Error checking for policies and security
--- @param policies policies object
--- @param security security object
-function checkOptionalPolicies(policies, security)
-  if policies then
-    for _, v in pairs(policies) do
-      local validTypes = {"reqMapping", "rateLimit", "backendRouting"}
-      if (v.type == nil or v.value == nil) then
-        return false, { statusCode = 400, message = "Missing field in policy object. Need \"type\" and \"value\"." }
-      elseif utils.tableContains(validTypes, v.type) == false then
-        return false, { statusCode = 400, message = "Invalid type in policy object. Valid types: " .. cjson.encode(validTypes) }
-      end
-    end
-  end
-  if security then
-    for _, sec in ipairs(security) do
-      local validScopes = {"tenant", "api", "resource"}
-      if (sec.type == nil or sec.scope == nil) then
-        return false, { statusCode = 400, message = "Missing field in security object. Need \"type\" and \"scope\"." }
-      elseif utils.tableContains(validScopes, sec.scope) == false == nil then
-        return false, { statusCode = 400, message = "Invalid scope in security object. Valid scopes:" .. cjson.encode(validScopes) }
-      end
-    end 
-  end
-end
-
---- Get one or all APIs from the gateway
--- GET /v1/apis
-function getAPIs()
-  local queryParams = ngx.req.get_uri_args()
-  local id = ngx.var.api_id
-  if id == nil or id == '' then
-    getAllAPIs(queryParams)
-  else
-    local query = ngx.var.query
-    if (query ~= nil and query ~= '') then
-      if query ~= "tenant" then
-        request.err(400, "Invalid request")
-      else
-        getAPITenant(id)
-      end
-    else
-      getAPI(id)
-    end
-  end
-end
-
---- Get all APIs in redis
--- @param queryParams object containing optional query parameters
-function getAllAPIs(queryParams)
-  local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 10000)
-  local apis = redis.getAllAPIs(red)
-  redis.close(red)
-  local apiList
-  if next(queryParams) ~= nil then
-    apiList = filterAPIs(apis, queryParams);
-  end
-  if apiList == nil then
-    apiList = {}
-    for k, v in pairs(apis) do
-      if k%2 == 0 then
-        apiList[#apiList+1] = cjson.decode(v)
-      end
-    end
-  end
-  ngx.header.content_type = "application/json; charset=utf-8"
-  apiList = (next(apiList) == nil) and "[]" or cjson.encode(apiList)
-  request.success(200, apiList)
-end
-
---- Filter APIs based on query parameters
--- @param apis list of apis
--- @param queryParams query parameters to filter tenants
-function filterAPIs(apis, queryParams)
-  local basePath = queryParams['filter[where][basePath]']
-  local name = queryParams['filter[where][name]']
-  -- missing or invalid query parameters
-  if (basePath == nil and name == nil) then
-    return nil
-  end
-  -- filter tenants
-  local apiList = {}
-  for k, v in pairs(apis) do
-    if k%2 == 0 then
-      local api = cjson.decode(v)
-      if (basePath ~= nil and name == nil and api.basePath == basePath) or
-         (name ~= nil and basePath == nil and api.name == name) or
-         (basePath ~= nil and name ~= nil and api.basePath == basePath and api.name == name) then
-       apiList[#apiList+1] = api
-      end
-    end
-  end
-  return apiList
-end
-
---- Get API by its id
--- @param id of API
-function getAPI(id)
-  local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 10000)
-  local api = redis.getAPI(red, id)
-  if api == nil then
-    request.err(404, utils.concatStrings({"Unknown api id ", id}))
-  end
-  redis.close(red)
-  ngx.header.content_type = "application/json; charset=utf-8"
-  request.success(200, cjson.encode(api))
-end
-
---- Get belongsTo relation tenant
--- @param id id of API
-function getAPITenant(id)
-  local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 10000)
-  local api = redis.getAPI(red, id)
-  if api == nil then
-    request.err(404, utils.concatStrings({"Unknown api id ", id}))
-  end
-  local tenantId = api.tenantId
-  local tenant = redis.getTenant(red, tenantId)
-  if tenant == nil then
-    request.err(404, utils.concatStrings({"Unknown tenant id ", tenantId}))
-  end
-  redis.close(red)
-  ngx.header.content_type = "application/json; charset=utf-8"
-  request.success(200, cjson.encode(tenant))
-end
-
---- Delete API from gateway
--- DELETE /v1/apis/<id>
-function deleteAPI()
-  local id = ngx.var.api_id
-  if id == nil or id == '' then
-    request.err(400, "No id specified.")
-  end
-  local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 10000)
-  local api = redis.getAPI(red, id)
-  if api == nil then
-    request.err(404, utils.concatStrings({"Unknown API id ", id}))
-  end
-  -- Delete API
-  redis.deleteAPI(red, id)
-  -- Delete all resources for the API
-  local basePath = api.basePath:sub(2)
-  for path, v in pairs(api.resources) do
-    local gatewayPath = utils.concatStrings({basePath, path})
-    gatewayPath = (gatewayPath:sub(1,1) == '/') and gatewayPath:sub(2) or gatewayPath
-    resources.deleteResource(red, gatewayPath, api.tenantId)
-  end
-  redis.close(red)
-  request.success(200, {})
-end
-
-return _M;
diff --git a/scripts/lua/management/lib/apis.lua b/scripts/lua/management/lib/apis.lua
new file mode 100644
index 0000000..d09fef4
--- /dev/null
+++ b/scripts/lua/management/lib/apis.lua
@@ -0,0 +1,169 @@
+-- Copyright (c) 2016 IBM. All rights reserved.
+--
+--   Permission is hereby granted, free of charge, to any person obtaining a
+--   copy of this software and associated documentation files (the "Software"),
+--   to deal in the Software without restriction, including without limitation
+--   the rights to use, copy, modify, merge, publish, distribute, sublicense,
+--   and/or sell copies of the Software, and to permit persons to whom the
+--   Software is furnished to do so, subject to the following conditions:
+--
+--   The above copyright notice and this permission notice shall be included in
+--   all copies or substantial portions of the Software.
+--
+--   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+--   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+--   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+--   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+--   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+--   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+--   DEALINGS IN THE SOFTWARE.
+
+--- @module apis
+-- Module for querying APIs
+
+local cjson = require "cjson"
+local redis = require "lib/redis"
+local utils = require "lib/utils"
+local request = require "lib/request"
+local resources = require "management/lib/resources"
+
+local MANAGEDURL_HOST = os.getenv("PUBLIC_MANAGEDURL_HOST")
+MANAGEDURL_HOST = (MANAGEDURL_HOST ~= nil and MANAGEDURL_HOST ~= '') and MANAGEDURL_HOST or "0.0.0.0"
+local MANAGEDURL_PORT = os.getenv("PUBLIC_MANAGEDURL_PORT")
+MANAGEDURL_PORT = (MANAGEDURL_PORT ~= nil and MANAGEDURL_PORT ~= '') and MANAGEDURL_PORT or "8080"
+
+local _M = {}
+
+--- Get all APIs in redis
+-- @param red redis client
+-- @param queryParams object containing optional query parameters
+function _M.getAllAPIs(red, queryParams)
+  local apis = redis.getAllAPIs(red)
+  local apiList
+  if next(queryParams) ~= nil then
+    apiList = filterAPIs(apis, queryParams);
+  end
+  if apiList == nil then
+    apiList = {}
+    for k, v in pairs(apis) do
+      if k%2 == 0 then
+        apiList[#apiList+1] = cjson.decode(v)
+      end
+    end
+  end
+  return apiList
+end
+
+--- Get API by its id
+-- @param red redis client
+-- @param id of API
+function _M.getAPI(red, id)
+  local api = redis.getAPI(red, id)
+  if api == nil then
+    request.err(404, utils.concatStrings({"Unknown api id ", id}))
+  end
+  return api
+end
+
+--- Get belongsTo relation tenant
+-- @param red redis client
+-- @param id id of API
+function _M.getAPITenant(red, id)
+  local api = redis.getAPI(red, id)
+  if api == nil then
+    request.err(404, utils.concatStrings({"Unknown api id ", id}))
+  end
+  local tenantId = api.tenantId
+  local tenant = redis.getTenant(red, tenantId)
+  if tenant == nil then
+    request.err(404, utils.concatStrings({"Unknown tenant id ", tenantId}))
+  end
+  return tenant
+end
+
+--- Add API to redis
+-- @param red redis client
+-- @param decoded JSON body as a lua table
+-- @param existingAPI optional existing API for updates
+function _M.addAPI(red, decoded, existingAPI)
+  -- Format basePath
+  local basePath = decoded.basePath:sub(1,1) == '/' and decoded.basePath:sub(2) or decoded.basePath
+  basePath = basePath:sub(-1) == '/' and basePath:sub(1, -2) or basePath
+  -- Create managedUrl object
+  local uuid = existingAPI ~= nil and existingAPI.id or utils.uuid()
+  local managedUrl = utils.concatStrings({"http://", MANAGEDURL_HOST, ":", MANAGEDURL_PORT, "/api/", decoded.tenantId})
+  if basePath:sub(1,1) ~= '' then
+    managedUrl = utils.concatStrings({managedUrl, "/", basePath})
+  end
+  local tenantObj = redis.getTenant(red, decoded.tenantId)
+  local managedUrlObj = {
+    id = uuid,
+    name = decoded.name,
+    basePath = utils.concatStrings({"/", basePath}),
+    tenantId = decoded.tenantId,
+    tenantNamespace = tenantObj.namespace,
+    tenantInstance = tenantObj.instance,
+    cors = decoded.cors,
+    resources = decoded.resources,
+    managedUrl = managedUrl
+  }
+  -- Add API object to redis
+  managedUrlObj = redis.addAPI(red, uuid, managedUrlObj, existingAPI)
+  -- Add resources to redis
+  for path, resource in pairs(decoded.resources) do
+    local gatewayPath = utils.concatStrings({basePath, path})
+    gatewayPath = (gatewayPath:sub(1,1) == '/') and gatewayPath:sub(2) or gatewayPath
+    resource.apiId = uuid
+    resources.addResource(red, resource, gatewayPath, tenantObj)
+  end
+  return managedUrlObj
+end
+
+--- Delete API from gateway
+-- @param red redis client
+-- @param id id of API to delete
+function _M.deleteAPI(red, id)
+  local api = redis.getAPI(red, id)
+  if api == nil then
+    request.err(404, utils.concatStrings({"Unknown API id ", id}))
+  end
+  -- Delete API
+  redis.deleteAPI(red, id)
+  -- Delete all resources for the API
+  local basePath = api.basePath:sub(2)
+  for path in pairs(api.resources) do
+    local gatewayPath = utils.concatStrings({basePath, path})
+    gatewayPath = (gatewayPath:sub(1,1) == '/') and gatewayPath:sub(2) or gatewayPath
+    resources.deleteResource(red, gatewayPath, api.tenantId)
+  end
+  return {}
+end
+
+--- Filter APIs based on query parameters
+-- @param apis list of apis
+-- @param queryParams query parameters to filter tenants
+function filterAPIs(apis, queryParams)
+  local basePath = queryParams['filter[where][basePath]']
+  basePath = basePath == nil and queryParams['basePath'] or basePath
+  local name = queryParams['filter[where][name]']
+  name = name == nil and queryParams['title'] or name
+  -- missing or invalid query parameters
+  if (basePath == nil and name == nil) then
+    return nil
+  end
+  -- filter tenants
+  local apiList = {}
+  for k, v in pairs(apis) do
+    if k%2 == 0 then
+      local api = cjson.decode(v)
+      if (basePath ~= nil and name == nil and api.basePath == basePath) or
+          (name ~= nil and basePath == nil and api.name == name) or
+          (basePath ~= nil and name ~= nil and api.basePath == basePath and api.name == name) then
+        apiList[#apiList+1] = api
+      end
+    end
+  end
+  return apiList
+end
+
+return _M
\ No newline at end of file
diff --git a/scripts/lua/management/resources.lua b/scripts/lua/management/lib/resources.lua
similarity index 100%
rename from scripts/lua/management/resources.lua
rename to scripts/lua/management/lib/resources.lua
diff --git a/scripts/lua/management/lib/swagger.lua b/scripts/lua/management/lib/swagger.lua
new file mode 100644
index 0000000..90f24ed
--- /dev/null
+++ b/scripts/lua/management/lib/swagger.lua
@@ -0,0 +1,187 @@
+-- Copyright (c) 2016 IBM. All rights reserved.
+--
+--   Permission is hereby granted, free of charge, to any person obtaining a
+--   copy of this software and associated documentation files (the "Software"),
+--   to deal in the Software without restriction, including without limitation
+--   the rights to use, copy, modify, merge, publish, distribute, sublicense,
+--   and/or sell copies of the Software, and to permit persons to whom the
+--   Software is furnished to do so, subject to the following conditions:
+--
+--   The above copyright notice and this permission notice shall be included in
+--   all copies or substantial portions of the Software.
+--
+--   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+--   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+--   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+--   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+--   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+--   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+--   DEALINGS IN THE SOFTWARE.
+
+--- @module swagger
+-- Module for parsing swagger file
+
+local _M = {}
+
+-- Convert passed-in swagger body to valid lua table
+-- @param swagger swagger file to parse
+function _M.parseSwagger(swagger)
+  local backends = parseBackends(swagger)
+  local policies = parsePolicies(swagger)
+  local security = parseSecurity(swagger)
+  local decoded = {
+    name = swagger.info.title,
+    basePath = swagger.basePath,
+    resources = {}
+  }
+  for path, verbObj in pairs(swagger.paths) do
+    decoded.resources[path] = { operations = {} }
+    for verb, value in pairs(verbObj) do
+      decoded.resources[path].operations[verb] = {}
+      local verbObj = decoded.resources[path].operations[verb]
+      local backend = (backends["all"] ~= nil) and backends["all"] or backends[value.operationId]
+      verbObj.backendUrl = backend.backendUrl
+      verbObj.backendMethod = (backend.backendMethod == 'keep') and verb or backend.backendMethod
+      verbObj.policies = policies
+      verbObj.security = security
+    end
+  end
+  return decoded
+end
+
+--- Parse backendUrl and backendMethod
+-- @param swagger swagger file to parse
+function parseBackends(swagger)
+  local configObj = swagger["x-gateway-configuration"]
+  configObj = (configObj == nil) and swagger["x-ibm-configuration"] or configObj
+  if configObj ~= nil then
+    for _, obj in pairs(configObj.assembly.execute) do
+      for policy, v in pairs(obj) do
+        local res = {}
+        if policy == "operation-switch" then
+          local caseObj = v.case
+          for _, case in pairs(caseObj) do
+            for _, op in pairs(case.operations) do
+              res[op] = {
+                backendUrl = case.execute[1]["proxy"]["target-url"],
+                backendMethod = case.execute[1]["proxy"].verb
+              }
+            end
+          end
+          return res
+        end
+        if policy == "proxy" then
+          res["all"] = {
+            backendUrl = v["target-url"],
+            backendMethod = v.verb
+          }
+          return res
+        end
+      end
+    end
+  end
+end
+
+--- Parse policies in swagger
+-- @param swagger swagger file to parse
+function parsePolicies(swagger)
+  local policies = {}
+  -- parse rate limit
+  policies = parseRateLimit(swagger, policies)
+  policies = parseRequestMapping(swagger, policies)
+  return policies
+end
+
+--- Parse rate limit
+function parseRateLimit(swagger, policies)
+  local rlObj = swagger["x-gateway-rate-limit"]
+  rlObj = (rlObj == nil) and swagger["x-ibm-rate-limit"] or rlObj
+  if rlObj ~= nil then
+    rlObj = rlObj[1]
+    if rlObj.unit == "second" then
+      rlObj.unit = 1
+    elseif rlObj.unit == "minute" then
+      rlObj.unit = 60
+    elseif rlObj.unit == "hour" then
+      rlObj.unit = 3600
+    elseif rlObj.unit == "day" then
+      rlObj.unit = 86400
+    else
+      rlObj.unit = 60   -- default to minute
+    end
+    policies[#policies+1] = {
+      type = "rateLimit",
+      value = {
+        interval = rlObj.unit * rlObj.units,
+        rate = rlObj.rate,
+        scope = "api",
+        subscription = "true"
+      }
+    }
+  end
+  return policies
+end
+
+--- Parse request mapping
+function parseRequestMapping(swagger, policies)
+  local valueList = {}
+  if swagger["x-ibm-configuration"] ~= nil then
+    for _, obj in pairs(swagger["x-ibm-configuration"].assembly.execute) do
+      for policy, v in pairs(obj) do
+        if policy == "set-variable" then
+          for _, actionObj in pairs(v.actions) do
+            local fromValue = actionObj.value
+            local toParsedArray = {string.match(actionObj.set, "([^.]+).([^.]+).([^.]+)") }
+            local toName = toParsedArray[3]
+            local toLocation = toParsedArray[2]
+            toLocation = toLocation == "headers" and "header" or toLocation
+            valueList[#valueList+1] = {
+              action = "insert",
+              from = {
+                value = fromValue
+              },
+              to = {
+                name = toName,
+                location = toLocation
+              }
+            }
+          end
+        end
+      end
+    end
+  end
+  if next(valueList) ~= nil then
+    policies[#policies+1] ={
+      type = "reqMapping",
+      value = valueList
+    }
+  end
+  return policies
+end
+
+--- Parse security in swagger
+-- @param swagger swagger file to parse
+function parseSecurity(swagger)
+  local security = {}
+  if swagger["securityDefinitions"] ~= nil then
+    local secObject = swagger["securityDefinitions"]
+    for key, sec in pairs(secObject) do
+      if sec.type == 'apiKey' then
+        security[#security+1] = {
+          type = sec.type,
+          scope = "api",
+          header = sec.name
+        }
+      elseif sec.type == 'oauth2' then
+        security[#security+1] = {
+          type = sec.type,
+          scope = "api",
+          provider = key
+        }
+      end
+    end
+  end
+  return security
+end
+
+return _M
\ No newline at end of file
diff --git a/scripts/lua/management/lib/tenants.lua b/scripts/lua/management/lib/tenants.lua
new file mode 100644
index 0000000..97098db
--- /dev/null
+++ b/scripts/lua/management/lib/tenants.lua
@@ -0,0 +1,162 @@
+-- Copyright (c) 2016 IBM. All rights reserved.
+--
+--   Permission is hereby granted, free of charge, to any person obtaining a
+--   copy of this software and associated documentation files (the "Software"),
+--   to deal in the Software without restriction, including without limitation
+--   the rights to use, copy, modify, merge, publish, distribute, sublicense,
+--   and/or sell copies of the Software, and to permit persons to whom the
+--   Software is furnished to do so, subject to the following conditions:
+--
+--   The above copyright notice and this permission notice shall be included in
+--   all copies or substantial portions of the Software.
+--
+--   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+--   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+--   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+--   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+--   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+--   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+--   DEALINGS IN THE SOFTWARE.
+
+--- @module tenants
+-- Management interface for tenants for the gateway
+
+local cjson = require "cjson"
+local redis = require "lib/redis"
+local utils = require "lib/utils"
+local request = require "lib/request"
+local apis = require "management/lib/apis"
+
+local _M = {};
+
+function _M.addTenant(red, decoded, existingTenant)
+  -- Return tenant object
+  local uuid = existingTenant ~= nil and existingTenant.id or utils.uuid()
+  local tenantObj = {
+    id = uuid,
+    namespace = decoded.namespace,
+    instance = decoded.instance
+  }
+  tenantObj = redis.addTenant(red, uuid, tenantObj)
+  return cjson.decode(tenantObj)
+end
+
+--- Get all tenants in redis
+-- @param red redis client
+-- @param queryParams object containing optional query parameters
+function _M.getAllTenants(red, queryParams)
+  local tenants = redis.getAllTenants(red)
+  local tenantList
+  if next(queryParams) ~= nil then
+    tenantList = filterTenants(tenants, queryParams);
+  end
+  if tenantList == nil then
+    tenantList = {}
+    for k, v in pairs(tenants) do
+      if k%2 == 0 then
+        tenantList[#tenantList+1] = cjson.decode(v)
+      end
+    end
+  end
+  return tenantList
+end
+
+--- Filter tenants based on query parameters
+-- @param tenants list of tenants
+-- @param queryParams query parameters to filter tenants
+function filterTenants(tenants, queryParams)
+  local namespace = queryParams['filter[where][namespace]']
+  local instance = queryParams['filter[where][instance]']
+  -- missing or invalid query parameters
+  if (namespace == nil and instance == nil) or (instance ~= nil and namespace == nil) then
+    return nil
+  end
+  -- filter tenants
+  local tenantList = {}
+  for k, v in pairs(tenants) do
+    if k%2 == 0 then
+      local tenant = cjson.decode(v)
+      if (namespace ~= nil and instance == nil and tenant.namespace == namespace) or
+          (namespace ~= nil and instance ~= nil and tenant.namespace == namespace and tenant.instance == instance) then
+        tenantList[#tenantList+1] = tenant
+      end
+    end
+  end
+  return tenantList
+end
+
+--- Get tenant by its id
+-- @param red redis client
+-- @param id tenant id
+function _M.getTenant(red, id)
+  local tenant = redis.getTenant(red, id)
+  if tenant == nil then
+    request.err(404, utils.concatStrings({"Unknown tenant id ", id }))
+  end
+  return tenant
+end
+
+--- Get APIs associated with tenant
+-- @param red redis client
+-- @param id tenant id
+-- @param queryParams object containing optional query parameters
+function _M.getTenantAPIs(red, id, queryParams)
+  local apis = redis.getAllAPIs(red)
+  local apiList
+  if next(queryParams) ~= nil then
+    apiList = filterTenantAPIs(id, apis, queryParams);
+  end
+  if apiList == nil then
+    apiList = {}
+    for k, v in pairs(apis) do
+      if k%2 == 0 then
+        local decoded = cjson.decode(v)
+        if decoded.tenantId == id then
+          apiList[#apiList+1] = decoded
+        end
+      end
+    end
+  end
+  return apiList
+end
+
+--- Filter apis based on query paramters
+-- @param queryParams query parameters to filter apis
+function filterTenantAPIs(id, apis, queryParams)
+  local basePath = queryParams['filter[where][basePath]']
+  basePath = basePath == nil and queryParams['basePath'] or basePath
+  local name = queryParams['filter[where][name]']
+  name = name == nil and queryParams['title'] or name
+  -- missing query parameters
+  if (basePath == nil and name == nil)then
+    return nil
+  end
+  -- filter apis
+  local apiList = {}
+  for k, v in pairs(apis) do
+    if k%2 == 0 then
+      local api = cjson.decode(v)
+      if api.tenantId == id and
+          ((basePath ~= nil and name == nil and api.basePath == basePath) or
+              (name ~= nil and basePath == nil and api.name == name) or
+              (basePath ~= nil and name ~= nil and api.basePath == basePath and api.name == name)) then
+        apiList[#apiList+1] = api
+      end
+    end
+  end
+  return apiList
+end
+
+--- Delete tenant from gateway
+-- @param red redis client
+-- @param id id of tenant to delete
+function _M.deleteTenant(red, id)
+  local tenantAPIs = _M.getTenantAPIs(red, id, {})
+  for _, v in pairs(tenantAPIs) do
+    apis.deleteAPI(red, v.id)
+  end
+  redis.deleteTenant(red, id)
+  return {}
+end
+
+return _M
diff --git a/scripts/lua/management/lib/validation.lua b/scripts/lua/management/lib/validation.lua
new file mode 100644
index 0000000..17af88a
--- /dev/null
+++ b/scripts/lua/management/lib/validation.lua
@@ -0,0 +1,155 @@
+-- Copyright (c) 2016 IBM. All rights reserved.
+--
+--   Permission is hereby granted, free of charge, to any person obtaining a
+--   copy of this software and associated documentation files (the "Software"),
+--   to deal in the Software without restriction, including without limitation
+--   the rights to use, copy, modify, merge, publish, distribute, sublicense,
+--   and/or sell copies of the Software, and to permit persons to whom the
+--   Software is furnished to do so, subject to the following conditions:
+--
+--   The above copyright notice and this permission notice shall be included in
+--   all copies or substantial portions of the Software.
+--
+--   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+--   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+--   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+--   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+--   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+--   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+--   DEALINGS IN THE SOFTWARE.
+
+--- @module validation
+-- Module for validating api body
+
+local cjson = require "cjson"
+local redis = require "lib/redis"
+local utils = require "lib/utils"
+
+local _M = {}
+
+function _M.validate(red, decoded)
+  local fields = {"name", "basePath", "tenantId", "resources"}
+  for _, v in pairs(fields) do
+    local res, err = isValid(red, v, decoded[v])
+    if res == false then
+      return err
+    end
+  end
+  return nil
+end
+
+--- Check JSON body fields for errors
+-- @param red Redis client instance
+-- @param field name of field
+-- @param object field object
+function isValid(red, field, object)
+  -- Check that field exists in body
+  if not object then
+    return false, { statusCode = 400, message = utils.concatStrings({"Missing field '", field, "' in request body."}) }
+  end
+  -- Additional check for basePath
+  if field == "basePath" then
+    local basePath = object
+    if string.match(basePath, "'") then
+      return false, { statusCode = 400, message = "basePath contains illegal character \"'\"." }
+    end
+  end
+  -- Additional check for tenantId
+  if field == "tenantId" then
+    local tenant = redis.getTenant(red, object)
+    if tenant == nil then
+      return false, { statusCode = 404, message = utils.concatStrings({"Unknown tenant id ", object }) }
+    end
+  end
+  if field == "resources" then
+    local res, err = checkResources(object)
+    if res ~= nil and res == false then
+      return res, err
+    end
+  end
+  -- All error checks passed
+  return true
+end
+
+--- Error checking for resources
+-- @param resources resources object
+function checkResources(resources)
+  if next(resources) == nil then
+    return false, { statusCode = 400, message = "Empty resources object." }
+  end
+  for path, resource in pairs(resources) do
+    -- Check resource path for illegal characters
+    if string.match(path, "'") then
+      return false, { statusCode = 400, message = "resource path contains illegal character \"'\"." }
+    end
+    -- Check that resource path begins with slash
+    if path:sub(1,1) ~= '/' then
+      return false, { statusCode = 400, message = "Resource path must begin with '/'." }
+    end
+    -- Check operations object
+    local res, err = checkOperations(resource.operations)
+    if res ~= nil and res == false then
+      return res, err
+    end
+  end
+end
+
+--- Error checking for operations
+-- @param operations operations object
+function checkOperations(operations)
+  if not operations or next(operations) == nil then
+    return false, { statusCode = 400, message = "Missing or empty field 'operations' or in resource path object." }
+  end
+  local allowedVerbs = {GET=true, POST=true, PUT=true, DELETE=true, PATCH=true, HEAD=true, OPTIONS=true}
+  for verb, verbObj in pairs(operations) do
+    if allowedVerbs[verb:upper()] == nil then
+      return false, { statusCode = 400, message = utils.concatStrings({"Resource verb '", verb, "' not supported."}) }
+    end
+    -- Check required fields
+    local requiredFields = {"backendMethod", "backendUrl"}
+    for k, v in pairs(requiredFields) do
+      if verbObj[v] == nil then
+        return false, { statusCode = 400, message = utils.concatStrings({"Missing field '", v, "' for '", verb, "' operation."}) }
+      end
+      if v == "backendMethod" then
+        local backendMethod = verbObj[v]
+        if allowedVerbs[backendMethod:upper()] == nil then
+          return false, { statusCode = 400, message = utils.concatStrings({"backendMethod '", backendMethod, "' not supported."}) }
+        end
+      end
+    end
+    -- Check optional fields
+    local res, err = checkOptionalPolicies(verbObj.policies, verbObj.security)
+    if res ~= nil and res == false then
+      return res, err
+    end
+  end
+end
+
+--- Error checking for policies and security
+-- @param policies policies object
+-- @param security security object
+function checkOptionalPolicies(policies, security)
+  if policies then
+    for _, v in pairs(policies) do
+      local validTypes = {"reqMapping", "rateLimit", "backendRouting"}
+      if (v.type == nil or v.value == nil) then
+        return false, { statusCode = 400, message = "Missing field in policy object. Need \"type\" and \"value\"." }
+      elseif utils.tableContains(validTypes, v.type) == false then
+        return false, { statusCode = 400, message = "Invalid type in policy object. Valid: " .. cjson.encode(validTypes) }
+      end
+    end
+  end
+  if security then
+    for _, sec in ipairs(security) do
+      local validScopes = {"tenant", "api", "resource"}
+      if (sec.type == nil or sec.scope == nil) then
+        return false, { statusCode = 400, message = "Missing field in security object. Need \"type\" and \"scope\"." }
+      elseif utils.tableContains(validScopes, sec.scope) == false then
+        return false, { statusCode = 400, message = "Invalid scope in security object. Valid: " .. cjson.encode(validScopes) }
+      end
+    end
+  end
+end
+
+return _M
\ No newline at end of file
diff --git a/scripts/lua/management/routes/apis.lua b/scripts/lua/management/routes/apis.lua
new file mode 100644
index 0000000..a50431a
--- /dev/null
+++ b/scripts/lua/management/routes/apis.lua
@@ -0,0 +1,198 @@
+-- Copyright (c) 2016 IBM. All rights reserved.
+--
+--   Permission is hereby granted, free of charge, to any person obtaining a
+--   copy of this software and associated documentation files (the "Software"),
+--   to deal in the Software without restriction, including without limitation
+--   the rights to use, copy, modify, merge, publish, distribute, sublicense,
+--   and/or sell copies of the Software, and to permit persons to whom the
+--   Software is furnished to do so, subject to the following conditions:
+--
+--   The above copyright notice and this permission notice shall be included in
+--   all copies or substantial portions of the Software.
+--
+--   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+--   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+--   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+--   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+--   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+--   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+--   DEALINGS IN THE SOFTWARE.
+
+--- @module apis
+-- Management interface for apis for the gateway
+
+local cjson = require "cjson"
+local redis = require "lib/redis"
+local utils = require "lib/utils"
+local request = require "lib/request"
+local apis = require "management/lib/apis"
+local tenants = require "management/lib/tenants"
+local swagger = require "management/lib/swagger"
+local validation = require "management/lib/validation"
+
+local REDIS_HOST = os.getenv("REDIS_HOST")
+local REDIS_PORT = os.getenv("REDIS_PORT")
+local REDIS_PASS = os.getenv("REDIS_PASS")
+
+local _M = {}
+
+--- Request handler for routing API calls appropriately
+function _M.requestHandler()
+  local requestMethod = ngx.req.get_method()
+  ngx.header.content_type = "application/json; charset=utf-8"
+  if requestMethod == "GET" then
+    getAPIs()
+  elseif requestMethod == 'POST' or requestMethod == 'PUT' then
+    addAPI()
+  elseif requestMethod == "DELETE" then
+    deleteAPI()
+  else
+    request.err(400, "Invalid verb.")
+  end
+end
+
+function getAPIs()
+  local queryParams = ngx.req.get_uri_args()
+  local id = ngx.var.api_id
+  local version = ngx.var.version
+  local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 10000)
+  if id == '' then
+    local apiList
+    if version == 'v1' then
+      apiList = apis.getAllAPIs(red, queryParams)
+    elseif version == 'v2' then
+      local tenantId = ngx.var.tenantId
+      local v2ApiList = {}
+      apiList = tenants.getTenantAPIs(red, tenantId, queryParams)
+      for _, api in pairs(apiList) do
+        v2ApiList[#v2ApiList+1] = {
+          id = api.id,
+          managedUrl = api.managedUrl,
+          open_api_doc = redis.getSwagger(red, api.id)
+        }
+      end
+      apiList = v2ApiList
+    end
+    apiList = (next(apiList) == nil) and "[]" or cjson.encode(apiList)
+    redis.close(red)
+    request.success(200, apiList)
+  else
+    local query = ngx.var.query
+    if query ~= '' then
+      if query ~= "tenant" then
+        redis.close(red)
+        request.err(400, "Invalid request")
+      else
+        local tenant = apis.getAPITenant(red, id)
+        tenant = cjson.encode(tenant)
+        redis.close(red)
+        request.success(200, tenant)
+      end
+    else
+      local api = apis.getAPI(red, id)
+      if version == 'v1' then
+        redis.close(red)
+        api = cjson.encode(api)
+        request.success(200, api)
+      elseif version == 'v2' then
+        local returnObj = {
+          id = api.id,
+          managedUrl = api.managedUrl,
+          open_api_doc = redis.getSwagger(red, api.id)
+        }
+        redis.close(red)
+        request.success(200, cjson.encode(returnObj))
+      end
+    end
+  end
+end
+
+function addAPI()
+  local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 10000)
+  local id = ngx.var.api_id
+  local existingAPI = checkForExistingAPI(red, id)
+  ngx.req.read_body()
+  local args = ngx.req.get_body_data()
+  if not args then
+    redis.close(red)
+    request.err(400, "Missing request body")
+  end
+  -- Convert json into Lua table
+  local version = ngx.var.version
+  local decoded
+  local swaggerTable;
+  if version == 'v1' then
+    decoded = cjson.decode(args)
+  elseif version == 'v2' then
+    swaggerTable = cjson.decode(args)
+    decoded = swagger.parseSwagger(swaggerTable)
+    local tenantId = ngx.var.tenantId
+    local tenant = {
+      namespace = tenantId,
+      instance = ''
+    }
+    tenants.addTenant(red, tenant, {id = tenantId})
+    decoded.tenantId = tenantId
+  end
+  -- Check for api id in JSON body
+  if existingAPI == nil and decoded.id ~= nil then
+    existingAPI = redis.getAPI(red, decoded.id)
+    if existingAPI == nil then
+      redis.close(red)
+      request.err(404, utils.concatStrings({"Unknown API id ", decoded.id}))
+    end
+  end
+  local err = validation.validate(red, decoded)
+  if err ~= nil then
+    redis.close(red)
+    request.err(err.statusCode, err.message)
+  end
+  local managedUrlObj = apis.addAPI(red, decoded, existingAPI)
+  if version == 'v1' then
+    redis.close(red)
+    managedUrlObj = cjson.encode(managedUrlObj)
+    request.success(200, managedUrlObj)
+  elseif version == 'v2' then
+    redis.addSwagger(red, managedUrlObj.id, swaggerTable)
+    local returnObj = {
+      id = managedUrlObj.id,
+      managedUrl = managedUrlObj.managedUrl,
+      open_api_doc = swaggerTable
+    }
+    redis.close(red)
+    request.success(200, cjson.encode(returnObj))
+  end
+end
+
+function deleteAPI()
+  local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 10000)
+  local id = ngx.var.api_id
+  if id == nil or id == '' then
+    redis.close(red)
+    request.err(400, "No id specified.")
+  end
+  apis.deleteAPI(red, id)
+  local version = ngx.var.version
+  if version == 'v2' then
+    redis.deleteSwagger(red, id)
+  end
+  redis.close(red)
+  request.success(204)
+end
+
+--- Check for api id from uri and use existing API if it already exists in redis
+-- @param red Redis client instance
+-- @param id API id to check
+function checkForExistingAPI(red, id)
+  local existing
+  if id ~= nil and id ~= '' then
+    existing = redis.getAPI(red, id)
+    if existing == nil then
+      redis.close(red)
+      request.err(404, utils.concatStrings({"Unknown API id ", id}))
+    end
+  end
+  return existing
+end
+
+return _M;
diff --git a/scripts/lua/management/subscriptions.lua b/scripts/lua/management/routes/subscriptions.lua
similarity index 100%
rename from scripts/lua/management/subscriptions.lua
rename to scripts/lua/management/routes/subscriptions.lua
diff --git a/scripts/lua/management/routes/tenants.lua b/scripts/lua/management/routes/tenants.lua
new file mode 100644
index 0000000..1eec6f0
--- /dev/null
+++ b/scripts/lua/management/routes/tenants.lua
@@ -0,0 +1,151 @@
+-- Copyright (c) 2016 IBM. All rights reserved.
+--
+--   Permission is hereby granted, free of charge, to any person obtaining a
+--   copy of this software and associated documentation files (the "Software"),
+--   to deal in the Software without restriction, including without limitation
+--   the rights to use, copy, modify, merge, publish, distribute, sublicense,
+--   and/or sell copies of the Software, and to permit persons to whom the
+--   Software is furnished to do so, subject to the following conditions:
+--
+--   The above copyright notice and this permission notice shall be included in
+--   all copies or substantial portions of the Software.
+--
+--   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+--   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+--   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+--   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+--   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+--   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+--   DEALINGS IN THE SOFTWARE.
+
+--- @module tenants
+-- Management interface for tenants for the gateway
+
+local cjson = require "cjson"
+local redis = require "lib/redis"
+local utils = require "lib/utils"
+local request = require "lib/request"
+local tenants = require "management/lib/tenants"
+local REDIS_HOST = os.getenv("REDIS_HOST")
+local REDIS_PORT = os.getenv("REDIS_PORT")
+local REDIS_PASS = os.getenv("REDIS_PASS")
+
+local _M = {};
+
+--- Request handler for routing tenant calls appropriately
+function _M.requestHandler()
+  local requestMethod = ngx.req.get_method()
+  ngx.header.content_type = "application/json; charset=utf-8"
+  if requestMethod == "GET" then
+    getTenants()
+  elseif requestMethod == "PUT" or requestMethod == "POST" then
+    addTenant()
+  elseif requestMethod == "DELETE" then
+    deleteTenant()
+  else
+    request.err(400, "Invalid verb.")
+  end
+end
+
+function addTenant()
+  -- Open connection to redis or use one from connection pool
+  local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 10000)
+  -- Check for tenant id and use existingTenant if it already exists in redis
+  local existingTenant = checkForExistingTenant(red)
+  -- Read in the PUT JSON Body
+  ngx.req.read_body()
+  local args = ngx.req.get_body_data()
+  if not args then
+    redis.close(red)
+    request.err(400, "Missing request body")
+  end
+  -- Convert json into Lua table
+  local decoded = cjson.decode(args)
+  -- Check for tenant id in JSON body
+  if existingTenant == nil and decoded.id ~= nil then
+    existingTenant = redis.getTenant(red, decoded.id)
+    if existingTenant == nil then
+      redis.close(red)
+      request.err(404, utils.concatStrings({"Unknown Tenant id ", decoded.id}))
+    end
+  end
+  -- Error checking
+  local res, err = utils.tableContainsAll(decoded, {"namespace", "instance"})
+  if res == false then
+    redis.close(red)
+    request.err(err.statusCode, err.message)
+  end
+  -- Return tenant object
+  local tenantObj = tenants.addTenant(red, decoded, existingTenant)
+  tenantObj = cjson.encode(tenantObj)
+  redis.close(red)
+  request.success(200, tenantObj)
+end
+
+--- Check for tenant id from uri and use existing tenant if it already exists in redis
+-- @param red Redis client instance
+function checkForExistingTenant(red)
+  local id = ngx.var.tenant_id
+  local existing
+  -- Get object from redis
+  if id ~= nil and id ~= '' then
+    existing = redis.getTenant(red, id)
+    if existing == nil then
+      redis.close(red)
+      request.err(404, utils.concatStrings({"Unknown Tenant id ", id}))
+    end
+  end
+  return existing
+end
+
+--- Get one or all tenants from the gateway
+-- GET /v1/tenants
+function getTenants()
+  local queryParams = ngx.req.get_uri_args()
+  local id = ngx.var.tenant_id
+  local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 10000)
+  if id == '' then
+    local tenantList = tenants.getAllTenants(red, queryParams)
+    tenantList = (next(tenantList) == nil) and "[]" or cjson.encode(tenantList)
+    redis.close(red)
+    request.success(200, tenantList)
+  else
+    local query = ngx.var.query
+    if query ~= '' then
+      if query ~= "apis" then
+        redis.close(red)
+        request.err(400, "Invalid request")
+      else
+        local apis = tenants.getTenantAPIs(red, id, queryParams)
+        apis = cjson.encode(apis)
+        redis.close(red)
+        request.success(200, apis)
+      end
+    else
+      local tenant = tenants.getTenant(red, id)
+      tenant = cjson.encode(tenant)
+      redis.close(red)
+      request.success(200, tenant)
+    end
+  end
+end
+
+--- Delete tenant from gateway
+-- DELETE /v1/tenants/<id>
+function deleteTenant()
+  local id = ngx.var.tenant_id
+  if id == nil or id == '' then
+    request.err(400, "No id specified.")
+  end
+  local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 10000)
+  local tenant = redis.getTenant(red, id)
+  if tenant == nil then
+    redis.close(red)
+    request.err(404, utils.concatStrings({"Unknown tenant id ", id}))
+  end
+  tenants.deleteTenant(red, id)
+  redis.close(red)
+  request.success(200, cjson.encode({}))
+end
+
+return _M
diff --git a/scripts/lua/management/tenants.lua b/scripts/lua/management/tenants.lua
deleted file mode 100644
index eb29e39..0000000
--- a/scripts/lua/management/tenants.lua
+++ /dev/null
@@ -1,261 +0,0 @@
--- Copyright (c) 2016 IBM. All rights reserved.
---
---   Permission is hereby granted, free of charge, to any person obtaining a
---   copy of this software and associated documentation files (the "Software"),
---   to deal in the Software without restriction, including without limitation
---   the rights to use, copy, modify, merge, publish, distribute, sublicense,
---   and/or sell copies of the Software, and to permit persons to whom the
---   Software is furnished to do so, subject to the following conditions:
---
---   The above copyright notice and this permission notice shall be included in
---   all copies or substantial portions of the Software.
---
---   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
---   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
---   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
---   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
---   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
---   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
---   DEALINGS IN THE SOFTWARE.
-
---- @module tenants
--- Management interface for tenants for the gateway
-
-local cjson = require "cjson"
-local redis = require "lib/redis"
-local utils = require "lib/utils"
-local request = require "lib/request"
-local REDIS_HOST = os.getenv("REDIS_HOST")
-local REDIS_PORT = os.getenv("REDIS_PORT")
-local REDIS_PASS = os.getenv("REDIS_PASS")
-
-local _M = {};
-
---- Request handler for routing tenant calls appropriately
-function _M.requestHandler()
-  local requestMethod = ngx.req.get_method()
-  if requestMethod == "GET" then
-    getTenants()
-  elseif requestMethod == "PUT" then
-    addTenant()
-  elseif requestMethod == "POST" then
-    addTenant()
-  elseif requestMethod == "DELETE" then
-    deleteTenant()
-  else
-    ngx.status = 400
-    ngx.say("Invalid verb")
-  end
-end
-
---- Add a tenant to the Gateway
--- PUT /v1/tenants
--- body:
--- {
---    "namespace": *(String) tenant namespace
---    "instance": *(String) tenant instance
--- }
-function addTenant()
-  -- Open connection to redis or use one from connection pool
-  local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 10000)
-  -- Check for tenant id and use existingTenant if it already exists in redis
-  local existingTenant = checkForExistingTenant(red)
-  -- Read in the PUT JSON Body
-  ngx.req.read_body()
-  local args = ngx.req.get_body_data()
-  if not args then
-    request.err(400, "Missing request body")
-  end
-  -- Convert json into Lua table
-  local decoded = cjson.decode(args)
-  -- Check for tenant id in JSON body
-  if existingTenant == nil and decoded.id ~= nil then
-    existingTenant = redis.getTenant(red, decoded.id)
-    if existingTenant == nil then
-      request.err(404, utils.concatStrings({"Unknown Tenant id ", decoded.id}))
-    end
-  end
-  -- Error checking
-  local res, err = utils.tableContainsAll(decoded, {"namespace", "instance"})
-  if res == false then
-    request.err(err.statusCode, err.message)
-  end
-  -- Return tenant object
-  local uuid = existingTenant ~= nil and existingTenant.id or utils.uuid()
-  local tenantObj = {
-    id = uuid,
-    namespace = decoded.namespace,
-    instance = decoded.instance
-  }
-  tenantObj = redis.addTenant(red, uuid, tenantObj)
-  redis.close(red)
-  ngx.header.content_type = "application/json; charset=utf-8"
-  request.success(200, tenantObj)
-end
-
---- Check for tenant id from uri and use existing tenant if it already exists in redis
--- @param red Redis client instance
-function checkForExistingTenant(red)
-  local id = ngx.var.tenant_id
-  local existing
-  -- Get object from redis
-  if id ~= nil and id ~= '' then
-    existing = redis.getTenant(red, id)
-    if existing == nil then
-      request.err(404, utils.concatStrings({"Unknown Tenant id ", id}))
-    end
-  end
-  return existing
-end
-
---- Get one or all tenants from the gateway
--- GET /v1/tenants
-function getTenants()
-  local queryParams = ngx.req.get_uri_args()
-  local id = ngx.var.tenant_id
-  if id == nil or id == '' then
-    getAllTenants(queryParams)
-  else
-    local query = ngx.var.query
-    if (query ~= nil and query ~= '') then
-      if query ~= "apis" then
-        request.err(400, "Invalid request")
-      else
-        getTenantAPIs(id, queryParams)
-      end
-    else
-      getTenant(id)
-    end
-  end
-end
-
---- Get all tenants in redis
--- @param queryParams object containing optional query parameters
-function getAllTenants(queryParams)
-  local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 10000)
-  local tenants = redis.getAllTenants(red)
-  redis.close(red)
-  local tenantList
-  if next(queryParams) ~= nil then
-    tenantList = filterTenants(tenants, queryParams);
-  end
-  if tenantList == nil then
-    tenantList = {}
-    for k, v in pairs(tenants) do
-      if k%2 == 0 then
-        tenantList[#tenantList+1] = cjson.decode(v)
-      end
-    end
-  end
-  ngx.header.content_type = "application/json; charset=utf-8"
-  tenantList = (next(tenantList) == nil) and "[]" or cjson.encode(tenantList)
-  request.success(200, tenantList)
-end
-
---- Filter tenants based on query parameters
--- @param tenants list of tenants
--- @param queryParams query parameters to filter tenants
-function filterTenants(tenants, queryParams)
-  local namespace = queryParams['filter[where][namespace]']
-  local instance = queryParams['filter[where][instance]']
-  -- missing or invalid query parameters
-  if (namespace == nil and instance == nil) or (instance ~= nil and namespace == nil) then
-    return nil
-  end
-  -- filter tenants
-  local tenantList = {}
-  for k, v in pairs(tenants) do
-    if k%2 == 0 then
-      local tenant = cjson.decode(v)
-      if (namespace ~= nil and instance == nil and tenant.namespace == namespace) or
-         (namespace ~= nil and instance ~= nil and tenant.namespace == namespace and tenant.instance == instance) then
-        tenantList[#tenantList+1] = tenant
-      end
-    end
-  end
-  return tenantList
-end
-
---- Get tenant by its id
--- @param id tenant id
-function getTenant(id)
-  local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 10000)
-  local tenant = redis.getTenant(red, id)
-  if tenant == nil then
-    request.err(404, utils.concatStrings({"Unknown tenant id ", id }))
-  end
-  redis.close(red)
-  ngx.header.content_type = "application/json; charset=utf-8"
-  request.success(200, cjson.encode(tenant))
-end
-
---- Get APIs associated with tenant
--- @param id tenant id
--- @param queryParams object containing optional query parameters
-function getTenantAPIs(id, queryParams)
-  local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 10000)
-  local apis = redis.getAllAPIs(red)
-  redis.close(red)
-  local apiList
-  if next(queryParams) ~= nil then
-    apiList = filterTenantAPIs(id, apis, queryParams);
-  end
-  if apiList == nil then
-    apiList = {}
-    for k, v in pairs(apis) do
-      if k%2 == 0 then
-        local decoded = cjson.decode(v)
-        if decoded.tenantId == id then
-          apiList[#apiList+1] = decoded
-        end
-      end
-    end
-  end
-  ngx.header.content_type = "application/json; charset=utf-8"
-  apiList = (next(apiList) == nil) and "[]" or cjson.encode(apiList)
-  request.success(200, apiList)
-end
-
---- Filter apis based on query paramters
--- @param queryParams query parameters to filter apis
-function filterTenantAPIs(id, apis, queryParams)
-  local basePath = queryParams['filter[where][basePath]']
-  local name = queryParams['filter[where][name]']
-  -- missing query parameters
-  if (basePath == nil and name == nil)then
-    return nil
-  end
-  -- filter apis
-  local apiList = {}
-  for k, v in pairs(apis) do
-    if k%2 == 0 then
-      local api = cjson.decode(v)
-      if api.tenantId == id and
-        ((basePath ~= nil and name == nil and api.basePath == basePath) or
-        (name ~= nil and basePath == nil and api.name == name) or
-        (basePath ~= nil and name ~= nil and api.basePath == basePath and api.name == name)) then
-          apiList[#apiList+1] = api
-      end
-    end
-  end
-  return apiList
-end
-
---- Delete tenant from gateway
--- DELETE /v1/tenants/<id>
-function deleteTenant()
-  local id = ngx.var.tenant_id
-  if id == nil or id == '' then
-    request.err(400, "No id specified.")
-  end
-  local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 10000)
-  local tenant = redis.getTenant(red, id)
-  if tenant == nil then
-    request.err(404, utils.concatStrings({"Unknown tenant id ", id}))
-  end
-  redis.deleteTenant(red, id)
-  redis.close(red)
-  request.success(200, {})
-end
-
-return _M
diff --git a/scripts/lua/policies/rateLimit.lua b/scripts/lua/policies/rateLimit.lua
index 71a763b..8da8783 100644
--- a/scripts/lua/policies/rateLimit.lua
+++ b/scripts/lua/policies/rateLimit.lua
@@ -29,6 +29,7 @@
 local utils = require "lib/utils"
 local logger = require "lib/logger"
 local request = require "lib/request"
+local redis = require "lib/redis"
 
 local _M = {}
 
@@ -39,14 +40,19 @@
   local i = 60 / obj.interval
   local r = i * obj.rate
   r = utils.concatStrings({tostring(r), 'r/m'})
+  local tenantId = ngx.var.tenant
+  local gatewayPath = ngx.var.gatewayPath
   local k
   -- Check scope
   if obj.scope == 'tenant' then
-    k = utils.concatStrings({"tenant:", ngx.var.tenant})
+    k = utils.concatStrings({"tenant:", tenantId})
   elseif obj.scope == 'api' then
-    k = utils.concatStrings({"tenant:", ngx.var.tenant, ":api:", ngx.var.apiId})
+    local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 10000)
+    local apiId = redis.resourceToApi(red, utils.concatStrings({'resources:', tenantId, ':', gatewayPath}))
+    k = utils.concatStrings({"tenant:", tenantId, ":api:", apiId})
+    redis.close(red)
   elseif obj.scope == 'resource' then
-    k = utils.concatStrings({"tenant:", ngx.var.tenant, ":resource:", ngx.var.gatewayPath})
+    k = utils.concatStrings({"tenant:", tenantId, ":resource:", gatewayPath})
   end
   -- Check subscription
   if obj.subscription ~= nil and obj.subscription == true and apiKey ~= nil then
diff --git a/scripts/lua/policies/security/apiKey.lua b/scripts/lua/policies/security/apiKey.lua
index 689262e..361b438 100644
--- a/scripts/lua/policies/security/apiKey.lua
+++ b/scripts/lua/policies/security/apiKey.lua
@@ -76,7 +76,7 @@
   local header = (securityObj.header == nil) and 'x-api-key' or securityObj.header
   local apiKey = ngx.var[utils.concatStrings({'http_', header}):gsub("-", "_")]
   if not apiKey then
-    request.err(401, utils.concatStrings({'API key header "', header, '" is required.'}))
+    request.err(401, 'Unauthorized')
     return nil
   end
   if securityObj.hashed then
@@ -84,7 +84,7 @@
   end 
   local ok = validate(red, tenant, gatewayPath, apiId, scope, header, apiKey)
   if not ok then
-    request.err(401, 'Invalid API key.')
+    request.err(401, 'Invalid API key')
     return nil
   end
   return apiKey
diff --git a/scripts/lua/routing.lua b/scripts/lua/routing.lua
index f4d54ae..bfcc50a 100644
--- a/scripts/lua/routing.lua
+++ b/scripts/lua/routing.lua
@@ -41,12 +41,15 @@
 function _M.processCall()
   -- Get resource object from redis
   local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 10000)
-  local resourceKeys = redis.getAllResourceKeys(red, ngx.var.tenant)
-  local redisKey = _M.findRedisKey(resourceKeys, ngx.var.tenant, ngx.var.gatewayPath)
+  local tenantId = ngx.var.tenant
+  local gatewayPath = ngx.var.gatewayPath
+  local resourceKeys = redis.getAllResourceKeys(red, tenantId)
+  local redisKey = _M.findRedisKey(resourceKeys, tenantId, gatewayPath)
   if redisKey == nil then
     request.err(404, 'Not found.')
   end
   local obj = cjson.decode(redis.getResource(red, redisKey, "resources"))
+  redis.close(red)
   ngx.var.tenantNamespace = obj.tenantNamespace
   ngx.var.tenantInstance = obj.tenantInstance
   for verb, opFields in pairs(obj.operations) do
@@ -54,7 +57,7 @@
       -- Check if auth is required
       local key
       if (opFields.security) then
-        for k, sec in ipairs(opFields.security) do  
+        for _, sec in ipairs(opFields.security) do
           local result = utils.concatStrings({key, security.process(sec)})
           if key == nil then
             key = result -- use the key from the first policy. 
diff --git a/tests/install-deps.sh b/tests/install-deps.sh
index b5d07d3..d629df3 100755
--- a/tests/install-deps.sh
+++ b/tests/install-deps.sh
@@ -11,3 +11,4 @@
 luarocks install --tree=lua_modules sha1
 luarocks install --tree=lua_modules md5
 luarocks install --tree=lua_modules net-url
+luarocks install --tree=lua_modules luafilesystem
\ No newline at end of file
diff --git a/tests/scripts/lua/lib/request.lua b/tests/scripts/lua/lib/request.lua
index c7aadd4..f09785c 100644
--- a/tests/scripts/lua/lib/request.lua
+++ b/tests/scripts/lua/lib/request.lua
@@ -20,6 +20,7 @@
 
 local fakengx = require 'fakengx'
 local request = require 'lib/request'
+local cjson = require 'cjson'
 
 describe('Testing Request module', function()
   before_each(function()
@@ -29,8 +30,10 @@
   it('should return correct error response', function()
     local code = 500
     local msg = 'Internal server error\n'
-    request.err(code, msg)
-    assert.are.equal('Error: ' .. msg .. '\n', ngx._body)
+    request.err(code ,msg)
+    local expected = cjson.encode{status = code, message = 'Error: ' .. msg}
+    local actual = ngx._body
+    assert.are.same(expected .. '\n', actual)
     assert.are.equal(code, ngx._exit)
   end)
 
diff --git a/tests/scripts/lua/management/examples/example1.json b/tests/scripts/lua/management/examples/example1.json
new file mode 100644
index 0000000..c6a6fe7
--- /dev/null
+++ b/tests/scripts/lua/management/examples/example1.json
@@ -0,0 +1,86 @@
+{
+  "swagger": "2.0",
+  "info": {
+    "version": "1.0",
+    "title": "Hello World API"
+  },
+  "basePath": "/native",
+  "schemes": [
+    "https"
+  ],
+  "consumes": [
+    "application/json"
+  ],
+  "produces": [
+    "application/json"
+  ],
+  "paths": {
+    "/hello": {
+      "get": {
+        "description": "Returns a greeting to the user!",
+        "parameters": [
+          {
+            "name": "user",
+            "in": "path",
+            "type": "string",
+            "required": true,
+            "description": "The name of the user to greet."
+          }
+        ],
+        "responses": {
+          "200": {
+            "description": "Returns the greeting.",
+            "schema": {
+              "type": "string"
+            }
+          },
+          "400": {
+            "description": "Invalid characters in \"user\" were provided."
+          }
+        }
+      }
+    }
+  },
+  "securityDefinitions": {
+    "client_id": {
+      "type": "apiKey",
+      "name": "X-Api-Key",
+      "in": "header"
+    },
+    "google": {
+      "type": "oauth2",
+      "flow": "tokenIntrospect",
+      "x-tokenintrospect": {
+        "url": "https://www.googleapis.com/oauth2/v3/tokeninfo"
+      }
+    }
+  },
+  "security": [
+    {
+      "client_id": [],
+      "google": []
+    }
+  ],
+  "x-gateway-rate-limit": [
+    {
+      "unit": "minute",
+      "units": 3,
+      "rate": 100
+    }
+  ],
+  "x-gateway-configuration": {
+    "cors": {
+      "enabled": true
+    },
+    "assembly": {
+      "execute": [
+        {
+          "proxy": {
+            "target-url": "https://appconnect.mybluemix.net",
+            "verb": "keep"
+          }
+        }
+      ]
+    }
+  }
+}
\ No newline at end of file
diff --git a/tests/scripts/lua/management/examples/example2.json b/tests/scripts/lua/management/examples/example2.json
new file mode 100644
index 0000000..79b5bdc
--- /dev/null
+++ b/tests/scripts/lua/management/examples/example2.json
@@ -0,0 +1,136 @@
+{
+  "swagger": "2.0",
+  "info": {
+    "version": "1.0",
+    "title": "Hello World API"
+  },
+  "basePath": "/whisk",
+  "schemes": [
+    "https"
+  ],
+  "consumes": [
+    "application/json"
+  ],
+  "produces": [
+    "application/json"
+  ],
+  "paths": {
+    "/hello": {
+      "get": {
+        "operationId": "getHello",
+        "description": "Returns a greeting to the user!",
+        "x-openwhisk": {
+          "namespace": "greend@us.ibm.com_dev",
+          "package": "demo",
+          "action": "hello",
+          "url": "https://openwhisk.ng.bluemix.net/api/some/action/path.http"
+        },
+        "parameters": [
+          {
+            "name": "user",
+            "in": "path",
+            "type": "string",
+            "required": true,
+            "description": "The name of the user to greet."
+          }
+        ],
+        "responses": {
+          "200": {
+            "description": "Returns the greeting.",
+            "schema": {
+              "type": "string"
+            }
+          },
+          "400": {
+            "description": "Invalid characters in \"user\" were provided."
+          }
+        }
+      },
+      "post": {
+        "operationId": "postHello",
+        "description": null,
+        "x-openwhisk": {
+          "package": "demo",
+          "action": "createUser",
+          "url": "https://openwhisk.ng.bluemix.net/api/user@us.ibm.com/demo/createuser"
+        }
+      }
+    }
+  },
+  "securityDefinitions": {
+    "client_id": {
+      "type": "apiKey",
+      "name": "X-Api-Key",
+      "in": "header"
+    },
+    "google": {
+      "type": "oauth2",
+      "flow": "tokenIntrospect",
+      "x-tokenintrospect": {
+        "url": "https://www.googleapis.com/oauth2/v3/tokeninfo"
+      }
+    }
+  },
+  "security": [
+    {
+      "client_id": [],
+      "client_secret": []
+    }
+  ],
+  "x-ibm-rate-limit": [
+    {
+      "unit": "minute",
+      "units": 3,
+      "rate": 100
+    }
+  ],
+  "x-ibm-configuration": {
+    "assembly": {
+      "execute": [
+        {
+          "set-variable": {
+            "actions": [
+              {
+                "set": "message.headers.Authorization",
+                "value": "Basic xxx"
+              }
+            ]
+          }
+        },
+        {
+          "operation-switch": {
+            "case": [
+              {
+                "operations": [
+                  "getHello"
+                ],
+                "execute": [
+                  {
+                    "proxy": {
+                      "target-url": "https://openwhisk.ng.bluemix.net/api/some/action/path.http",
+                      "verb": "keep"
+                    }
+                  }
+                ]
+              },
+              {
+                "operations": [
+                  "postHello"
+                ],
+                "execute": [
+                  {
+                    "proxy": {
+                      "target-url": "https://openwhisk.ng.bluemix.net/api/user@us.ibm.com/demo/createuser",
+                      "verb": "keep"
+                    }
+                  }
+                ]
+              }
+            ],
+            "otherwise": []
+          }
+        }
+      ]
+    }
+  }
+}
\ No newline at end of file
diff --git a/tests/scripts/lua/management/lib/swagger.lua b/tests/scripts/lua/management/lib/swagger.lua
new file mode 100644
index 0000000..b82bb0c
--- /dev/null
+++ b/tests/scripts/lua/management/lib/swagger.lua
@@ -0,0 +1,183 @@
+-- Copyright (c) 2016 IBM. All rights reserved.
+--
+--   Permission is hereby granted, free of charge, to any person obtaining a
+--   copy of this software and associated documentation files (the "Software"),
+--   to deal in the Software without restriction, including without limitation
+--   the rights to use, copy, modify, merge, publish, distribute, sublicense,
+--   and/or sell copies of the Software, and to permit persons to whom the
+--   Software is furnished to do so, subject to the following conditions:
+--
+--   The above copyright notice and this permission notice shall be included in
+--   all copies or substantial portions of the Software.
+--
+--   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+--   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+--   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+--   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+--   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+--   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+--   DEALINGS IN THE SOFTWARE.
+
+local cjson = require 'cjson'
+local lfs = require 'lfs'
+local swagger = require 'management/lib/swagger'
+local exampleBasePath = lfs.currentdir() .. '/scripts/lua/management/examples/'
+
+describe('Testing v2 management interface', function()
+  it('should parse native swagger correctly', function()
+    local expected = cjson.decode([[
+      {
+        "basePath": "/native",
+        "name": "Hello World API",
+        "resources": {
+          "/hello": {
+            "operations": {
+              "get": {
+                "backendUrl": "https://appconnect.mybluemix.net",
+                "policies": [
+                  {
+                    "type": "rateLimit",
+                    "value": {
+                      "scope": "api",
+                      "subscription": "true",
+                      "interval": 180,
+                      "rate": 100
+                    }
+                  }
+                ],
+                "security": [
+                  {
+                    "type": "oauth2",
+                    "provider": "google",
+                    "scope": "api"
+                  },
+                  {
+                    "type": "apiKey",
+                    "header": "X-Api-Key",
+                    "scope": "api"
+                  }
+                ],
+                "backendMethod": "get"
+              }
+            }
+          }
+        }
+      }
+    ]])
+    local jsonPath = exampleBasePath .. 'example1.json'
+    local jsonTable = loadJsonTable(jsonPath)
+    local actual = swagger.parseSwagger(jsonTable)
+    assert.are.same(expected, actual)
+  end)
+
+  it('should parse whisk swagger correctly', function()
+    local expected = cjson.decode([[
+        {
+          "basePath": "/whisk",
+          "name": "Hello World API",
+          "resources": {
+            "/hello": {
+              "operations": {
+                "post": {
+                  "backendUrl": "https://openwhisk.ng.bluemix.net/api/user@us.ibm.com/demo/createuser",
+                  "policies": [
+                    {
+                      "type": "rateLimit",
+                      "value": {
+                        "scope": "api",
+                        "subscription": "true",
+                        "interval": 180,
+                        "rate": 100
+                      }
+                    },
+                    {
+                      "type": "reqMapping",
+                      "value": [{
+                        "from": {
+                          "value": "Basic xxx"
+                        },
+                        "to": {
+                          "name": "Authorization",
+                          "location": "header"
+                        },
+                        "action": "insert"
+                      }]
+                    }
+                  ],
+                  "security": [
+                    {
+                      "type": "oauth2",
+                      "provider": "google",
+                      "scope": "api"
+                    },
+                    {
+                      "type": "apiKey",
+                      "header": "X-Api-Key",
+                      "scope": "api"
+                    }
+                  ],
+                  "backendMethod": "post"
+                },
+                "get": {
+                  "backendUrl": "https://openwhisk.ng.bluemix.net/api/some/action/path.http",
+                  "policies": [
+                    {
+                      "type": "rateLimit",
+                      "value": {
+                        "scope": "api",
+                        "subscription": "true",
+                        "interval": 180,
+                        "rate": 100
+                      }
+                    },
+                    {
+                      "type": "reqMapping",
+                      "value": [{
+                        "from": {
+                          "value": "Basic xxx"
+                        },
+                        "to": {
+                          "name": "Authorization",
+                          "location": "header"
+                        },
+                        "action": "insert"
+                      }]
+                    }
+                  ],
+                  "security": [
+                    {
+                      "type": "oauth2",
+                      "provider": "google",
+                      "scope": "api"
+                    },
+                    {
+                      "type": "apiKey",
+                      "header": "X-Api-Key",
+                      "scope": "api"
+                    }
+                  ],
+                  "backendMethod": "get"
+                }
+              }
+            }
+          }
+        }
+      ]])
+    local jsonPath = exampleBasePath .. 'example2.json'
+    local jsonTable = loadJsonTable(jsonPath)
+    local actual = swagger.parseSwagger(jsonTable)
+    assert.are.same(expected.resources["/hello"].operations.post.policies[2].value, actual.resources["/hello"].operations.post.policies[2].value)
+  end)
+end)
+
+function loadJsonTable(path)
+  local contents
+  local file = io.open(path, "r" )
+  if file then
+    contents = file:read("*a")
+    local decoded = cjson.decode(contents)
+    io.close( file )
+    return decoded
+  end
+  return nil
+end
\ No newline at end of file