diff --git a/README.md b/README.md
index 98818de2..13f5f4ea 100644
--- a/README.md
+++ b/README.md
@@ -110,7 +110,11 @@ There are two parts need to be updated for local development
 - https://github.com/topcoder-platform/challenge-api/blob/develop/config/default.js#L27-L28
 Two aws config should be uncommented
 
+and AUTH0 related configuration must be set at config file or in env variables.
 
+### Deploy the app
+
+- Follow the Notes section above
 - Install dependencies `npm install`
 - Run lint `npm run lint`
 - Run lint fix `npm run lint:fix`
@@ -118,6 +122,7 @@ Two aws config should be uncommented
   or re-create the index: `npm run init-es force`
 - Create tables `npm run create-tables`
 - Clear and init db `npm run init-db`
+- Seed tables: `npm run seed-tables`
 - Start app `npm start`
 - App is running at `http://localhost:3000`
 
diff --git a/app-constants.js b/app-constants.js
index d1b37094..d2b78ef1 100644
--- a/app-constants.js
+++ b/app-constants.js
@@ -55,6 +55,7 @@ const DiscussionTypes = {
 const Topics = {
   ChallengeCreated: 'challenge.notification.create',
   ChallengeUpdated: 'challenge.notification.update',
+  ChallengeDeleted: 'challenge.notification.delete',
   ChallengeTypeCreated: 'test.new.bus.events', // 'challenge.action.type.created',
   ChallengeTypeUpdated: 'test.new.bus.events', // 'challenge.action.type.updated',
   ChallengePhaseCreated: 'test.new.bus.events', // 'challenge.action.phase.created',
diff --git a/config/default.js b/config/default.js
index e4f1b17b..070efebc 100644
--- a/config/default.js
+++ b/config/default.js
@@ -25,8 +25,8 @@ module.exports = {
   KAFKA_ERROR_TOPIC: process.env.KAFKA_ERROR_TOPIC || 'common.error.reporting',
 
   AMAZON: {
-    // AWS_ACCESS_KEY_ID: process.env.AWS_FAKE_ID,
-    // AWS_SECRET_ACCESS_KEY: process.env.AWS_FAKE_KEY,
+    // AWS_ACCESS_KEY_ID: process.env.AWS_FAKE_ID || "FAKE_ACCESS_KEY",
+    // AWS_SECRET_ACCESS_KEY: process.env.AWS_FAKE_KEY || "FAKE_SECRET_ACCESS_KEY",
     AWS_REGION: process.env.AWS_REGION || 'ap-northeast-1',
     IS_LOCAL_DB: process.env.IS_LOCAL_DB || true,
     DYNAMODB_URL: process.env.DYNAMODB_URL || 'http://localhost:8000',
diff --git a/docs/swagger.yaml b/docs/swagger.yaml
index bedd977f..2c89fb87 100644
--- a/docs/swagger.yaml
+++ b/docs/swagger.yaml
@@ -605,6 +605,50 @@ paths:
           description: Server error
           schema:
             $ref: '#/definitions/ErrorModel'
+    delete:
+      tags:
+        - Challenges
+      description: >
+        Delete the challenge with the provided id.
+        Only challenges with status of "New" can be deleted.
+      security:
+        - bearer: []
+      produces:
+        - application/json
+      parameters:
+        - name: challengeId
+          in: path
+          required: true
+          type: string
+          format: UUID
+          description: The id of challenge to update
+      responses:
+        '200':
+          description: Deleted - The request was successful and the resource is returned.
+          schema:
+            $ref: '#/definitions/Challenge'
+        '400':
+          description: Bad request.  Request parameters were invalid.
+          schema:
+            $ref: '#/definitions/ErrorModel'
+        '401':
+          description: Unauthorized. Fail to authenticate the requester.
+          schema:
+            $ref: '#/definitions/ErrorModel'
+        '403':
+          description: >
+            Forbidden.  The requester does not have the correct permission to
+            update the challenge.
+          schema:
+            $ref: '#/definitions/ErrorModel'
+        '404':
+          description: Challenge not found
+          schema:
+            $ref: '#/definitions/ErrorModel'
+        '500':
+          description: Server error
+          schema:
+            $ref: '#/definitions/ErrorModel'
   /challenge-types:
     get:
       tags:
@@ -2433,6 +2477,9 @@ definitions:
       numOfRegistrants:
         type: integer
         description: number of registrants
+      overview:
+        description: the overview of the challenge
+        type: object
       created:
         type: string
         format: date-time
@@ -2903,6 +2950,9 @@ definitions:
       numOfRegistrants:
         type: integer
         description: number of registrants
+      overview:
+        description: the overview of the challenge
+        type: object
       created:
         type: string
         format: date-time
diff --git a/docs/topcoder-challenge-api.postman_collection.json b/docs/topcoder-challenge-api.postman_collection.json
index b4df3c8f..ba52f92b 100644
--- a/docs/topcoder-challenge-api.postman_collection.json
+++ b/docs/topcoder-challenge-api.postman_collection.json
@@ -20343,6 +20343,516 @@
 					],
 					"protocolProfileBehavior": {},
 					"_postman_isSubFolder": true
+				},
+				{
+					"name": "delete challenge",
+					"item": [
+						{
+							"name": "[STUB] CHALLENGE_ID1 can be set by running \"create challenge by admin\"",
+							"request": {
+								"method": "VIEW",
+								"header": [],
+								"url": {
+									"raw": ""
+								}
+							},
+							"response": []
+						},
+						{
+							"name": "[STUB] CHALLENGE_ID2 can be set by running \"create challenge by copilot\"",
+							"request": {
+								"method": "VIEW",
+								"header": [],
+								"url": {
+									"raw": ""
+								}
+							},
+							"response": []
+						},
+						{
+							"name": "delete challenge 1 by admin",
+							"event": [
+								{
+									"listen": "test",
+									"script": {
+										"id": "7748e11e-0536-4d66-821e-2acb8b9ee4e4",
+										"exec": [
+											"pm.test(\"Status code is 200\", function () {",
+											"    pm.response.to.have.status(200);",
+											"});"
+										],
+										"type": "text/javascript"
+									}
+								}
+							],
+							"request": {
+								"method": "DELETE",
+								"header": [
+									{
+										"key": "Accept",
+										"type": "text",
+										"value": "application/json"
+									},
+									{
+										"key": "Content-Type",
+										"type": "text",
+										"value": "application/json"
+									},
+									{
+										"key": "Authorization",
+										"type": "text",
+										"value": "Bearer {{admin_token}}"
+									}
+								],
+								"url": {
+									"raw": "{{URL}}/challenges/{{CHALLENGE_ID1}}",
+									"host": [
+										"{{URL}}"
+									],
+									"path": [
+										"challenges",
+										"{{CHALLENGE_ID1}}"
+									]
+								}
+							},
+							"response": []
+						},
+						{
+							"name": "delete challenge 2 by copilot",
+							"event": [
+								{
+									"listen": "test",
+									"script": {
+										"id": "21ce91c2-727e-4942-94ce-f7b9ad0e1922",
+										"exec": [
+											"pm.test(\"Status code is 200\", function () {",
+											"    pm.response.to.have.status(200);",
+											"});"
+										],
+										"type": "text/javascript"
+									}
+								}
+							],
+							"request": {
+								"method": "DELETE",
+								"header": [
+									{
+										"key": "Accept",
+										"type": "text",
+										"value": "application/json"
+									},
+									{
+										"key": "Content-Type",
+										"type": "text",
+										"value": "application/json"
+									},
+									{
+										"key": "Authorization",
+										"type": "text",
+										"value": "Bearer {{copilot1_token}}"
+									}
+								],
+								"url": {
+									"raw": "{{URL}}/challenges/{{CHALLENGE_ID2}}",
+									"host": [
+										"{{URL}}"
+									],
+									"path": [
+										"challenges",
+										"{{CHALLENGE_ID2}}"
+									]
+								}
+							},
+							"response": []
+						},
+						{
+							"name": "failure delete challenge by different copilot 403",
+							"event": [
+								{
+									"listen": "test",
+									"script": {
+										"id": "b27f9844-172a-45ab-8d78-d8e648162de7",
+										"exec": [
+											"pm.test(\"Status code is 403\", function () {",
+											"    pm.response.to.have.status(403);",
+											"});"
+										],
+										"type": "text/javascript"
+									}
+								}
+							],
+							"request": {
+								"method": "DELETE",
+								"header": [
+									{
+										"key": "Accept",
+										"type": "text",
+										"value": "application/json"
+									},
+									{
+										"key": "Content-Type",
+										"type": "text",
+										"value": "application/json"
+									},
+									{
+										"key": "Authorization",
+										"type": "text",
+										"value": "Bearer {{copilot2_token}}"
+									}
+								],
+								"url": {
+									"raw": "{{URL}}/challenges/{{CHALLENGE_ID2}}",
+									"host": [
+										"{{URL}}"
+									],
+									"path": [
+										"challenges",
+										"{{CHALLENGE_ID2}}"
+									]
+								}
+							},
+							"response": []
+						},
+						{
+							"name": "failure delete challenge by user 403",
+							"event": [
+								{
+									"listen": "test",
+									"script": {
+										"id": "1d6af907-bdc2-437f-9883-3040c1e12e79",
+										"exec": [
+											"pm.test(\"Status code is 403\", function () {",
+											"    pm.response.to.have.status(403);",
+											"});"
+										],
+										"type": "text/javascript"
+									}
+								}
+							],
+							"request": {
+								"method": "DELETE",
+								"header": [
+									{
+										"key": "Accept",
+										"type": "text",
+										"value": "application/json"
+									},
+									{
+										"key": "Content-Type",
+										"type": "text",
+										"value": "application/json"
+									},
+									{
+										"key": "Authorization",
+										"type": "text",
+										"value": "Bearer {{user_token}}"
+									}
+								],
+								"url": {
+									"raw": "{{URL}}/challenges/{{CHALLENGE_ID2}}",
+									"host": [
+										"{{URL}}"
+									],
+									"path": [
+										"challenges",
+										"{{CHALLENGE_ID2}}"
+									]
+								}
+							},
+							"response": []
+						},
+						{
+							"name": "failure delete challenge without token 401",
+							"event": [
+								{
+									"listen": "test",
+									"script": {
+										"id": "df9d716a-9a0f-4764-9154-2bd57532f0ea",
+										"exec": [
+											"pm.test(\"Status code is 401\", function () {",
+											"    pm.response.to.have.status(401);",
+											"});"
+										],
+										"type": "text/javascript"
+									}
+								}
+							],
+							"request": {
+								"method": "DELETE",
+								"header": [
+									{
+										"key": "Accept",
+										"type": "text",
+										"value": "application/json"
+									},
+									{
+										"key": "Content-Type",
+										"type": "text",
+										"value": "application/json"
+									}
+								],
+								"url": {
+									"raw": "{{URL}}/challenges/{{CHALLENGE_ID2}}",
+									"host": [
+										"{{URL}}"
+									],
+									"path": [
+										"challenges",
+										"{{CHALLENGE_ID2}}"
+									]
+								}
+							},
+							"response": []
+						},
+						{
+							"name": "failure delete challenge with invalid token 401",
+							"event": [
+								{
+									"listen": "test",
+									"script": {
+										"id": "db2448ac-deda-4c6c-a47b-6c7af95ddf8b",
+										"exec": [
+											"pm.test(\"Status code is 401\", function () {",
+											"    pm.response.to.have.status(401);",
+											"});"
+										],
+										"type": "text/javascript"
+									}
+								}
+							],
+							"request": {
+								"method": "DELETE",
+								"header": [
+									{
+										"key": "Accept",
+										"type": "text",
+										"value": "application/json"
+									},
+									{
+										"key": "Content-Type",
+										"type": "text",
+										"value": "application/json"
+									},
+									{
+										"key": "Authorization",
+										"type": "text",
+										"value": "Bearer invalid"
+									}
+								],
+								"url": {
+									"raw": "{{URL}}/challenges/{{CHALLENGE_ID2}}",
+									"host": [
+										"{{URL}}"
+									],
+									"path": [
+										"challenges",
+										"{{CHALLENGE_ID2}}"
+									]
+								}
+							},
+							"response": []
+						},
+						{
+							"name": "failure delete challenge with expire token 401",
+							"event": [
+								{
+									"listen": "test",
+									"script": {
+										"id": "2edcc5f2-8a0f-4791-93b2-da3a897daf76",
+										"exec": [
+											"pm.test(\"Status code is 401\", function () {",
+											"    pm.response.to.have.status(401);",
+											"});"
+										],
+										"type": "text/javascript"
+									}
+								}
+							],
+							"request": {
+								"method": "DELETE",
+								"header": [
+									{
+										"key": "Accept",
+										"type": "text",
+										"value": "application/json"
+									},
+									{
+										"key": "Content-Type",
+										"type": "text",
+										"value": "application/json"
+									},
+									{
+										"key": "Authorization",
+										"type": "text",
+										"value": "Bearer {{expire_token}}"
+									}
+								],
+								"url": {
+									"raw": "{{URL}}/challenges/{{CHALLENGE_ID2}}",
+									"host": [
+										"{{URL}}"
+									],
+									"path": [
+										"challenges",
+										"{{CHALLENGE_ID2}}"
+									]
+								}
+							},
+							"response": []
+						},
+						{
+							"name": "failure delete challenge not found 404",
+							"event": [
+								{
+									"listen": "test",
+									"script": {
+										"id": "9e8ed6d8-7a40-4ced-8176-fed5a8d8b095",
+										"exec": [
+											"pm.test(\"Status code is 404\", function () {",
+											"    pm.response.to.have.status(404);",
+											"});"
+										],
+										"type": "text/javascript"
+									}
+								}
+							],
+							"request": {
+								"method": "DELETE",
+								"header": [
+									{
+										"key": "Accept",
+										"type": "text",
+										"value": "application/json"
+									},
+									{
+										"key": "Content-Type",
+										"type": "text",
+										"value": "application/json"
+									},
+									{
+										"key": "Authorization",
+										"type": "text",
+										"value": "Bearer {{admin_token}}"
+									}
+								],
+								"url": {
+									"raw": "{{URL}}/challenges/:challengeId",
+									"host": [
+										"{{URL}}"
+									],
+									"path": [
+										"challenges",
+										":challengeId"
+									],
+									"variable": [
+										{
+											"key": "challengeId",
+											"value": "19d20d64-e84e-452f-abd5-018e6abc68ab"
+										}
+									]
+								}
+							},
+							"response": []
+						},
+						{
+							"name": "delete challenge using m2m token",
+							"event": [
+								{
+									"listen": "test",
+									"script": {
+										"id": "c46553ad-8bb4-46b2-84ec-ba7a029b74ee",
+										"exec": [
+											"pm.test(\"Status code is 200\", function () {",
+											"    pm.response.to.have.status(200);",
+											"});"
+										],
+										"type": "text/javascript"
+									}
+								}
+							],
+							"request": {
+								"method": "DELETE",
+								"header": [
+									{
+										"key": "Accept",
+										"type": "text",
+										"value": "application/json"
+									},
+									{
+										"key": "Content-Type",
+										"type": "text",
+										"value": "application/json"
+									},
+									{
+										"key": "Authorization",
+										"type": "text",
+										"value": "Bearer {{m2m_challenges_delete}}"
+									}
+								],
+								"url": {
+									"raw": "{{URL}}/challenges/{{CHALLENGE_ID2}}",
+									"host": [
+										"{{URL}}"
+									],
+									"path": [
+										"challenges",
+										"{{CHALLENGE_ID2}}"
+									]
+								}
+							},
+							"response": []
+						},
+						{
+							"name": "failure delete challenge using forbidden m2m token 403",
+							"event": [
+								{
+									"listen": "test",
+									"script": {
+										"id": "3eeabff3-ceee-47f5-a23c-b2ff1322a2e8",
+										"exec": [
+											"pm.test(\"Status code is 403\", function () {",
+											"    pm.response.to.have.status(403);",
+											"});"
+										],
+										"type": "text/javascript"
+									}
+								}
+							],
+							"request": {
+								"method": "DELETE",
+								"header": [
+									{
+										"key": "Accept",
+										"type": "text",
+										"value": "application/json"
+									},
+									{
+										"key": "Content-Type",
+										"type": "text",
+										"value": "application/json"
+									},
+									{
+										"key": "Authorization",
+										"type": "text",
+										"value": "Bearer {{m2m_challenge_attachments_read}}"
+									}
+								],
+								"url": {
+									"raw": "{{URL}}/challenges/{{CHALLENGE_ID2}}",
+									"host": [
+										"{{URL}}"
+									],
+									"path": [
+										"challenges",
+										"{{CHALLENGE_ID2}}"
+									]
+								}
+							},
+							"response": []
+						}
+					],
+					"protocolProfileBehavior": {},
+					"_postman_isSubFolder": true
 				}
 			],
 			"protocolProfileBehavior": {}
diff --git a/docs/topcoder-challenge-api.postman_environment.json b/docs/topcoder-challenge-api.postman_environment.json
index 14c31335..98fe5b82 100644
--- a/docs/topcoder-challenge-api.postman_environment.json
+++ b/docs/topcoder-challenge-api.postman_environment.json
@@ -152,6 +152,11 @@
 			"value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3RvcGNvZGVyLWRldi5hdXRoMC5jb20vIiwic3ViIjoiZW5qdzE4MTBlRHozWFR3U08yUm4yWTljUVRyc3BuM0JAY2xpZW50cyIsImF1ZCI6Imh0dHBzOi8vbTJtLnRvcGNvZGVyLWRldi5jb20vIiwiaWF0IjoxNTUwOTA2Mzg4LCJleHAiOjE2NDA5OTI3ODgsImF6cCI6ImVuancxODEwZUR6M1hUd1NPMlJuMlk5Y1FUcnNwbjNCIiwic2NvcGUiOiJ1cGRhdGU6Y2hhbGxlbmdlcyBhbGw6Y2hhbGxlbmdlcyIsImd0eSI6ImNsaWVudC1jcmVkZW50aWFscyJ9.dN4uHhmPN33ljY2YSZmndxAnSXaowQaYENiPL5wjDvc",
 			"enabled": true
 		},
