diff --git a/packages/builder/src/components/database/ModelDataTable/api.js b/packages/builder/src/components/database/ModelDataTable/api.js
index c573814e88..e9592074bf 100644
--- a/packages/builder/src/components/database/ModelDataTable/api.js
+++ b/packages/builder/src/components/database/ModelDataTable/api.js
@@ -1,6 +1,6 @@
 import api from "builderStore/api"
 
-export async function createUser(user, appId, instanceId) {
+export async function createUser(user, instanceId) {
   const CREATE_USER_URL = `/api/${instanceId}/users`
   const response = await api.post(CREATE_USER_URL, user)
   return await response.json()
@@ -15,20 +15,20 @@ export async function createDatabase(appname, instanceName) {
 }
 
 export async function deleteRecord(record, instanceId) {
-  const DELETE_RECORDS_URL = `/api/${instanceId}/records/${record._id}/${record._rev}`
+  const DELETE_RECORDS_URL = `/api/${instanceId}/${record.modelId}/records/${record._id}/${record._rev}`
   const response = await api.delete(DELETE_RECORDS_URL)
   return response
 }
 
 export async function saveRecord(record, instanceId) {
-  const SAVE_RECORDS_URL = `/api/${instanceId}/records`
+  const SAVE_RECORDS_URL = `/api/${instanceId}/${record.modelId}/records`
   const response = await api.post(SAVE_RECORDS_URL, record)
 
   return await response.json()
 }
 
 export async function fetchDataForView(viewName, instanceId) {
-  const FETCH_RECORDS_URL = `/api/${instanceId}/${viewName}/records`
+  const FETCH_RECORDS_URL = `/api/${instanceId}/views/${viewName}`
 
   const response = await api.get(FETCH_RECORDS_URL)
   return await response.json()
diff --git a/packages/builder/src/components/database/ModelDataTable/modals/CreateUser.svelte b/packages/builder/src/components/database/ModelDataTable/modals/CreateUser.svelte
index 842703af1c..77e0850b87 100644
--- a/packages/builder/src/components/database/ModelDataTable/modals/CreateUser.svelte
+++ b/packages/builder/src/components/database/ModelDataTable/modals/CreateUser.svelte
@@ -7,14 +7,15 @@
 
   let username
   let password
+  let accessLevelId
 
-  $: valid = username && password
+  $: valid = username && password && accessLevelId
   $: instanceId = $backendUiStore.selectedDatabase._id
   $: appId = $store.appId
 
   async function createUser() {
-    const user = { name: username, username, password }
-    const response = await api.createUser(user, appId, instanceId)
+    const user = { name: username, username, password, accessLevelId }
+    const response = await api.createUser(user, instanceId)
     backendUiStore.actions.users.create(response)
     onClosed()
   }
@@ -30,6 +31,14 @@
       <label class="uk-form-label" for="form-stacked-text">Password</label>
       <input class="uk-input" type="password" bind:value={password} />
     </div>
+    <div class="uk-margin">
+      <label class="uk-form-label" for="form-stacked-text">Access Level</label>
+      <select class="uk-select" bind:value={accessLevelId}>
+        <option value=""></option>
+        <option value="POWER_USER">Power User</option>
+        <option value="ADMIN">Admin</option>
+      </select>
+    </div>
   </div>
   <footer>
     <ActionButton alert on:click={onClosed}>Cancel</ActionButton>
diff --git a/packages/cli/src/commands/init/index.js b/packages/cli/src/commands/init/index.js
index 4a25fbd8b2..f387a8d203 100644
--- a/packages/cli/src/commands/init/index.js
+++ b/packages/cli/src/commands/init/index.js
@@ -11,13 +11,6 @@ module.exports = {
       default: "~/.budibase",
       alias: "d",
     })
-    yargs.positional("database", {
-      type: "string",
-      describe: "use a local (PouchDB) or remote (CouchDB) database",
-      alias: "b",
-      default: "local",
-      choices: ["local", "remote"],
-    })
     yargs.positional("clientId", {
       type: "string",
       describe: "used to determine the name of the global databse",
@@ -28,7 +21,7 @@ module.exports = {
       type: "string",
       describe:
         "connection string for couch db, format: https://username:password@localhost:5984",
-      alias: "x",
+      alias: "u",
       default: "",
     })
     yargs.positional("quiet", {
diff --git a/packages/cli/src/commands/init/initHandler.js b/packages/cli/src/commands/init/initHandler.js
index 0467633ee8..c0c4ad1b9e 100644
--- a/packages/cli/src/commands/init/initHandler.js
+++ b/packages/cli/src/commands/init/initHandler.js
@@ -14,7 +14,6 @@ const run = async opts => {
   try {
     await ensureAppDir(opts)
     await setEnvironmentVariables(opts)
-    await prompts(opts)
     await createClientDatabase(opts)
     await createDevEnvFile(opts)
     console.log(chalk.green("Budibase successfully initialised."))
@@ -24,13 +23,13 @@ const run = async opts => {
 }
 
 const setEnvironmentVariables = async opts => {
-  if (opts.database === "local") {
+  if (opts.couchDbUrl) {
+    process.env.COUCH_DB_URL = opts.couchDbUrl
+  } else {
     const dataDir = join(opts.dir, ".data")
     await ensureDir(dataDir)
     process.env.COUCH_DB_URL =
       dataDir + (dataDir.endsWith("/") || dataDir.endsWith("\\") ? "" : "/")
-  } else {
-    process.env.COUCH_DB_URL = opts.couchDbUrl
   }
 }
 
@@ -39,25 +38,6 @@ const ensureAppDir = async opts => {
   await ensureDir(opts.dir)
 }
 
-const prompts = async opts => {
-  const questions = [
-    {
-      type: "input",
-      name: "couchDbUrl",
-      message:
-        "CouchDB Connection String (e.g. https://user:password@localhost:5984): ",
-      validate: function(value) {
-        return !!value || "Please enter connection string"
-      },
-    },
-  ]
-
-  if (opts.database === "remote" && !opts.couchDbUrl) {
-    const answers = await inquirer.prompt(questions)
-    opts.couchDbUrl = answers.couchDbUrl
-  }
-}
-
 const createClientDatabase = async opts => {
   // cannot be a top level require as it
   // will cause environment module to be loaded prematurely
diff --git a/packages/server/.vscode/launch.json b/packages/server/.vscode/launch.json
index 964e9297f4..4dc6b653cb 100644
--- a/packages/server/.vscode/launch.json
+++ b/packages/server/.vscode/launch.json
@@ -49,6 +49,19 @@
             "program": "${workspaceFolder}/node_modules/jest-cli/bin/jest",
         }
       },
+      {
+        "type": "node",
+        "request": "launch",
+        "name": "Jest - Access Levels",
+        "program": "${workspaceFolder}/node_modules/.bin/jest",
+        "args": ["accesslevel.spec", "--runInBand"],
+        "console": "integratedTerminal",
+        "internalConsoleOptions": "neverOpen",
+        "disableOptimisticBPs": true,
+        "windows": {
+            "program": "${workspaceFolder}/node_modules/jest-cli/bin/jest",
+        }
+      },
       {
         "type": "node",
         "request": "launch",
diff --git a/packages/server/src/api/controllers/accesslevel.js b/packages/server/src/api/controllers/accesslevel.js
new file mode 100644
index 0000000000..9c9c7ff727
--- /dev/null
+++ b/packages/server/src/api/controllers/accesslevel.js
@@ -0,0 +1,108 @@
+const CouchDB = require("../../db")
+const newid = require("../../db/newid")
+const {
+  generateAdminPermissions,
+  generatePowerUserPermissions,
+  POWERUSER_LEVEL_ID,
+  ADMIN_LEVEL_ID,
+} = require("../../utilities/accessLevels")
+
+exports.fetch = async function(ctx) {
+  const db = new CouchDB(ctx.params.instanceId)
+  const body = await db.query("database/by_type", {
+    include_docs: true,
+    key: ["accesslevel"],
+  })
+  const customAccessLevels = body.rows.map(row => row.doc)
+
+  const staticAccessLevels = [
+    {
+      _id: ADMIN_LEVEL_ID,
+      name: "Admin",
+      permissions: await generateAdminPermissions(ctx.params.instanceId),
+    },
+    {
+      _id: POWERUSER_LEVEL_ID,
+      name: "Power User",
+      permissions: await generatePowerUserPermissions(ctx.params.instanceId),
+    },
+  ]
+
+  ctx.body = [...staticAccessLevels, ...customAccessLevels]
+}
+
+exports.find = async function(ctx) {
+  const db = new CouchDB(ctx.params.instanceId)
+  ctx.body = await db.get(ctx.params.levelId)
+}
+
+exports.update = async function(ctx) {
+  const db = new CouchDB(ctx.params.instanceId)
+  const level = await db.get(ctx.params.levelId)
+  level.name = ctx.body.name
+  level.permissions = ctx.request.body.permissions
+  const result = await db.put(level)
+  level._rev = result.rev
+  ctx.body = level
+  ctx.message = `Level ${level.name} updated successfully.`
+}
+
+exports.patch = async function(ctx) {
+  const db = new CouchDB(ctx.params.instanceId)
+  const level = await db.get(ctx.params.levelId)
+  const { removedPermissions, addedPermissions, _rev } = ctx.request.body
+
+  if (!_rev) throw new Error("Must supply a _rev to update an access level")
+
+  level._rev = _rev
+
+  if (removedPermissions) {
+    level.permissions = level.permissions.filter(
+      p =>
+        !removedPermissions.some(
+          rem => rem.name === p.name && rem.itemId === p.itemId
+        )
+    )
+  }
+
+  if (addedPermissions) {
+    level.permissions = [
+      ...level.permissions.filter(
+        p =>
+          !addedPermissions.some(
+            add => add.name === p.name && add.itemId === p.itemId
+          )
+      ),
+      ...addedPermissions,
+    ]
+  }
+
+  const result = await db.put(level)
+  level._rev = result.rev
+  ctx.body = level
+  ctx.message = `Access Level ${level.name} updated successfully.`
+}
+
+exports.create = async function(ctx) {
+  const db = new CouchDB(ctx.params.instanceId)
+
+  const level = {
+    name: ctx.request.body.name,
+    _rev: ctx.request.body._rev,
+    permissions: ctx.request.body.permissions || [],
+    _id: newid(),
+    type: "accesslevel",
+  }
+
+  const result = await db.put(level)
+  level._rev = result.rev
+  ctx.body = level
+  ctx.message = `Access Level '${level.name}' created successfully.`
+}
+
+exports.destroy = async function(ctx) {
+  const db = new CouchDB(ctx.params.instanceId)
+  await db.remove(ctx.params.levelId, ctx.params.rev)
+  ctx.message = `Access Level ${ctx.params.id} deleted successfully`
+  ctx.status = 200
+}
diff --git a/packages/server/src/api/controllers/auth.js b/packages/server/src/api/controllers/auth.js
index 4cfc2f438a..fb00024d43 100644
--- a/packages/server/src/api/controllers/auth.js
+++ b/packages/server/src/api/controllers/auth.js
@@ -24,20 +24,21 @@ exports.authenticate = async ctx => {
 
   // Check the user exists in the instance DB by username
   const instanceDb = new CouchDB(instanceId)
-  const { rows } = await instanceDb.query("database/by_username", {
-    include_docs: true,
-    username,
-  })
 
-  if (rows.length === 0) ctx.throw(500, `User does not exist.`)
-
-  const dbUser = rows[0].doc
+  let dbUser
+  try {
+    dbUser = await instanceDb.get(`user_${username}`)
+  } catch (_) {
+    // do not want to throw a 404 - as this could be
+    // used to dtermine valid usernames
+    ctx.throw(401, "Invalid Credentials")
+  }
 
   // authenticate
   if (await bcrypt.compare(password, dbUser.password)) {
     const payload = {
       userId: dbUser._id,
-      accessLevel: "",
+      accessLevelId: dbUser.accessLevelId,
       instanceId: instanceId,
     }
 
diff --git a/packages/server/src/api/controllers/record.js b/packages/server/src/api/controllers/record.js
index ca1d4275f7..c4ac1deba3 100644
--- a/packages/server/src/api/controllers/record.js
+++ b/packages/server/src/api/controllers/record.js
@@ -8,6 +8,7 @@ exports.save = async function(ctx) {
   console.log("THIS INSTANCE", ctx.params.instanceId);
   const db = new CouchDB(ctx.params.instanceId)
   const record = ctx.request.body
+  record.modelId = ctx.params.modelId
 
   if (!record._rev && !record._id) {
     record._id = newid()
@@ -45,13 +46,12 @@ exports.save = async function(ctx) {
   const response = await db.post(record)
   record._rev = response.rev
   // ctx.eventPublisher.emit("RECORD_CREATED", record)
-
   ctx.body = record
   ctx.status = 200
   ctx.message = `${model.name} created successfully`
 }
 
-exports.fetch = async function(ctx) {
+exports.fetchView = async function(ctx) {
   const db = new CouchDB(ctx.params.instanceId)
   const response = await db.query(`database/${ctx.params.viewName}`, {
     include_docs: true,
@@ -59,13 +59,30 @@ exports.fetch = async function(ctx) {
   ctx.body = response.rows.map(row => row.doc)
 }
 
+exports.fetchModel = async function(ctx) {
+  const db = new CouchDB(ctx.params.instanceId)
+  const response = await db.query(`database/all_${ctx.params.modelId}`, {
+    include_docs: true,
+  })
+  ctx.body = response.rows.map(row => row.doc)
+}
+
 exports.find = async function(ctx) {
   const db = new CouchDB(ctx.params.instanceId)
-  ctx.body = await db.get(ctx.params.recordId)
+  const record = await db.get(ctx.params.recordId)
+  if (record.modelId !== ctx.params.modelId) {
+    ctx.throw(400, "Supplied modelId doe not match the record's modelId")
+    return
+  }
+  ctx.body = record
 }
 
 exports.destroy = async function(ctx) {
-  const databaseId = ctx.params.instanceId
-  const db = new CouchDB(databaseId)
+  const db = new CouchDB(ctx.params.instanceId)
+  const record = await db.get(ctx.params.recordId)
+  if (record.modelId !== ctx.params.modelId) {
+    ctx.throw(400, "Supplied modelId doe not match the record's modelId")
+    return
+  }
   ctx.body = await db.remove(ctx.params.recordId, ctx.params.revId)
 }
diff --git a/packages/server/src/api/controllers/user.js b/packages/server/src/api/controllers/user.js
index 2be0132a47..f654caa4c0 100644
--- a/packages/server/src/api/controllers/user.js
+++ b/packages/server/src/api/controllers/user.js
@@ -2,8 +2,11 @@ const CouchDB = require("../../db")
 const clientDb = require("../../db/clientDb")
 const bcrypt = require("../../utilities/bcrypt")
 const env = require("../../environment")
-
 const getUserId = userName => `user_${userName}`
+const {
+  POWERUSER_LEVEL_ID,
+  ADMIN_LEVEL_ID,
+} = require("../../utilities/accessLevels")
 
 exports.fetch = async function(ctx) {
   const database = new CouchDB(ctx.params.instanceId)
@@ -18,17 +21,26 @@ exports.fetch = async function(ctx) {
 exports.create = async function(ctx) {
   const database = new CouchDB(ctx.params.instanceId)
   const appId = (await database.get("_design/database")).metadata.applicationId
-  const { username, password, name } = ctx.request.body
+  const { username, password, name, accessLevelId } = ctx.request.body
 
-  if (!username || !password) ctx.throw(400, "Username and Password Required.")
+  if (!username || !password) {
+    ctx.throw(400, "Username and Password Required.")
+  }
 
-  const response = await database.post({
+  const accessLevel = await checkAccessLevel(database, accessLevelId)
+
+  if (!accessLevel) ctx.throw(400, "Invalid Access Level")
+
+  const user = {
     _id: getUserId(username),
     username,
     password: await bcrypt.hash(password),
     name: name || username,
     type: "user",
-  })
+    accessLevelId,
+  }
+
+  const response = await database.post(user)
 
   // the clientDB needs to store a map of users against the app
   const db = new CouchDB(clientDb.name(env.CLIENT_ID))
@@ -49,6 +61,8 @@ exports.create = async function(ctx) {
   }
 }
 
+exports.update = async function(ctx) {}
+
 exports.destroy = async function(ctx) {
   const database = new CouchDB(ctx.params.instanceId)
   await database.destroy(getUserId(ctx.params.username))
@@ -65,3 +79,18 @@ exports.find = async function(ctx) {
     _rev: user._rev,
   }
 }
+
+const checkAccessLevel = async (db, accessLevelId) => {
+  if (!accessLevelId) return
+  if (
+    accessLevelId === POWERUSER_LEVEL_ID ||
+    accessLevelId === ADMIN_LEVEL_ID
+  ) {
+    return {
+      _id: accessLevelId,
+      name: accessLevelId,
+      permissions: [],
+    }
+  }
+  return await db.get(accessLevelId)
+}
diff --git a/packages/server/src/api/index.js b/packages/server/src/api/index.js
index 360cf8f827..66c3168f23 100644
--- a/packages/server/src/api/index.js
+++ b/packages/server/src/api/index.js
@@ -7,7 +7,6 @@ const {
   authRoutes,
   pageRoutes,
   userRoutes,
-  recordRoutes,
   instanceRoutes,
   clientRoutes,
   applicationRoutes,
@@ -15,7 +14,8 @@ const {
   viewRoutes,
   staticRoutes,
   componentRoutes,
-  workflowRoutes
+  workflowRoutes,
+  accesslevelRoutes,
 } = require("./routes")
 
 const router = new Router()
@@ -71,9 +71,6 @@ router.use(modelRoutes.allowedMethods())
 router.use(userRoutes.routes())
 router.use(userRoutes.allowedMethods())
 
-router.use(recordRoutes.routes())
-router.use(recordRoutes.allowedMethods())
-
 router.use(instanceRoutes.routes())
 router.use(instanceRoutes.allowedMethods())
 
@@ -93,6 +90,9 @@ router.use(componentRoutes.allowedMethods())
 router.use(clientRoutes.routes())
 router.use(clientRoutes.allowedMethods())
 
+router.use(accesslevelRoutes.routes())
+router.use(accesslevelRoutes.allowedMethods())
+
 router.use(staticRoutes.routes())
 router.use(staticRoutes.allowedMethods())
 
diff --git a/packages/server/src/api/routes/accesslevel.js b/packages/server/src/api/routes/accesslevel.js
new file mode 100644
index 0000000000..d34acab02c
--- /dev/null
+++ b/packages/server/src/api/routes/accesslevel.js
@@ -0,0 +1,14 @@
+const Router = require("@koa/router")
+const controller = require("../controllers/accesslevel")
+
+const router = Router()
+
+router
+  .post("/api/:instanceId/accesslevels", controller.create)
+  .put("/api/:instanceId/accesslevels", controller.update)
+  .get("/api/:instanceId/accesslevels", controller.fetch)
+  .get("/api/:instanceId/accesslevels/:levelId", controller.find)
+  .delete("/api/:instanceId/accesslevels/:levelId/:rev", controller.destroy)
+  .patch("/api/:instanceId/accesslevels/:levelId", controller.patch)
+
+module.exports = router
diff --git a/packages/server/src/api/routes/application.js b/packages/server/src/api/routes/application.js
index dddf64a710..60cc781ac6 100644
--- a/packages/server/src/api/routes/application.js
+++ b/packages/server/src/api/routes/application.js
@@ -1,11 +1,17 @@
 const Router = require("@koa/router")
 const controller = require("../controllers/application")
+const authorized = require("../../middleware/authorized")
+const { BUILDER } = require("../../utilities/accessLevels")
 
 const router = Router()
 
 router
-  .get("/api/applications", controller.fetch)
-  .get("/api/:applicationId/appPackage", controller.fetchAppPackage)
-  .post("/api/applications", controller.create)
+  .get("/api/applications", authorized(BUILDER), controller.fetch)
+  .get(
+    "/api/:applicationId/appPackage",
+    authorized(BUILDER),
+    controller.fetchAppPackage
+  )
+  .post("/api/applications", authorized(BUILDER), controller.create)
 
 module.exports = router
diff --git a/packages/server/src/api/routes/client.js b/packages/server/src/api/routes/client.js
index ff87b82e22..16acf1b7a3 100644
--- a/packages/server/src/api/routes/client.js
+++ b/packages/server/src/api/routes/client.js
@@ -1,8 +1,10 @@
 const Router = require("@koa/router")
 const controller = require("../controllers/client")
+const authorized = require("../../middleware/authorized")
+const { BUILDER } = require("../../utilities/accessLevels")
 
 const router = Router()
 
-router.get("/api/client/id", controller.getClientId)
+router.get("/api/client/id", authorized(BUILDER), controller.getClientId)
 
 module.exports = router
diff --git a/packages/server/src/api/routes/component.js b/packages/server/src/api/routes/component.js
index 5df34381fa..8fbe7ac41a 100644
--- a/packages/server/src/api/routes/component.js
+++ b/packages/server/src/api/routes/component.js
@@ -1,10 +1,13 @@
 const Router = require("@koa/router")
 const controller = require("../controllers/component")
+const authorized = require("../../middleware/authorized")
+const { BUILDER } = require("../../utilities/accessLevels")
 
 const router = Router()
 
 router.get(
   "/:appId/components/definitions",
+  authorized(BUILDER),
   controller.fetchAppComponentDefinitions
 )
 
diff --git a/packages/server/src/api/routes/index.js b/packages/server/src/api/routes/index.js
index e0737ffa6c..c515d5f437 100644
--- a/packages/server/src/api/routes/index.js
+++ b/packages/server/src/api/routes/index.js
@@ -1,7 +1,6 @@
 const authRoutes = require("./auth")
 const pageRoutes = require("./pages")
 const userRoutes = require("./user")
-const recordRoutes = require("./record")
 const instanceRoutes = require("./instance")
 const clientRoutes = require("./client")
 const applicationRoutes = require("./application")
@@ -9,13 +8,13 @@ const modelRoutes = require("./model")
 const viewRoutes = require("./view")
 const staticRoutes = require("./static")
 const componentRoutes = require("./component")
-const workflowRoutes = require("./workflow");
+const workflowRoutes = require("./workflow")
+const accesslevelRoutes = require("./accesslevel")
 
 module.exports = {
   authRoutes,
   pageRoutes,
   userRoutes,
-  recordRoutes,
   instanceRoutes,
   clientRoutes,
   applicationRoutes,
@@ -23,5 +22,6 @@ module.exports = {
   viewRoutes,
   staticRoutes,
   componentRoutes,
-  workflowRoutes
+  workflowRoutes,
+  accesslevelRoutes,
 }
diff --git a/packages/server/src/api/routes/instance.js b/packages/server/src/api/routes/instance.js
index fd74a98bf1..9b7b3db511 100644
--- a/packages/server/src/api/routes/instance.js
+++ b/packages/server/src/api/routes/instance.js
@@ -1,10 +1,12 @@
 const Router = require("@koa/router")
 const controller = require("../controllers/instance")
+const authorized = require("../../middleware/authorized")
+const { BUILDER } = require("../../utilities/accessLevels")
 
 const router = Router()
 
 router
-  .post("/api/:applicationId/instances", controller.create)
-  .delete("/api/instances/:instanceId", controller.destroy)
+  .post("/api/:applicationId/instances", authorized(BUILDER), controller.create)
+  .delete("/api/instances/:instanceId", authorized(BUILDER), controller.destroy)
 
 module.exports = router
diff --git a/packages/server/src/api/routes/model.js b/packages/server/src/api/routes/model.js
index d25d0d17cb..d9eb5cf798 100644
--- a/packages/server/src/api/routes/model.js
+++ b/packages/server/src/api/routes/model.js
@@ -1,12 +1,49 @@
 const Router = require("@koa/router")
-const controller = require("../controllers/model")
+const modelController = require("../controllers/model")
+const recordController = require("../controllers/record")
+const authorized = require("../../middleware/authorized")
+const {
+  READ_MODEL,
+  WRITE_MODEL,
+  BUILDER,
+} = require("../../utilities/accessLevels")
 
 const router = Router()
 
+// records
+
 router
-  .get("/api/:instanceId/models", controller.fetch)
-  .post("/api/:instanceId/models", controller.create)
+  .get(
+    "/api/:instanceId/:modelId/records",
+    authorized(READ_MODEL, ctx => ctx.params.modelId),
+    recordController.fetchModel
+  )
+  .get(
+    "/api/:instanceId/:modelId/records/:recordId",
+    authorized(READ_MODEL, ctx => ctx.params.modelId),
+    recordController.find
+  )
+  .post(
+    "/api/:instanceId/:modelId/records",
+    authorized(WRITE_MODEL, ctx => ctx.params.modelId),
+    recordController.save
+  )
+  .delete(
+    "/api/:instanceId/:modelId/records/:recordId/:revId",
+    authorized(WRITE_MODEL, ctx => ctx.params.modelId),
+    recordController.destroy
+  )
+
+// models
+
+router
+  .get("/api/:instanceId/models", authorized(BUILDER), modelController.fetch)
+  .post("/api/:instanceId/models", authorized(BUILDER), modelController.create)
   // .patch("/api/:instanceId/models", controller.update)
-  .delete("/api/:instanceId/models/:modelId/:revId", controller.destroy)
+  .delete(
+    "/api/:instanceId/models/:modelId/:revId",
+    authorized(BUILDER),
+    modelController.destroy
+  )
 
 module.exports = router
diff --git a/packages/server/src/api/routes/pages.js b/packages/server/src/api/routes/pages.js
index 88fd1239f4..98f0f08b92 100644
--- a/packages/server/src/api/routes/pages.js
+++ b/packages/server/src/api/routes/pages.js
@@ -7,63 +7,85 @@ const {
   renameScreen,
   deleteScreen,
 } = require("../../utilities/builder")
+const authorized = require("../../middleware/authorized")
+const { BUILDER } = require("../../utilities/accessLevels")
 
 const router = Router()
 
-router.post("/_builder/api/:appId/pages/:pageName", async ctx => {
-  await buildPage(
-    ctx.config,
-    ctx.params.appId,
-    ctx.params.pageName,
-    ctx.request.body
-  )
-  ctx.response.status = StatusCodes.OK
-})
+router.post(
+  "/_builder/api/:appId/pages/:pageName",
+  authorized(BUILDER),
+  async ctx => {
+    await buildPage(
+      ctx.config,
+      ctx.params.appId,
+      ctx.params.pageName,
+      ctx.request.body
+    )
+    ctx.response.status = StatusCodes.OK
+  }
+)
 
-router.get("/_builder/api/:appId/pages/:pagename/screens", async ctx => {
-  ctx.body = await listScreens(
-    ctx.config,
-    ctx.params.appId,
-    ctx.params.pagename
-  )
-  ctx.response.status = StatusCodes.OK
-})
+router.get(
+  "/_builder/api/:appId/pages/:pagename/screens",
+  authorized(BUILDER),
+  async ctx => {
+    ctx.body = await listScreens(
+      ctx.config,
+      ctx.params.appId,
+      ctx.params.pagename
+    )
+    ctx.response.status = StatusCodes.OK
+  }
+)
 
-router.post("/_builder/api/:appId/pages/:pagename/screen", async ctx => {
-  ctx.body = await saveScreen(
-    ctx.config,
-    ctx.params.appId,
-    ctx.params.pagename,
-    ctx.request.body
-  )
-  ctx.response.status = StatusCodes.OK
-})
+router.post(
+  "/_builder/api/:appId/pages/:pagename/screen",
+  authorized(BUILDER),
+  async ctx => {
+    ctx.body = await saveScreen(
+      ctx.config,
+      ctx.params.appId,
+      ctx.params.pagename,
+      ctx.request.body
+    )
+    ctx.response.status = StatusCodes.OK
+  }
+)
 
-router.patch("/_builder/api/:appname/pages/:pagename/screen", async ctx => {
-  await renameScreen(
-    ctx.config,
-    ctx.params.appname,
-    ctx.params.pagename,
-    ctx.request.body.oldname,
-    ctx.request.body.newname
-  )
-  ctx.response.status = StatusCodes.OK
-})
+router.patch(
+  "/_builder/api/:appname/pages/:pagename/screen",
+  authorized(BUILDER),
+  async ctx => {
+    await renameScreen(
+      ctx.config,
+      ctx.params.appname,
+      ctx.params.pagename,
+      ctx.request.body.oldname,
+      ctx.request.body.newname
+    )
+    ctx.response.status = StatusCodes.OK
+  }
+)
 
-router.delete("/_builder/api/:appname/pages/:pagename/screen/*", async ctx => {
-  const name = ctx.request.path.replace(
-    `/_builder/api/${ctx.params.appname}/pages/${ctx.params.pagename}/screen/`,
-    ""
-  )
+router.delete(
+  "/_builder/api/:appname/pages/:pagename/screen/*",
+  authorized(BUILDER),
+  async ctx => {
+    const name = ctx.request.path.replace(
+      `/_builder/api/${ctx.params.appname}/pages/${ctx.params.pagename}/screen/`,
+      ""
+    )
 
-  await deleteScreen(
-    ctx.config,
-    ctx.params.appname,
-    ctx.params.pagename,
-    decodeURI(name)
-  )
+    await deleteScreen(
+      ctx.config,
+      ctx.params.appname,
+      ctx.params.pagename,
+      decodeURI(name)
+    )
 
-  ctx.response.status = StatusCodes.OK
-})
+    ctx.response.status = StatusCodes.OK
+  }
+)
 
 module.exports = router
diff --git a/packages/server/src/api/routes/record.js b/packages/server/src/api/routes/record.js
deleted file mode 100644
index fb0b543dc1..0000000000
--- a/packages/server/src/api/routes/record.js
+++ /dev/null
@@ -1,12 +0,0 @@
-const Router = require("@koa/router")
-const controller = require("../controllers/record")
-
-const router = Router()
-
-router
-  .get("/api/:instanceId/:viewName/records", controller.fetch)
-  .get("/api/:instanceId/records/:recordId", controller.find)
-  .post("/api/:instanceId/records", controller.save)
-  .delete("/api/:instanceId/records/:recordId/:revId", controller.destroy)
-
-module.exports = router
diff --git a/packages/server/src/api/routes/screen.js b/packages/server/src/api/routes/screen.js
index 3a167b6ef6..19823aab68 100644
--- a/packages/server/src/api/routes/screen.js
+++ b/packages/server/src/api/routes/screen.js
@@ -1,11 +1,17 @@
 const Router = require("@koa/router")
 const controller = require("../controllers/screen")
+const authorized = require("../../middleware/authorized")
+const { BUILDER } = require("../../utilities/accessLevels")
 
 const router = Router()
 
 router
-  .get("/api/:instanceId/screens", controller.fetch)
-  .post("/api/:instanceId/screens", controller.save)
-  .delete("/api/:instanceId/:screenId/:revId", controller.destroy)
+  .get("/api/:instanceId/screens", authorized(BUILDER), controller.fetch)
+  .post("/api/:instanceId/screens", authorized(BUILDER), controller.save)
+  .delete(
+    "/api/:instanceId/:screenId/:revId",
+    authorized(BUILDER),
+    controller.destroy
+  )
 
 module.exports = router
diff --git a/packages/server/src/api/routes/tests/accesslevel.spec.js b/packages/server/src/api/routes/tests/accesslevel.spec.js
new file mode 100644
index 0000000000..ef2d1575cd
--- /dev/null
+++ b/packages/server/src/api/routes/tests/accesslevel.spec.js
@@ -0,0 +1,184 @@
+const { 
+  createInstance, 
+  createClientDatabase,
+  createApplication,
+  createModel,
+  createView,
+  supertest,
+  defaultHeaders
+} = require("./couchTestUtils")
+const {
+  generateAdminPermissions,
+  generatePowerUserPermissions,
+  POWERUSER_LEVEL_ID,
+  ADMIN_LEVEL_ID,
+  READ_MODEL,
+  WRITE_MODEL,
+} = require("../../../utilities/accessLevels")
+
+describe("/accesslevels", () => {
+  let appId
+  let server
+  let request
+  let instanceId
+  let model
+  let view
+
+  beforeAll(async () => {
+    ({ request, server } = await supertest())
+    await createClientDatabase(request);
+    appId = (await createApplication(request))._id
+  });
+
+  afterAll(async () => {
+    server.close();
+  })
+
+  beforeEach(async () => {
+    instanceId = (await createInstance(request, appId))._id
+    model = await createModel(request, instanceId)
+    view = await createView(request, instanceId)
+  })
+
+  describe("create", () => {
+
+    it("returns a success message when level is successfully created", async () => {
+      const res = await request
+        .post(`/api/${instanceId}/accesslevels`)
+        .send({ name: "user" })
+        .set(defaultHeaders)
+        .expect('Content-Type', /json/)
+        .expect(200)
+
+      expect(res.res.statusMessage).toEqual("Access Level 'user' created successfully.")
+      expect(res.body._id).toBeDefined()
+      expect(res.body._rev).toBeDefined()
+      expect(res.body.permissions).toEqual([])
+    })
+
+  });
+
+  describe("fetch", () => {
+
+    it("should list custom levels, plus 2 default levels", async () => {
+      const createRes = await request
+        .post(`/api/${instanceId}/accesslevels`)
+        .send({ name: "user", permissions: [ { itemId: model._id, name: READ_MODEL }] })
+        .set(defaultHeaders)
+        .expect('Content-Type', /json/)
+        .expect(200)
+
+      const customLevel = createRes.body
+
+      const res = await request
+        .get(`/api/${instanceId}/accesslevels`)
+        .set(defaultHeaders)
+        .expect('Content-Type', /json/)
+        .expect(200)
+
+      expect(res.body.length).toBe(3)
+
+      const adminLevel = res.body.find(r => r._id === ADMIN_LEVEL_ID)
+      expect(adminLevel).toBeDefined()
+      expect(adminLevel.permissions).toEqual(await generateAdminPermissions(instanceId))
+
+      const powerUserLevel = res.body.find(r => r._id === POWERUSER_LEVEL_ID)
+      expect(powerUserLevel).toBeDefined()
+      expect(powerUserLevel.permissions).toEqual(await generatePowerUserPermissions(instanceId))
+
+      const customLevelFetched = res.body.find(r => r._id === customLevel._id)
+      expect(customLevelFetched.permissions).toEqual(customLevel.permissions)
+    })
+    
+  });
+
+  describe("destroy", () => {
+    it("should delete custom access level", async () => {
+      const createRes = await request
+        .post(`/api/${instanceId}/accesslevels`)
+        .send({ name: "user", permissions: [ { itemId: model._id, name: READ_MODEL } ] })
+        .set(defaultHeaders)
+        .expect('Content-Type', /json/)
+        .expect(200)
+
+      const customLevel = createRes.body
+
+      await request
+        .delete(`/api/${instanceId}/accesslevels/${customLevel._id}/${customLevel._rev}`)
+        .set(defaultHeaders)
+        .expect(200)
+
+      await request
+        .get(`/api/${instanceId}/accesslevels/${customLevel._id}`)
+        .set(defaultHeaders)
+        .expect(404)      
+    })
+  })
+
+  describe("patch", () => {
+    it("should add given permissions", async () => {
+      const createRes = await request
+        .post(`/api/${instanceId}/accesslevels`)
+        .send({ name: "user", permissions: [ { itemId: model._id, name: READ_MODEL }] })
+        .set(defaultHeaders)
+        .expect('Content-Type', /json/)
+        .expect(200)
+
+      const customLevel = createRes.body
+
+      await request
+        .patch(`/api/${instanceId}/accesslevels/${customLevel._id}`)
+        .send({
+          _rev: customLevel._rev,
+          addedPermissions:  [ { itemId: model._id, name: WRITE_MODEL } ] 
+        })
+        .set(defaultHeaders)
+        .expect('Content-Type', /json/)
+        .expect(200)
+
+      const finalRes = await request
+        .get(`/api/${instanceId}/accesslevels/${customLevel._id}`)
+        .set(defaultHeaders)
+        .expect(200) 
+
+      expect(finalRes.body.permissions.length).toBe(2)
+      expect(finalRes.body.permissions.some(p => p.name === WRITE_MODEL)).toBe(true)
+      expect(finalRes.body.permissions.some(p => p.name === READ_MODEL)).toBe(true)
+    })
+
+    it("should remove given permissions", async () => {
+      const createRes = await request
+        .post(`/api/${instanceId}/accesslevels`)
+        .send({ 
+          name: "user", 
+          permissions: [ 
+            { itemId: model._id, name: READ_MODEL },
+            { itemId: model._id, name: WRITE_MODEL },
+          ] 
+        })
+        .set(defaultHeaders)
+        .expect('Content-Type', /json/)
+        .expect(200)
+
+      const customLevel = createRes.body
+
+      await request
+        .patch(`/api/${instanceId}/accesslevels/${customLevel._id}`)
+        .send({
+          _rev: customLevel._rev,
+          removedPermissions:  [ { itemId: model._id, name: WRITE_MODEL }] 
+        })
+        .set(defaultHeaders)
+        .expect('Content-Type', /json/)
+        .expect(200)
+
+      const finalRes = await request
+        .get(`/api/${instanceId}/accesslevels/${customLevel._id}`)
+        .set(defaultHeaders)
+        .expect(200) 
+
+      expect(finalRes.body.permissions.length).toBe(1)
+      expect(finalRes.body.permissions.some(p => p.name === READ_MODEL)).toBe(true)
+    })
+  })
+});
diff --git a/packages/server/src/api/routes/tests/application.spec.js b/packages/server/src/api/routes/tests/application.spec.js
index 4e6766f71c..4c193613fd 100644
--- a/packages/server/src/api/routes/tests/application.spec.js
+++ b/packages/server/src/api/routes/tests/application.spec.js
@@ -1,9 +1,11 @@
 const { 
   createClientDatabase,  
   createApplication,
+  createInstance,
   destroyClientDatabase,
+  builderEndpointShouldBlockNormalUsers,
   supertest,
-  defaultHeaders
+  defaultHeaders,
 } = require("./couchTestUtils")
 
 describe("/applications", () => {
@@ -37,6 +39,18 @@ describe("/applications", () => {
       expect(res.res.statusMessage).toEqual("Application My App created successfully")
       expect(res.body._id).toBeDefined()
     })
+
+    it("should apply authorization to endpoint", async () => {
+      const otherApplication = await createApplication(request) 
+      const instance = await createInstance(request, otherApplication._id)
+      await builderEndpointShouldBlockNormalUsers({
+        request,
+        method: "POST",
+        url: `/api/applications`,
+        instanceId: instance._id,
+        body: { name: "My App" }
+      })
+    })
   })
 
   describe("fetch", () => {
@@ -53,6 +67,17 @@ describe("/applications", () => {
 
       expect(res.body.length).toBe(2)
     })
+
+    it("should apply authorization to endpoint", async () => {
+      const otherApplication = await createApplication(request) 
+      const instance = await createInstance(request, otherApplication._id)
+      await builderEndpointShouldBlockNormalUsers({
+        request,
+        method: "GET",
+        url: `/api/applications`,
+        instanceId: instance._id,
+      })
+    })
   })
 
 })
diff --git a/packages/server/src/api/routes/tests/couchTestUtils.js b/packages/server/src/api/routes/tests/couchTestUtils.js
index 85674e11d5..efec051ff8 100644
--- a/packages/server/src/api/routes/tests/couchTestUtils.js
+++ b/packages/server/src/api/routes/tests/couchTestUtils.js
@@ -2,6 +2,10 @@ const CouchDB = require("../../../db")
 const { create, destroy } = require("../../../db/clientDb")
 const supertest = require("supertest")
 const app = require("../../../app")
+const {
+  POWERUSER_LEVEL_ID,
+  generateAdminPermissions,
+} = require("../../../utilities/accessLevels")
 
 const TEST_CLIENT_ID = "test-client-id"
 
@@ -17,7 +21,7 @@ exports.supertest = async () => {
 
 exports.defaultHeaders = {
   Accept: "application/json",
-  Authorization: "Basic test-admin-secret",
+  Cookie: ["builder:token=test-admin-secret"],
 }
 
 exports.createModel = async (request, instanceId, model) => {
@@ -37,6 +41,31 @@ exports.createModel = async (request, instanceId, model) => {
   return res.body
 }
 
+exports.createRecord = async (request, instanceId, modelId, record) => {
+  record = record || {
+    modelId,
+    name: "test name",
+  }
+
+  const res = await request
+    .post(`/api/${instanceId}/${modelId}/records`)
+    .send(record)
+    .set(exports.defaultHeaders)
+  return res.body
+}
+
+exports.createView = async (request, instanceId, view) => {
+  view = view || {
+    map: "function(doc) { emit(doc[doc.key], doc._id); } ",
+  }
+
+  const res = await request
+    .post(`/api/${instanceId}/views`)
+    .set(exports.defaultHeaders)
+    .send(view)
+  return res.body
+}
+
 exports.createClientDatabase = async () => await create(TEST_CLIENT_ID)
 
 exports.createApplication = async (request, name = "test_application") => {
@@ -64,21 +93,170 @@ exports.createInstance = async (request, appId) => {
 exports.createUser = async (
   request,
   instanceId,
-  username = "bill",
-  password = "bills_password"
+  username = "babs",
+  password = "babs_password"
 ) => {
   const res = await request
     .post(`/api/${instanceId}/users`)
     .set(exports.defaultHeaders)
-    .send({ name: "Bill", username, password })
+    .send({
+      name: "Bill",
+      username,
+      password,
+      accessLevelId: POWERUSER_LEVEL_ID,
+    })
   return res.body
 }
 
+const createUser_WithOnePermission = async (
+  request,
+  instanceId,
+  permName,
+  itemId
+) => {
+  let permissions = await generateAdminPermissions(instanceId)
+  permissions = permissions.filter(
+    p => p.name === permName && p.itemId === itemId
+  )
+
+  return await createUserWithPermissions(
+    request,
+    instanceId,
+    permissions,
+    "onePermOnlyUser"
+  )
+}
+
+const createUser_WithAdminPermissions = async (request, instanceId) => {
+  let permissions = await generateAdminPermissions(instanceId)
+
+  return await createUserWithPermissions(
+    request,
+    instanceId,
+    permissions,
+    "adminUser"
+  )
+}
+
+const createUser_WithAllPermissionExceptOne = async (
+  request,
+  instanceId,
+  permName,
+  itemId
+) => {
+  let permissions = await generateAdminPermissions(instanceId)
+  permissions = permissions.filter(
+    p => !(p.name === permName && p.itemId === itemId)
+  )
+
+  return await createUserWithPermissions(
+    request,
+    instanceId,
+    permissions,
+    "allPermsExceptOneUser"
+  )
+}
+
+const createUserWithPermissions = async (
+  request,
+  instanceId,
+  permissions,
+  username
+) => {
+  const accessRes = await request
+    .post(`/api/${instanceId}/accesslevels`)
+    .send({ name: "TestLevel", permissions })
+    .set(exports.defaultHeaders)
+
+  const password = `password_${username}`
+  await request
+    .post(`/api/${instanceId}/users`)
+    .set(exports.defaultHeaders)
+    .send({
+      name: username,
+      username,
+      password,
+      accessLevelId: accessRes.body._id,
+    })
+
+  const db = new CouchDB(instanceId)
+  const designDoc = await db.get("_design/database")
+
+  const loginResult = await request
+    .post(`/api/authenticate`)
+    .set("Referer", `http://localhost:4001/${designDoc.metadata.applicationId}`)
+    .send({ username, password })
+
+  // returning necessary request headers
+  return {
+    Accept: "application/json",
+    Cookie: loginResult.headers["set-cookie"],
+  }
+}
+
+exports.testPermissionsForEndpoint = async ({
+  request,
+  method,
+  url,
+  body,
+  instanceId,
+  permissionName,
+  itemId,
+}) => {
+  const headers = await createUser_WithOnePermission(
+    request,
+    instanceId,
+    permissionName,
+    itemId
+  )
+
+  await createRequest(request, method, url, body)
+    .set(headers)
+    .expect(200)
+
+  const noPermsHeaders = await createUser_WithAllPermissionExceptOne(
+    request,
+    instanceId,
+    permissionName,
+    itemId
+  )
+
+  await createRequest(request, method, url, body)
+    .set(noPermsHeaders)
+    .expect(403)
+}
+
+exports.builderEndpointShouldBlockNormalUsers = async ({
+  request,
+  method,
+  url,
+  body,
+  instanceId,
+}) => {
+  const headers = await createUser_WithAdminPermissions(request, instanceId)
+
+  await createRequest(request, method, url, body)
+    .set(headers)
+    .expect(403)
+}
+
+const createRequest = (request, method, url, body) => {
+  let req
+
+  if (method === "POST") req = request.post(url).send(body)
+  else if (method === "GET") req = request.get(url)
+  else if (method === "DELETE") req = request.delete(url)
+  else if (method === "PATCH") req = request.patch(url).send(body)
+  else if (method === "PUT") req = request.put(url).send(body)
+
+  return req
+}
+
 exports.insertDocument = async (databaseId, document) => {
   const { id, ...documentFields } = document
   return await new CouchDB(databaseId).put({ _id: id, ...documentFields })
 }
 
 exports.destroyDocument = async (databaseId, documentId) => {
-  return await new CouchDB(databaseId).destroy(documentId);
-}
\ No newline at end of file
+  return await new CouchDB(databaseId).destroy(documentId)
+}
diff --git a/packages/server/src/api/routes/tests/model.spec.js b/packages/server/src/api/routes/tests/model.spec.js
index e32c94224f..65a44b677a 100644
--- a/packages/server/src/api/routes/tests/model.spec.js
+++ b/packages/server/src/api/routes/tests/model.spec.js
@@ -3,7 +3,9 @@ const {
   createModel, 
   supertest, 
   createClientDatabase, 
-  createApplication 
+  createApplication ,
+  defaultHeaders,
+  builderEndpointShouldBlockNormalUsers
 } = require("./couchTestUtils")
 
 describe("/models", () => {
@@ -38,7 +40,7 @@ describe("/models", () => {
             name: { type: "string" }
           }
         })
-        .set("Accept", "application/json")
+        .set(defaultHeaders)
         .expect('Content-Type', /json/)
         .expect(200)
         .end(async (err, res) => {
@@ -47,6 +49,22 @@ describe("/models", () => {
             done();
         });
       })
+
+      it("should apply authorization to endpoint", async () => {
+        await builderEndpointShouldBlockNormalUsers({
+          request,
+          method: "POST",
+          url: `/api/${instance._id}/models`,
+          instanceId: instance._id,
+          body: { 
+            name: "TestModel",
+            key: "name",
+            schema: {
+              name: { type: "string" }
+            }
+          }
+        })
+      })
     });
 
   describe("fetch", () => {
@@ -60,7 +78,7 @@ describe("/models", () => {
     it("returns all the models for that instance in the response body", done => {
       request
         .get(`/api/${instance._id}/models`)
-        .set("Accept", "application/json")
+        .set(defaultHeaders)
         .expect('Content-Type', /json/)
         .expect(200)
         .end(async (_, res) => {
@@ -69,7 +87,17 @@ describe("/models", () => {
             expect(fetchedModel.type).toEqual("model");            
             done();
         });
+    })
+
+    it("should apply authorization to endpoint", async () => {
+        await builderEndpointShouldBlockNormalUsers({
+          request,
+          method: "GET",
+          url: `/api/${instance._id}/models`,
+          instanceId: instance._id,
+        })
       })
+
     });
 
   describe("destroy", () => {
@@ -83,7 +111,7 @@ describe("/models", () => {
     it("returns a success response when a model is deleted.", done => {
       request
         .delete(`/api/${instance._id}/models/${testModel._id}/${testModel._rev}`)
-        .set("Accept", "application/json")
+        .set(defaultHeaders)
         .expect('Content-Type', /json/)
         .expect(200)
         .end(async (_, res) => {
@@ -91,5 +119,15 @@ describe("/models", () => {
             done();
         });
       })
-    });
+
+    it("should apply authorization to endpoint", async () => {
+      await builderEndpointShouldBlockNormalUsers({
+        request,
+        method: "DELETE",
+        url: `/api/${instance._id}/models/${testModel._id}/${testModel._rev}`,
+        instanceId: instance._id,
+      })
+    })
+
+  });
 });
diff --git a/packages/server/src/api/routes/tests/record.spec.js b/packages/server/src/api/routes/tests/record.spec.js
index b75b245135..c2ec2927f9 100644
--- a/packages/server/src/api/routes/tests/record.spec.js
+++ b/packages/server/src/api/routes/tests/record.spec.js
@@ -3,8 +3,15 @@ const {
   createClientDatabase,
   createInstance, 
   createModel,
-  supertest
+  supertest,
+  defaultHeaders,
+  testPermissionsForEndpoint,
+  shouldReturn403WhenNoPermission,
+  shouldReturn200WithOnlyOnePermission,
 } = require("./couchTestUtils");
+const {
+  WRITE_MODEL, READ_MODEL
+} = require("../../../utilities/accessLevels")
 
 describe("/records", () => {
   let request
@@ -24,25 +31,25 @@ describe("/records", () => {
     server.close();
   })
 
-  describe("save, load, update, delete", () => {
+  beforeEach(async () => {
+    instance = await createInstance(request, app._id)
+    model = await createModel(request, instance._id)
+    record = {
+      name: "Test Contact",
+      status: "new",
+      modelId: model._id
+    }
+  })
 
-    beforeEach(async () => {
-      instance = await createInstance(request, app._id)
-      model = await createModel(request, instance._id)
-      record = {
-        name: "Test Contact",
-        status: "new",
-        modelId: model._id
-      }
-    })
+  const createRecord = async r => 
+    await request
+      .post(`/api/${instance._id}/${model._id}/records`)
+      .send(r || record)
+      .set(defaultHeaders)
+      .expect('Content-Type', /json/)
+      .expect(200)
 
-    const createRecord = async r => 
-      await request
-        .post(`/api/${instance._id}/records`)
-        .send(r || record)
-        .set("Accept", "application/json")
-        .expect('Content-Type', /json/)
-        .expect(200)
+  describe("save", () => {   
 
     it("returns a success message when the record is created", async () => {
       const res = await createRecord()
@@ -51,33 +58,47 @@ describe("/records", () => {
       expect(res.body._rev).toBeDefined()
     })
 
+    it("should apply authorization to endpoint", async () => {
+      await testPermissionsForEndpoint({
+        request,
+        method: "POST",
+        url: `/api/${instance._id}/${model._id}/records`,
+        body: record,
+        instanceId: instance._id,
+        permissionName: WRITE_MODEL,
+        itemId: model._id,
+      })
+    })
+
     it("updates a record successfully", async () => {
       const rec = await createRecord()
       const existing = rec.body
 
       const res = await request
-        .post(`/api/${instance._id}/records`)
+        .post(`/api/${instance._id}/${model._id}/records`)
         .send({
           _id: existing._id,
           _rev: existing._rev,
           modelId: model._id,
           name: "Updated Name",
         })
-        .set("Accept", "application/json")
+        .set(defaultHeaders)
         .expect('Content-Type', /json/)
         .expect(200)
       
       expect(res.res.statusMessage).toEqual(`${model.name} updated successfully.`)
       expect(res.body.name).toEqual("Updated Name")
     })
+  })
 
+  describe("find", () => {   
     it("should load a record", async () => {
       const rec = await createRecord()
       const existing = rec.body
 
       const res = await request
-        .get(`/api/${instance._id}/records/${existing._id}`)
-        .set("Accept", "application/json")
+        .get(`/api/${instance._id}/${model._id}/records/${existing._id}`)
+        .set(defaultHeaders)
         .expect('Content-Type', /json/)
         .expect(200)
 
@@ -89,6 +110,31 @@ describe("/records", () => {
       })
     })
 
+    it("load should return 404 when record does not exist", async () => {
+      await createRecord()
+      await request
+        .get(`/api/${instance._id}/${model._id}/records/not-a-valid-id`)
+        .set(defaultHeaders)
+        .expect('Content-Type', /json/)
+        .expect(404)
+    })
+
+    it("should apply authorization to endpoint", async () => {
+      const rec = await createRecord()
+      await testPermissionsForEndpoint({
+        request,
+        method: "GET",
+        url: `/api/${instance._id}/${model._id}/records/${rec.body._id}`,
+        instanceId: instance._id,
+        permissionName: READ_MODEL,
+        itemId: model._id,
+      })
+    })
+
+  })
+
+  describe("fetch", () => {   
+
     it("should list all records for given modelId", async () => {
       const newRecord = {
         modelId: model._id,
@@ -99,8 +145,8 @@ describe("/records", () => {
       await createRecord(newRecord)
 
       const res = await request
-        .get(`/api/${instance._id}/all_${newRecord.modelId}/records`)
-        .set("Accept", "application/json")
+        .get(`/api/${instance._id}/${model._id}/records`)
+        .set(defaultHeaders)
         .expect('Content-Type', /json/)
         .expect(200)
 
@@ -109,13 +155,46 @@ describe("/records", () => {
       expect(res.body.find(r => r.name === record.name)).toBeDefined()
     })
 
-    it("load should return 404 when record does not exist", async () => {
-      await createRecord()
+    it("should apply authorization to endpoint", async () => {
+      await testPermissionsForEndpoint({
+        request,
+        method: "GET",
+        url: `/api/${instance._id}/${model._id}/records`,
+        instanceId: instance._id,
+        permissionName: READ_MODEL,
+        itemId: model._id,
+      })
+    })
+
+  })
+
+  describe("delete", () => {   
+
+    it("should remove a record", async () => {
+      const createRes = await createRecord()
+
       await request
-        .get(`/api/${instance._id}/records/not-a-valid-id`)
-        .set("Accept", "application/json")
-        .expect('Content-Type', /json/)
+        .delete(`/api/${instance._id}/${model._id}/records/${createRes.body._id}/${createRes.body._rev}`)
+        .set(defaultHeaders)
+        .expect(200)
+
+      await request
+        .get(`/api/${instance._id}/${model._id}/records/${createRes.body._id}`)
+        .set(defaultHeaders)
         .expect(404)
     })
+
+    it("should apply authorization to endpoint", async () => {
+      const createRes = await createRecord()
+      await testPermissionsForEndpoint({
+        request,
+        method: "DELETE",
+        url: `/api/${instance._id}/${model._id}/records/${createRes.body._id}/${createRes.body._rev}`,
+        instanceId: instance._id,
+        permissionName: WRITE_MODEL,
+        itemId: model._id,
+      })
+    })
+
   })
 })
diff --git a/packages/server/src/api/routes/tests/user.spec.js b/packages/server/src/api/routes/tests/user.spec.js
index bba748daff..d950f03c3d 100644
--- a/packages/server/src/api/routes/tests/user.spec.js
+++ b/packages/server/src/api/routes/tests/user.spec.js
@@ -5,7 +5,13 @@ const {
   supertest,
   defaultHeaders,
   createUser,
+  testPermissionsForEndpoint,
 } = require("./couchTestUtils")
+const { 
+  POWERUSER_LEVEL_ID, 
+  LIST_USERS, 
+  USER_MANAGEMENT 
+} = require("../../../utilities/accessLevels")
 
 describe("/users", () => {
   let request
@@ -43,6 +49,17 @@ describe("/users", () => {
       expect(res.body.find(u => u.username === "pam")).toBeDefined()
     })
 
+    it("should apply authorization to endpoint", async () => {
+      await createUser(request, instance._id, "brenda", "brendas_password")
+      await testPermissionsForEndpoint({
+        request,
+        method: "GET",
+        url: `/api/${instance._id}/users`,
+        instanceId: instance._id,
+        permissionName: LIST_USERS,
+      })
+    })
+
   })
 
   describe("create", () => {
@@ -51,12 +68,24 @@ describe("/users", () => {
       const res = await request
         .post(`/api/${instance._id}/users`)
         .set(defaultHeaders)
-        .send({ name: "Bill", username: "bill", password: "bills_password" })
+        .send({ name: "Bill", username: "bill", password: "bills_password", accessLevelId: POWERUSER_LEVEL_ID })
         .expect(200)
         .expect('Content-Type', /json/)
 
       expect(res.res.statusMessage).toEqual("User created successfully."); 
       expect(res.body._id).toBeUndefined()
     })
+
+    it("should apply authorization to endpoint", async () => {
+      await testPermissionsForEndpoint({
+        request,
+        method: "POST",
+        body: { name: "brandNewUser", username: "brandNewUser", password: "yeeooo", accessLevelId: POWERUSER_LEVEL_ID },
+        url: `/api/${instance._id}/users`,
+        instanceId: instance._id,
+        permissionName: USER_MANAGEMENT,
+      })
+    })
+
   });
 })
diff --git a/packages/server/src/api/routes/tests/view.spec.js b/packages/server/src/api/routes/tests/view.spec.js
index e2aeececce..df2c6e0048 100644
--- a/packages/server/src/api/routes/tests/view.spec.js
+++ b/packages/server/src/api/routes/tests/view.spec.js
@@ -3,9 +3,13 @@ const {
   createApplication,
   createInstance, 
   createModel,
+  createRecord,
   supertest,
-  defaultHeaders
+  defaultHeaders,
+  testPermissionsForEndpoint,
+  builderEndpointShouldBlockNormalUsers,
 } = require("./couchTestUtils")
+const { READ_VIEW } = require("../../../utilities/accessLevels")
 
 describe("/views", () => {
   let request
@@ -34,11 +38,10 @@ describe("/views", () => {
     .send({ 
       name: "TestView",
       map: `function(doc) {
-        if (doc.id) {
+        if (doc.type === 'record') {
           emit(doc.name, doc._id);
         }
       }`,
-      reduce: `function(keys, values) { }`
     })
     .set(defaultHeaders)
     .expect('Content-Type', /json/)
@@ -51,14 +54,28 @@ describe("/views", () => {
       expect(res.res.statusMessage).toEqual("View TestView created successfully.");
       expect(res.body.name).toEqual("TestView");
     })
+
+    it("should apply authorization to endpoint", async () => {
+      await builderEndpointShouldBlockNormalUsers({
+        request,
+        method: "POST",
+        url: `/api/${instance._id}/views`,
+        body: { 
+          name: "TestView",
+          map: `function(doc) {
+            if (doc.id) {
+              emit(doc.name, doc._id);
+            }
+          }`,
+          reduce: `function(keys, values) { }`
+        },
+        instanceId: instance._id,
+      })
+    })
   });
 
   describe("fetch", () => {
 
-    beforeEach(async () => {
-      model = await createModel(request, instance._id);
-    });
-
     it("should only return custom views", async () => {
       const view = await createView()
       const res = await request
@@ -69,5 +86,46 @@ describe("/views", () => {
       expect(res.body.length).toBe(1)
       expect(res.body.find(v => v.name === view.body.name)).toBeDefined()
     })
+
+    it("should apply authorization to endpoint", async () => {
+      await builderEndpointShouldBlockNormalUsers({
+        request,
+        method: "GET",
+        url: `/api/${instance._id}/views`,
+        instanceId: instance._id,
+      })
+    })
   });
+
+  describe("query", () => {
+
+    beforeEach(async () => {
+      model = await createModel(request, instance._id);
+    });
+
+    it("should return records from custom view", async () => {
+      await createView()
+      const rec1 = await createRecord(request, instance._id, model._id)
+      await createRecord(request, instance._id, model._id)
+      const res = await request
+        .get(`/api/${instance._id}/views/TestView`)
+        .set(defaultHeaders)
+        .expect('Content-Type', /json/)
+        .expect(200)
+      expect(res.body.length).toBe(2)
+      expect(res.body.find(r => r._id === rec1._id)).toBeDefined()
+    })
+
+    it("should apply authorization to endpoint", async () => {
+      await createView()
+      await testPermissionsForEndpoint({
+        request,
+        method: "GET",
+        url: `/api/${instance._id}/views/TestView`,
+        instanceId: instance._id,
+        permissionName: READ_VIEW,
+        itemId: "TestView",
+      })
+    })
+  })
 });
diff --git a/packages/server/src/api/routes/tests/workflow.spec.js b/packages/server/src/api/routes/tests/workflow.spec.js
index 4a4d91228e..0e0ce6bfc6 100644
--- a/packages/server/src/api/routes/tests/workflow.spec.js
+++ b/packages/server/src/api/routes/tests/workflow.spec.js
@@ -5,7 +5,8 @@ const {
   defaultHeaders,
   supertest,
   insertDocument,
-  destroyDocument
+  destroyDocument,
+  builderEndpointShouldBlockNormalUsers
 } = require("./couchTestUtils")
 
 const TEST_WORKFLOW = {
@@ -71,6 +72,16 @@ describe("/workflows", () => {
         expect(res.body.message).toEqual("Workflow created successfully");
         expect(res.body.workflow.name).toEqual("My Workflow");
     })
+
+    it("should apply authorization to endpoint", async () => {
+      await builderEndpointShouldBlockNormalUsers({
+        request,
+        method: "POST",
+        url: `/api/${instance._id}/workflows`,
+        instanceId: instance._id,
+        body: TEST_WORKFLOW
+      })
+    })
   })
 
   describe("update", () => {
@@ -103,6 +114,15 @@ describe("/workflows", () => {
 
         expect(res.body[0]).toEqual(expect.objectContaining(TEST_WORKFLOW));
     })
+
+    it("should apply authorization to endpoint", async () => {
+      await builderEndpointShouldBlockNormalUsers({
+        request,
+        method: "GET",
+        url: `/api/${instance._id}/workflows`,
+        instanceId: instance._id,
+      })
+    })
   })
 
   describe("find", () => {
@@ -116,6 +136,16 @@ describe("/workflows", () => {
 
         expect(res.body).toEqual(expect.objectContaining(TEST_WORKFLOW));
     })
+
+    it("should apply authorization to endpoint", async () => {
+      await createWorkflow();
+      await builderEndpointShouldBlockNormalUsers({
+        request,
+        method: "GET",
+        url: `/api/${instance._id}/workflows/${workflow.id}`,
+        instanceId: instance._id,
+      })
+    })
   })
 
   describe("destroy", () => {
@@ -129,5 +159,15 @@ describe("/workflows", () => {
 
         expect(res.body.id).toEqual(TEST_WORKFLOW._id);
     })
+
+    it("should apply authorization to endpoint", async () => {
+      await createWorkflow();
+      await builderEndpointShouldBlockNormalUsers({
+        request,
+        method: "DELETE",
+        url: `/api/${instance._id}/workflows/${workflow.id}/${workflow._rev}`,
+        instanceId: instance._id,
+      })
+    })
   })
 });
diff --git a/packages/server/src/api/routes/user.js b/packages/server/src/api/routes/user.js
index 61f4a096fb..20e17c7473 100644
--- a/packages/server/src/api/routes/user.js
+++ b/packages/server/src/api/routes/user.js
@@ -1,12 +1,26 @@
 const Router = require("@koa/router")
 const controller = require("../controllers/user")
+const authorized = require("../../middleware/authorized")
+const { USER_MANAGEMENT, LIST_USERS } = require("../../utilities/accessLevels")
 
 const router = Router()
 
 router
-  .get("/api/:instanceId/users", controller.fetch)
-  .get("/api/:instanceId/users/:username", controller.find)
-  .post("/api/:instanceId/users", controller.create)
-  .delete("/api/:instanceId/users/:username", controller.destroy)
+  .get("/api/:instanceId/users", authorized(LIST_USERS), controller.fetch)
+  .get(
+    "/api/:instanceId/users/:username",
+    authorized(USER_MANAGEMENT),
+    controller.find
+  )
+  .post(
+    "/api/:instanceId/users",
+    authorized(USER_MANAGEMENT),
+    controller.create
+  )
+  .delete(
+    "/api/:instanceId/users/:username",
+    authorized(USER_MANAGEMENT),
+    controller.destroy
+  )
 
 module.exports = router
diff --git a/packages/server/src/api/routes/view.js b/packages/server/src/api/routes/view.js
index f7ce9b41b5..193ece1cdf 100644
--- a/packages/server/src/api/routes/view.js
+++ b/packages/server/src/api/routes/view.js
@@ -1,12 +1,20 @@
 const Router = require("@koa/router")
-const controller = require("../controllers/view")
+const viewController = require("../controllers/view")
+const recordController = require("../controllers/record")
+const authorized = require("../../middleware/authorized")
+const { BUILDER, READ_VIEW } = require("../../utilities/accessLevels")
 
 const router = Router()
 
 router
-  .get("/api/:instanceId/views", controller.fetch)
+  .get(
+    "/api/:instanceId/views/:viewName",
+    authorized(READ_VIEW, ctx => ctx.params.viewName),
+    recordController.fetchView
+  )
+  .get("/api/:instanceId/views", authorized(BUILDER), viewController.fetch)
   // .patch("/api/:databaseId/views", controller.update);
   // .delete("/api/:instanceId/views/:viewId/:revId", controller.destroy);
-  .post("/api/:instanceId/views", controller.create)
+  .post("/api/:instanceId/views", authorized(BUILDER), viewController.create)
 
 module.exports = router
diff --git a/packages/server/src/api/routes/workflow.js b/packages/server/src/api/routes/workflow.js
index 5bd501bd76..df69ca32f6 100644
--- a/packages/server/src/api/routes/workflow.js
+++ b/packages/server/src/api/routes/workflow.js
@@ -1,15 +1,28 @@
 const Router = require("@koa/router")
 const controller = require("../controllers/workflow")
+const authorized = require("../../middleware/authorized")
+const { BUILDER } = require("../../utilities/accessLevels")
 
 const router = Router()
 
 router
-  .get("/api/:instanceId/workflows", controller.fetch)
-  .get("/api/:instanceId/workflows/:id", controller.find)
-  .get("/api/:instanceId/workflows/:id/:action", controller.fetchActionScript)
-  .post("/api/:instanceId/workflows", controller.create)
-  .post("/api/:instanceId/workflows/action", controller.executeAction)
-  .put("/api/:instanceId/workflows", controller.update)
-  .delete("/api/:instanceId/workflows/:id/:rev", controller.destroy)
+  .get("/api/:instanceId/workflows", authorized(BUILDER), controller.fetch)
+  .get("/api/:instanceId/workflows/:id", authorized(BUILDER), controller.find)
+  .get(
+    "/api/:instanceId/workflows/:id/:action",
+    authorized(BUILDER),
+    controller.fetchActionScript
+  )
+  .put("/api/:instanceId/workflows", authorized(BUILDER), controller.update)
+  .post(
+    "/api/:instanceId/workflows/action",
+    authorized(BUILDER),
+    controller.executeAction
+  )
+  .delete(
+    "/api/:instanceId/workflows/:id",
+    authorized(BUILDER),
+    controller.destroy
+  )
 
 module.exports = router
diff --git a/packages/server/src/middleware/authenticated.js b/packages/server/src/middleware/authenticated.js
index 1bcc5575c3..9308d1b173 100644
--- a/packages/server/src/middleware/authenticated.js
+++ b/packages/server/src/middleware/authenticated.js
@@ -1,6 +1,11 @@
 const jwt = require("jsonwebtoken")
 const STATUS_CODES = require("../utilities/statusCodes")
 const env = require("../environment")
+const accessLevelController = require("../api/controllers/accesslevel")
+const {
+  ADMIN_LEVEL_ID,
+  POWERUSER_LEVEL_ID,
+} = require("../utilities/accessLevels")
 
 module.exports = async (ctx, next) => {
   if (ctx.path === "/_builder") {
@@ -8,8 +13,9 @@ module.exports = async (ctx, next) => {
     return
   }
 
-  if (ctx.isDev && ctx.cookies.get("builder:token") === env.ADMIN_SECRET) {
+  if (ctx.cookies.get("builder:token") === env.ADMIN_SECRET) {
     ctx.isAuthenticated = true
+    ctx.isBuilder = true
     await next()
     return
   }
@@ -23,7 +29,15 @@ module.exports = async (ctx, next) => {
   }
 
   try {
-    ctx.jwtPayload = jwt.verify(token, ctx.config.jwtSecret)
+    const jwtPayload = jwt.verify(token, ctx.config.jwtSecret)
+
+    ctx.user = {
+      ...jwtPayload,
+      accessLevel: await getAccessLevel(
+        jwtPayload.instanceId,
+        jwtPayload.accessLevelId
+      ),
+    }
     ctx.isAuthenticated = true
   } catch (err) {
     ctx.throw(err.status || STATUS_CODES.FORBIDDEN, err.text)
@@ -31,3 +45,25 @@ module.exports = async (ctx, next) => {
 
   await next()
 }
+
+const getAccessLevel = async (instanceId, accessLevelId) => {
+  if (
+    accessLevelId === POWERUSER_LEVEL_ID ||
+    accessLevelId === ADMIN_LEVEL_ID
+  ) {
+    return {
+      _id: accessLevelId,
+      name: accessLevelId,
+      permissions: [],
+    }
+  }
+
+  const findAccessContext = {
+    params: {
+      levelId: accessLevelId,
+      instanceId,
+    },
+  }
+  await accessLevelController.find(findAccessContext)
+  return findAccessContext.body
+}
diff --git a/packages/server/src/middleware/authorized.js b/packages/server/src/middleware/authorized.js
new file mode 100644
index 0000000000..b07715af36
--- /dev/null
+++ b/packages/server/src/middleware/authorized.js
@@ -0,0 +1,58 @@
+const {
+  adminPermissions,
+  ADMIN_LEVEL_ID,
+  POWERUSER_LEVEL_ID,
+  BUILDER,
+} = require("../utilities/accessLevels")
+
+module.exports = (permName, getItemId) => async (ctx, next) => {
+  if (!ctx.isAuthenticated) {
+    ctx.throw(403, "Session not authenticated")
+  }
+
+  if (ctx.isBuilder) {
+    await next()
+    return
+  }
+
+  if (permName === BUILDER) {
+    ctx.throw(403, "Not Authorized")
+    return
+  }
+
+  if (!ctx.user) {
+    ctx.throw(403, "User not found")
+  }
+
+  const permissionId = ({ name, itemId }) => name + (itemId ? `-${itemId}` : "")
+
+  if (ctx.user.accessLevel._id === ADMIN_LEVEL_ID) {
+    await next()
+    return
+  }
+
+  const thisPermissionId = permissionId({
+    name: permName,
+    itemId: getItemId && getItemId(ctx),
+  })
+
+  // power user has everything, except the admin specific perms
+  if (
+    ctx.user.accessLevel._id === POWERUSER_LEVEL_ID &&
+    !adminPermissions.map(permissionId).includes(thisPermissionId)
+  ) {
+    await next()
+    return
+  }
+
+  if (
+    ctx.user.accessLevel.permissions
+      .map(permissionId)
+      .includes(thisPermissionId)
+  ) {
+    await next()
+    return
+  }
+
+  ctx.throw(403, "Not Authorized")
+}
diff --git a/packages/server/src/utilities/accessLevels.js b/packages/server/src/utilities/accessLevels.js
new file mode 100644
index 0000000000..b859d35f61
--- /dev/null
+++ b/packages/server/src/utilities/accessLevels.js
@@ -0,0 +1,79 @@
+const viewController = require("../api/controllers/view")
+const modelController = require("../api/controllers/model")
+const workflowController = require("../api/controllers/workflow")
+
+exports.ADMIN_LEVEL_ID = "ADMIN"
+exports.POWERUSER_LEVEL_ID = "POWER_USER"
+
+exports.READ_MODEL = "read-model"
+exports.WRITE_MODEL = "write-model"
+exports.READ_VIEW = "read-view"
+exports.EXECUTE_WORKFLOW = "execute-workflow"
+exports.USER_MANAGEMENT = "user-management"
+exports.BUILDER = "builder"
+exports.LIST_USERS = "list-users"
+
+exports.adminPermissions = [
+  {
+    name: exports.USER_MANAGEMENT,
+  },
+]
+
+exports.generateAdminPermissions = async instanceId => [
+  ...exports.adminPermissions,
+  ...(await exports.generatePowerUserPermissions(instanceId)),
+]
+
+exports.generatePowerUserPermissions = async instanceId => {
+  const fetchModelsCtx = {
+    params: {
+      instanceId,
+    },
+  }
+  await modelController.fetch(fetchModelsCtx)
+  const models = fetchModelsCtx.body
+
+  const fetchViewsCtx = {
+    params: {
+      instanceId,
+    },
+  }
+  await viewController.fetch(fetchViewsCtx)
+  const views = fetchViewsCtx.body
+
+  const fetchWorkflowsCtx = {
+    params: {
+      instanceId,
+    },
+  }
+  await workflowController.fetch(fetchWorkflowsCtx)
+  const workflows = fetchWorkflowsCtx.body
+
+  const readModelPermissions = models.map(m => ({
+    itemId: m._id,
+    name: exports.READ_MODEL,
+  }))
+
+  const writeModelPermissions = models.map(m => ({
+    itemId: m._id,
+    name: exports.WRITE_MODEL,
+  }))
+
+  const viewPermissions = views.map(v => ({
+    itemId: v.name,
+    name: exports.READ_VIEW,
+  }))
+
+  const executeWorkflowPermissions = workflows.map(w => ({
+    itemId: w._id,
+    name: exports.EXECUTE_WORKFLOW,
+  }))
+
+  return [
+    ...readModelPermissions,
+    ...writeModelPermissions,
+    ...viewPermissions,
+    ...executeWorkflowPermissions,
+    { name: exports.LIST_USERS },
+  ]
+}