diff --git a/database/migrations/20210000000014-create-survey-excerpts-table.js b/database/migrations/20210000000014-create-survey-excerpts-table.js
new file mode 100644
index 0000000000000000000000000000000000000000..cdf8c32ca0bb284fe7c69e9f2b7d89ae0e066964
--- /dev/null
+++ b/database/migrations/20210000000014-create-survey-excerpts-table.js
@@ -0,0 +1,66 @@
+module.exports = {
+	up: (migration, Types) => {
+		return migration.createTable('survey_excerpts', {
+			id: {
+				type: Types.UUID,
+				primaryKey: true,
+				defaultValue: Types.UUIDV4,
+				allowNull: false,
+			},
+			survey_id: {
+				type: Types.UUID,
+				allowNull: false,
+				references: {
+					model: 'surveys',
+					key: 'id',
+				},
+			},
+			shortcode: {
+				type: Types.TEXT,
+				allowNull: false,
+			},
+			name: {
+				type: Types.TEXT,
+				allowNull: false,
+			},
+			view_name: {
+				type: Types.TEXT,
+				allowNull: false,
+			},
+			parameters: {
+				type: Types.JSONB,
+				defaultValue: {},
+				allowNull: false,
+			},
+			filters: {
+				type: Types.JSONB,
+				defaultValue: {},
+				allowNull: false,
+			},
+			meta: {
+				type: Types.JSONB,
+				defaultValue: {},
+				allowNull: false,
+			},
+			created_at: {
+				type: Types.DATE,
+				defaultValue: Types.fn('now'),
+				allowNull: false,
+			},
+			updated_at: {
+				type: Types.DATE,
+				defaultValue: Types.fn('now'),
+				allowNull: false,
+			},
+			deleted_at: {
+				type: Types.DATE,
+				defaultValue: null,
+				allowNull: true,
+			},
+		})
+	},
+
+	down: (migration, Types) => {
+		return migration.dropTable('survey_excerpts')
+	},
+}
diff --git a/src/database/models/Survey.js b/src/database/models/Survey.js
index dbe253de1053f673195a60087c8d68261d072254..bca039b0e7ddccea11b3c2497280e516d3514d16 100644
--- a/src/database/models/Survey.js
+++ b/src/database/models/Survey.js
@@ -4,6 +4,7 @@ const BaseModel = require('./BaseModel')
 class Survey extends BaseModel {
 	static associate(models) {
 		this.hasMany(models.SurveyUser, { foreignKey: 'survey_id' })
+		this.hasMany(models.SurveyExcerpt, { foreignKey: 'survey_id' })
 		this.belongsToMany(models.User, { through: models.SurveyUser, timestamps: false })
 	}
 
diff --git a/src/database/models/SurveyExcerpt.js b/src/database/models/SurveyExcerpt.js
new file mode 100644
index 0000000000000000000000000000000000000000..a46bb472afa308fd4294e31d701915fe47c49721
--- /dev/null
+++ b/src/database/models/SurveyExcerpt.js
@@ -0,0 +1,65 @@
+const timestamps = require('./properties/timestamps')
+const BaseModel = require('./BaseModel')
+
+class SurveyExcerpt extends BaseModel {
+	static associate(models) {
+		this.belongsTo(models.Survey, { foreignKey: 'survey_id' })
+	}
+
+	toJSON() {
+		return {
+			id: this.id,
+			name: this.name,
+			shortcode: this.shortcode,
+			meta: this.meta,
+		}
+	}
+}
+
+module.exports = (sequelize, DataTypes) => {
+	SurveyExcerpt.init(
+		Object.assign(
+			{
+				id: {
+					type: DataTypes.UUID,
+					primaryKey: true,
+					defaultValue: DataTypes.UUIDV4,
+					validate: {
+						isUUID: 4,
+					},
+				},
+				shortcode: {
+					type: DataTypes.TEXT,
+					allowNull: false,
+				},
+				view_name: {
+					type: DataTypes.TEXT,
+					allowNull: false,
+				},
+				parameters: {
+					type: DataTypes.JSONB,
+					defaultValue: {},
+					allowNull: false,
+				},
+				filters: {
+					type: DataTypes.JSONB,
+					defaultValue: {},
+					allowNull: false,
+				},
+				meta: {
+					type: DataTypes.JSONB,
+					defaultValue: {},
+					allowNull: false,
+				},
+			},
+			timestamps(DataTypes),
+		),
+		{
+			sequelize,
+			paranoid: true,
+			tableName: 'survey_excerpts',
+		},
+	)
+
+	return SurveyExcerpt
+}
diff --git a/src/domain/auth/OAuthFlow.js b/src/domain/auth/OAuthFlow.js
index 0a11e47442e31c0e9ec6e2c6325dfb69c463010f..ce25c979f4e629ce78b374d5feb6cacd34ac6016 100644
--- a/src/domain/auth/OAuthFlow.js
+++ b/src/domain/auth/OAuthFlow.js
@@ -158,7 +158,7 @@ const scopeDescriptionMap = exports.validScopes = {
 		icon: 'openid',
 		name: 'Open ID Connect',
 		description:
-			'Use your Jetsam account to log in to a third party services. This will provide the third party with your name (if provided), your email address and your Jetsam account ID.'
+			'Use your Jetsam app account to log in to another app or website. This will provide the operator with your name and email address.'
 	},
 	'metrics:create': {
 		name: 'Create Metrics',
diff --git a/src/http/controllers/api/content.js b/src/http/controllers/api/content.js
index 1f126df9f4a691a6499f3268974f72223aad764c..0bf47a95f53516a8ea61e1641e83ed85de54619b 100644
--- a/src/http/controllers/api/content.js
+++ b/src/http/controllers/api/content.js
@@ -98,38 +98,50 @@ function pointFromString(str) {
 	}
 }
 
+function bufferFromCorners(pointFrom, pointTo) {
+	const fromPoint = pointFromString(pointFrom)
+	const toPoint = pointFromString(pointTo)
+
+	const minFromLong = Math.min(fromPoint.longitude, toPoint.longitude)
+	const maxFromLong = Math.max(fromPoint.longitude, toPoint.longitude)
+	const minFromLat = Math.min(fromPoint.latitude, toPoint.latitude)
+	const maxFromLat = Math.max(fromPoint.latitude, toPoint.latitude)
+
+	return [
+		[minFromLong, minFromLat],
+		[minFromLong, maxFromLat],
+		[maxFromLong, maxFromLat],
+		[maxFromLong, minFromLat],
+		[minFromLong, minFromLat],
+	]
+}
+
+function bufferFromPolygon(poly) {
+	return poly.split(';')
+		.map(s => s.trim())
+		.filter(Boolean)
+		.map(s => pointFromString(s))
+}
+
 exports.getWithin = async ctx => {
 	const {
 		point_from,
 		point_to,
-		date_from,
+		within,
+		date_from = moment.utc().subtract(30, 'days'),
 		date_to = moment.utc(),
 		types = '',
 		format = 'full',
 	} = ctx.request.query
 
-	const fromPoint = pointFromString(point_from)
-	const toPoint = pointFromString(point_to)
+	const pointBufferData = within ? bufferFromPolygon(within) : bufferFromCorners(point_from, point_to)
+	const pointBuffer = pointBufferData.map(pb => pb.map(Number).join(' ')).join(',')
 
 	const fromDate = moment.utc(date_from)
 	const toDate = moment.utc(date_to)
 
 	const metricTypes = splitString(types)
 
-	const minFromLong = Math.min(fromPoint.longitude, toPoint.longitude)
-	const maxFromLong = Math.max(fromPoint.longitude, toPoint.longitude)
-	const minFromLat = Math.min(fromPoint.latitude, toPoint.latitude)
-	const maxFromLat = Math.max(fromPoint.latitude, toPoint.latitude)
-
-	const pointBuffer = [
-		[minFromLong, minFromLat],
-		[minFromLong, maxFromLat],
-		[maxFromLong, maxFromLat],
-		[maxFromLong, minFromLat],
-		[minFromLong, minFromLat],
-	]
-		.map(pb => pb.map(Number).join(' '))
-		.join(',')
 
 	const query =
 		format === 'marker'
@@ -139,8 +151,8 @@ exports.getWithin = async ctx => {
 	const metrics = await query(
 		pointBuffer,
 		metricTypes,
-		// fromDate.toISOString(),
-		moment().subtract(12, 'months').toISOString(),
+		fromDate.toISOString(),
+		// moment().subtract(12, 'months').toISOString(),
 		toDate.toISOString(),
 	)
 
diff --git a/src/http/controllers/api/v2/surveys.js b/src/http/controllers/api/v2/surveys.js
index 07127a5e9b9db3bed7fb359bd2f9ac3ed251092f..24dce09034d3fd3178db1b0dc49e46ce068c4dbe 100644
--- a/src/http/controllers/api/v2/surveys.js
+++ b/src/http/controllers/api/v2/surveys.js
@@ -51,6 +51,43 @@ exports.list = async ctx => {
 	}
 }
 
+exports.listExcerpts = async ctx => {
+	const {Sequelize, SurveyExcerpt} = require('database/models')
+	const user = await ctx.services['core.auth'].getUser()
+
+	if (!user) {
+		throw new UnauthorizedError()
+	}
+
+	const surveys = await user.getSurveys({ include: [SurveyExcerpt] })
+
+	ctx.body = {
+		excerpts: surveys.flatMap(s => s.SurveyExcerpts.map(se => se.toJSON()))
+	}
+}
+
+exports.getExcerpt = async ctx => {
+	if (!ctx.models.excerpt) {
+		throw new NotFoundError('Survey Excerpt')
+	}
+
+	const { view_name } = ctx.models.excerpt
+	try {
+		const result = await sequelize.query(`SELECT * FROM ${ view_name }`, {
+			type: QueryTypes.SELECT,
+		})
+
+		ctx.body = {
+			data: result,
+		}
+	} catch(e) {
+		console.log(e)
+		ctx.body = {
+			data: null
+		}
+	}
+}
+
 exports.joined = async ctx => {
 	const user = await ctx.services['core.auth'].getUser()
 	if (user == null) {
diff --git a/src/http/params/survey_excerpt.js b/src/http/params/survey_excerpt.js
new file mode 100644
index 0000000000000000000000000000000000000000..103e2447b4d28d5591b6f16c46748576fe001c51
--- /dev/null
+++ b/src/http/params/survey_excerpt.js
@@ -0,0 +1,7 @@
+const { SurveyExcerpt, Sequelize} = require('database/models')
+
+module.exports = async (id, ctx, next) => {
+	ctx.models = ctx.models ?? {}
+	ctx.models.excerpt = await SurveyExcerpt.findByPk(id)
+	return await next()
+}
diff --git a/src/http/routers/routes_v2.js b/src/http/routers/routes_v2.js
index fc6db7be393b145036e43450caeab180fad6e7b6..8f9ea777a90c90e011b6e8be169892d069615d0f 100644
--- a/src/http/routers/routes_v2.js
+++ b/src/http/routers/routes_v2.js
@@ -51,8 +51,11 @@ router.delete('/uploads/:upload_id', noop)
 router.put('/uploads/:upload_id/:property', noop)
 
 router.param('survey', param('survey'))
+router.param('excerpt', param('survey_excerpt'))
 
 router.get('/surveys', controller('api/v2/surveys', 'list'))
+router.get('/excerpts', controller('api/v2/surveys', 'listExcerpts'))
+router.get('/excerpts/:excerpt', controller('api/v2/surveys', 'getExcerpt'))
 router.get('/surveys/:survey', controller('api/v2/surveys', 'get'))
 router.post('/surveys/:survey/membership', controller('api/v2/surveys', 'join'))
 router.delete('/surveys/:survey/membership', controller('api/v2/surveys', 'leave'))
@@ -62,54 +65,4 @@ if (config('app.dev')) {
 
 router.post('/an/ev', controller('api/analytics', 'track'))
 
-// api.post('/metrics', controller('api/content', 'postMetric'))
-// api.get('/metrics', controller('api/content', 'getWithin'))
-//
-// api.get('/images', controller('api/storage', 'getFiles'))
-// api.post(
-// 	'/images',
-// 	upload.single('featured_image'),
-// 	controller('api/storage', 'saveFile'),
-// )
-// api.post('/images/:imageId/feature', controller('api/storage', 'featureImage'))
-//
-// /** @deprecated */
-// api.post(
-// 	'/feature',
-// 	upload.single('featured_image'),
-// 	controller('api/storage', 'saveFile'),
-// )
-//
-// api.get('/feed', controller('api/storage', 'feed'))
-// api.post('/feed/:fileId/like', controller('api/storage', 'like'))
-// api.post('/feed/:fileId/unlike', controller('api/storage', 'unlike'))
-//
-// api.post('/register', controller('api/auth', 'register'))
-// api.post('/login', controller('api/auth', 'login'))
-//
-// api.post('/auth/reset-token', controller('api/auth', 'triggerPasswordReset'))
-// api.post('/auth/reset-password', controller('api/auth', 'handlePasswordReset'))
-//
-// api.param('oauthClientId', param('oauth_client'))
-//
-// api.get('/oauth/clients', controller('api/oauth', 'listClients'))
-// api.post('/oauth/clients', controller('api/oauth', 'createClient'))
-// api.post(
-// 	'/oauth/clients/:oauthClientId/redirects',
-// 	controller('api/oauth', 'addClientRedirect'),
-// )
-// api.delete(
-// 	'/oauth/clients/:oauthClientId/redirects',
-// 	controller('api/oauth', 'removeClientRedirect'),
-// )
-//
-// api.get('/self', controller('api/user', 'self'))
-// api.get('/self/bundles', controller('api/app', 'getBundles'))
-// api.put('/self/:property', controller('api/user', 'updateOne'))
-//
-// api.post('/an/id', async ctx => {})
-// api.post('/an/ev', controller('api/analytics', 'track'))
-//
-// api.post('/feedback', controller('api/feedback', 'send'))
-
 module.exports = router