+		{
+			"key": "m2m_challenges_delete",
+			"value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3RvcGNvZGVyLWRldi5hdXRoMC5jb20vIiwic3ViIjoiZW5qdzE4MTBlRHozWFR3U08yUm4yWTljUVRyc3BuM0JAY2xpZW50cyIsImF1ZCI6Imh0dHBzOi8vbTJtLnRvcGNvZGVyLWRldi5jb20vIiwiaWF0IjoxNTUwOTA2Mzg4LCJleHAiOjE2NDA5OTI3ODgsImF6cCI6ImVuancxODEwZUR6M1hUd1NPMlJuMlk5Y1FUcnNwbjNCIiwic2NvcGUiOiJkZWxldGU6Y2hhbGxlbmdlcyBhbGw6Y2hhbGxlbmdlcyIsImd0eSI6ImNsaWVudC1jcmVkZW50aWFscyJ9.NCZdRy3TErhxG5A2ic4ahC70xfihq2UA5y0Z7OyR7Go",
+			"enabled": true
+		},
 		{
 			"key": "m2m_challenge_types_create",
 			"value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3RvcGNvZGVyLWRldi5hdXRoMC5jb20vIiwic3ViIjoiZW5qdzE4MTBlRHozWFR3U08yUm4yWTljUVRyc3BuM0JAY2xpZW50cyIsImF1ZCI6Imh0dHBzOi8vbTJtLnRvcGNvZGVyLWRldi5jb20vIiwiaWF0IjoxNTUwOTA2Mzg4LCJleHAiOjE2NDA5OTI3ODgsImF6cCI6ImVuancxODEwZUR6M1hUd1NPMlJuMlk5Y1FUcnNwbjNCIiwic2NvcGUiOiJjcmVhdGU6Y2hhbGxlbmdlX3R5cGVzIGFsbDpjaGFsbGVuZ2VfdHlwZXMiLCJndHkiOiJjbGllbnQtY3JlZGVudGlhbHMifQ.Njg1n3o0I8J9aa6y8xdiwfZFIGH2rJmW-7vKv3kXg5I",
diff --git a/local/docker-compose.yml b/local/docker-compose.yml
index e58c8a80..88b4db1c 100644
--- a/local/docker-compose.yml
+++ b/local/docker-compose.yml
@@ -3,8 +3,8 @@ services:
   dynamodb:
     image: tray/dynamodb-local
     ports:
-      - "7777:7777"
-    command: "-inMemory -port 7777"
+      - "8000:8000"
+    command: "-inMemory -port 8000"
   esearch:
     image: "docker.elastic.co/elasticsearch/elasticsearch:6.8.0"
     ports:
@@ -24,6 +24,8 @@ services:
     build:
       context: ../
       dockerfile: mock-api/Dockerfile
+    volumes:
+      - ../mock-api:/challenge-api/mock-api
     environment:
       DYNAMODB_URL: http://dynamodb:7777
       IS_LOCAL_DB: "true"
diff --git a/mock-api/app.js b/mock-api/app.js
index 90fea518..ac97e1f4 100755
--- a/mock-api/app.js
+++ b/mock-api/app.js
@@ -14,8 +14,32 @@ app.set('port', config.PORT)
 
 app.use(cors())
 
+const groups = {
+  '33ba038e-48da-487b-96e8-8d3b99b6d181': {
+    id: '33ba038e-48da-487b-96e8-8d3b99b6d181',
+    name: 'group1',
+    description: 'desc1',
+    privateGroup: false,
+    selfRegister: true,
+    domain: 'domain1'
+  },
+  '33ba038e-48da-487b-96e8-8d3b99b6d182': {
+    id: '33ba038e-48da-487b-96e8-8d3b99b6d182',
+    name: 'group2',
+    description: 'desc2',
+    privateGroup: true,
+    selfRegister: false,
+    domain: 'domain2'
+  }
+}
+
 // get challenge resources
 app.get('/v5/resources', (req, res) => {
+  winston.debug(`query: ${JSON.stringify(req.query, null, 2)}`)
+  if (Number(req.query.page) > 1) {
+    res.json([])
+    return
+  }
   const challengeId = req.query.challengeId
   winston.info(`Get resources of challenge id ${challengeId}`)
 
@@ -118,6 +142,17 @@ app.get('/v5/groups', (req, res) => {
   res.json(result)
 })
 
+// get group by id
+app.get('/v5/groups/:groupId', (req, res) => {
+  winston.info(`Find group, groupId: ${req.params.groupId}`)
+  if (!groups[req.params.groupId]) {
+    res.status(404).send({ message: `group ${req.params.groupId} not found` })
+  }
+  const result = groups[req.params.groupId]
+  winston.info(`Result: ${JSON.stringify(result, null, 4)}`)
+  res.json(result)
+})
+
 // terms API
 app.get('/v5/terms/:termId', (req, res) => {
   const termId = req.params.termId
diff --git a/src/common/helper.js b/src/common/helper.js
index 03d6bfb7..6bae877f 100644
--- a/src/common/helper.js
+++ b/src/common/helper.js
@@ -763,6 +763,43 @@ async function validateChallengeTerms (terms = []) {
   return listOfTerms
 }
 
+/**
+ * Calculate the sum of prizes.
+ *
+ * @param {Array} prizes the list of prize
+ * @returns {Number} the result prize
+ */
+function sumOfPrizes (prizes) {
+  let sum = 0
+  if (!prizes.length) {
+    return sum
+  }
+  for (const prize of prizes) {
+    sum += prize.value
+  }
+  return sum
+}
+
+/**
+ * Get group by id
+ * @param {String} groupId the group id
+ * @returns {Promise<Object>} the group
+ */
+async function getGroupById (groupId) {
+  const token = await getM2MToken()
+  try {
+    const result = await axios.get(`${config.GROUPS_API_URL}/${groupId}`, {
+      headers: { Authorization: `Bearer ${token}` }
+    })
+    return result.data
+  } catch (err) {
+    if (err.response.status === HttpStatus.NOT_FOUND) {
+      return
+    }
+    throw err
+  }
+}
+
 module.exports = {
   wrapExpress,
   autoWrapExpress,
@@ -798,5 +835,7 @@ module.exports = {
   getCompleteUserGroupTreeIds,
   expandWithParentGroups,
   getResourceRoles,
-  userHasFullAccess
+  userHasFullAccess,
+  sumOfPrizes,
+  getGroupById
 }
diff --git a/src/controllers/ChallengeController.js b/src/controllers/ChallengeController.js
index 7f611d8a..61586671 100644
--- a/src/controllers/ChallengeController.js
+++ b/src/controllers/ChallengeController.js
@@ -60,10 +60,22 @@ async function partiallyUpdateChallenge (req, res) {
   res.send(result)
 }
 
+/**
+ * Delete challenge
+ * @param {Object} req the request
+ * @param {Object} res the response
+ */
+async function deleteChallenge (req, res) {
+  logger.debug(`deleteChallenge User: ${JSON.stringify(req.authUser)} - ChallengeID: ${req.params.challengeId}`)
+  const result = await service.deleteChallenge(req.authUser, req.params.challengeId)
+  res.send(result)
+}
+
 module.exports = {
   searchChallenges,
   createChallenge,
   getChallenge,
   fullyUpdateChallenge,
-  partiallyUpdateChallenge
+  partiallyUpdateChallenge,
+  deleteChallenge
 }
diff --git a/src/models/Challenge.js b/src/models/Challenge.js
index 72be37ed..8ddbf407 100644
--- a/src/models/Challenge.js
+++ b/src/models/Challenge.js
@@ -112,6 +112,9 @@ const schema = new Schema({
     type: [Object],
     required: false
   },
+  overview: {
+    type: Object
+  },
   created: {
     type: Date,
     required: true
diff --git a/src/routes.js b/src/routes.js
index f7ed7d27..70ff73da 100644
--- a/src/routes.js
+++ b/src/routes.js
@@ -52,6 +52,13 @@ module.exports = {
       auth: 'jwt',
       access: [constants.UserRoles.Admin, constants.UserRoles.Copilot, constants.UserRoles.Manager],
       scopes: [UPDATE, ALL]
+    },
+    delete: {
+      controller: 'ChallengeController',
+      method: 'deleteChallenge',
+      auth: 'jwt',
+      access: [constants.UserRoles.Admin, constants.UserRoles.Copilot, constants.UserRoles.Manager],
+      scopes: [DELETE, ALL]
     }
   },
   '/challenge-types': {
diff --git a/src/services/ChallengeService.js b/src/services/ChallengeService.js
index 1ce204e3..9a5c3ce4 100644
--- a/src/services/ChallengeService.js
+++ b/src/services/ChallengeService.js
@@ -21,6 +21,20 @@ const ChallengeTimelineTemplateService = require('./ChallengeTimelineTemplateSer
 
 const esClient = helper.getESClient()
 
+/**
+ * Check if user can perform modification/deletion to a challenge
+ *
+ * @param {Object} user the JwT user object
+ * @param {Object} challenge the challenge object
+ * @returns {undefined}
+ */
+async function ensureAccessibleForChallenge (user, challenge) {
+  const userHasFullAccess = await helper.userHasFullAccess(challenge.id, user.userId)
+  if (!user.isMachine && !helper.hasAdminRole(user) && challenge.createdBy.toLowerCase() !== user.handle.toLowerCase() && !userHasFullAccess) {
+    throw new errors.ForbiddenError(`Only M2M, admin, challenge's copilot or users with full access can perform modification.`)
+  }
+}
+
 /**
  * Filter challenges by groups access
  * @param {Object} currentUser the user who perform operation
@@ -342,9 +356,27 @@ async function searchChallenges (currentUser, criteria) {
   if (!_.isUndefined(criteria.group) || !_.isUndefined(criteria.groups)) {
     // check group access
     if (_.isUndefined(currentUser)) {
-      throw new errors.BadRequestError('Authentication is required to filter challenges based on groups')
-    }
-    if (!currentUser.isMachine && !helper.hasAdminRole(currentUser)) {
+      if (criteria.group) {
+        const group = await helper.getGroupById(criteria.group)
+        if (group && !group.privateGroup) {
+          groupsToFilter.push(criteria.group)
+        }
+      }
+      if (criteria.groups && criteria.groups.length > 0) {
+        const promises = []
+        _.each(criteria.groups, (g) => {
+          promises.push(
+            (async () => {
+              const group = await helper.getGroupById(g)
+              if (group && !group.privateGroup) {
+                groupsToFilter.push(g)
+              }
+            })()
+          )
+        })
+        await Promise.all(promises)
+      }
+    } else if (!currentUser.isMachine && !helper.hasAdminRole(currentUser)) {
       if (accessibleGroups.includes(criteria.group)) {
         groupsToFilter.push(criteria.group)
       }
@@ -848,6 +880,16 @@ async function createChallenge (currentUser, challenge, userToken) {
   if (challenge.phases && challenge.phases.length > 0) {
     challenge.endDate = helper.calculateChallengeEndDate(challenge)
   }
+
+  // auto-populate totalPrizes
+  if (challenge.prizeSets) {
+    const prizeSetsGroup = _.groupBy(challenge.prizeSets, 'type')
+    if (prizeSetsGroup[constants.prizeSetTypes.ChallengePrizes]) {
+      const totalPrizes = helper.sumOfPrizes(prizeSetsGroup[constants.prizeSetTypes.ChallengePrizes][0].prizes)
+      _.assign(challenge, { overview: { totalPrizes } })
+    }
+  }
+
   const ret = await helper.create('Challenge', _.assign({
     id: uuid(),
     created: moment().utc(),
@@ -1193,10 +1235,7 @@ async function update (currentUser, challengeId, data, userToken, isFull) {
     newAttachments = await helper.getByIds('Attachment', data.attachmentIds || [])
   }
 
-  const userHasFullAccess = await helper.userHasFullAccess(challengeId, currentUser.userId)
-  if (!currentUser.isMachine && !helper.hasAdminRole(currentUser) && challenge.createdBy.toLowerCase() !== currentUser.handle.toLowerCase() && !userHasFullAccess) {
-    throw new errors.ForbiddenError(`Only M2M, admin, challenge's copilot or users with full access can perform modification.`)
-  }
+  await ensureAccessibleForChallenge(currentUser, challenge)
 
   // Only M2M can update url and options of discussions
   if (data.discussions && data.discussions.length > 0) {
@@ -1266,6 +1305,18 @@ async function update (currentUser, challengeId, data, userToken, isFull) {
     throw new errors.BadRequestError(`Cannot change the timelineTemplateId for challenges with status: ${finalStatus}`)
   }
 
+  if (data.prizeSets) {
+    const prizeSetsGroup = _.groupBy(data.prizeSets, 'type')
+    if (!prizeSetsGroup[constants.prizeSetTypes.ChallengePrizes] && _.get(challenge, 'overview.totalPrizes')) {
+      // remove the totalPrizes if challenge prizes are empty
+      challenge.overview = _.omit(challenge.overview, ['totalPrizes'])
+    } else {
+      const totalPrizes = helper.sumOfPrizes(prizeSetsGroup[constants.prizeSetTypes.ChallengePrizes][0].prizes)
+      logger.debug(`re-calculate total prizes, current value is ${totalPrizes.value}`)
+      _.assign(challenge, { overview: { totalPrizes } })
+    }
+  }
+
   if (data.phases || data.startDate) {
     if (data.phases && data.phases.length > 0) {
       for (let i = 0; i < challenge.phases.length; i += 1) {
@@ -1555,6 +1606,17 @@ async function update (currentUser, challengeId, data, userToken, isFull) {
   if (challenge.phases && challenge.phases.length > 0) {
     challenge.currentPhase = challenge.phases.slice().reverse().find(phase => phase.isOpen)
     challenge.endDate = helper.calculateChallengeEndDate(challenge)
+    const registrationPhase = _.find(challenge.phases, p => p.name === 'Registration')
+    const submissionPhase = _.find(challenge.phases, p => p.name === 'Submission')
+    challenge.currentPhaseNames = _.map(_.filter(challenge.phases, p => p.isOpen === true), 'name')
+    if (registrationPhase) {
+      challenge.registrationStartDate = registrationPhase.actualStartDate || registrationPhase.scheduledStartDate
+      challenge.registrationEndDate = registrationPhase.actualEndDate || registrationPhase.scheduledEndDate
+    }
+    if (submissionPhase) {
+      challenge.submissionStartDate = submissionPhase.actualStartDate || submissionPhase.scheduledStartDate
+      challenge.submissionEndDate = submissionPhase.actualEndDate || submissionPhase.scheduledEndDate
+    }
   }
   // Update ES
   await esClient.update({
@@ -1722,7 +1784,8 @@ fullyUpdateChallenge.schema = {
     terms: Joi.array().items(Joi.object().keys({
       id: Joi.id(),
       roleId: Joi.id()
-    }).unknown(true)).optional().allow([])
+    }).unknown(true)).optional().allow([]),
+    overview: Joi.any().forbidden()
   }).unknown(true).required(),
   userToken: Joi.any()
 }
@@ -1807,17 +1870,52 @@ partiallyUpdateChallenge.schema = {
       handle: Joi.string().required(),
       placement: Joi.number().integer().positive().required()
     }).unknown(true)).min(1),
-    terms: Joi.array().items(Joi.id().optional()).optional().allow([])
+    terms: Joi.array().items(Joi.id().optional()).optional().allow([]),
+    overview: Joi.any().forbidden()
   }).unknown(true).required(),
   userToken: Joi.any()
 }
 
+/**
+ * Delete challenge.
+ * @param {Object} currentUser the user who perform operation
+ * @param {String} challengeId the challenge id
+ * @returns {Object} the deleted challenge
+ */
+async function deleteChallenge (currentUser, challengeId) {
+  const challenge = await helper.getById('Challenge', challengeId)
+  if (challenge.status !== constants.challengeStatuses.New) {
+    throw new errors.BadRequestError(`Challenge with status other than "${constants.challengeStatuses.New}" cannot be removed`)
+  }
+  // check groups authorization
+  await ensureAccessibleByGroupsAccess(currentUser, challenge)
+  // check if user are allowed to delete the challenge
+  await ensureAccessibleForChallenge(currentUser, challenge)
+  // delete DB record
+  await models.Challenge.delete(challenge)
+  // delete ES document
+  await esClient.delete({
+    index: config.get('ES.ES_INDEX'),
+    type: config.get('ES.ES_TYPE'),
+    refresh: config.get('ES.ES_REFRESH'),
+    id: challengeId
+  })
+  await helper.postBusEvent(constants.Topics.ChallengeDeleted, { id: challengeId })
+  return challenge
+}
+
+deleteChallenge.schema = {
+  currentUser: Joi.any(),
+  challengeId: Joi.id()
+}
+
 module.exports = {
   searchChallenges,
   createChallenge,
   getChallenge,
   fullyUpdateChallenge,
-  partiallyUpdateChallenge
+  partiallyUpdateChallenge,
+  deleteChallenge
 }
 
-// logger.buildService(module.exports)
+logger.buildService(module.exports)