diff --git a/docs/docs.go b/docs/docs.go
index fbd9998a5aaccf9cea15498eec6984d0488d596b..734b8808010f8a64ff1f1055e13f3be2a39b6bdb 100644
--- a/docs/docs.go
+++ b/docs/docs.go
@@ -1441,7 +1441,98 @@ const docTemplate = `{
                 }
             }
         },
+        "/course/{id}/quiz": {
+            "get": {
+                "description": "Get all cours",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "course"
+                ],
+                "summary": "Get Course quiz",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "format": "uuid",
+                        "description": "Course id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "allOf": [
+                                {
+                                    "$ref": "#/definitions/web.BaseResponse"
+                                },
+                                {
+                                    "type": "object",
+                                    "properties": {
+                                        "data": {
+                                            "type": "array",
+                                            "items": {
+                                                "$ref": "#/definitions/quiz.Quiz"
+                                            }
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    }
+                }
+            }
+        },
         "/material/{id}": {
+            "get": {
+                "description": "Get material detail",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "content"
+                ],
+                "summary": "Get material detail",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "example": "IF3270",
+                        "description": "Material id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "allOf": [
+                                {
+                                    "$ref": "#/definitions/web.BaseResponse"
+                                },
+                                {
+                                    "type": "object",
+                                    "properties": {
+                                        "data": {
+                                            "$ref": "#/definitions/material.Material"
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    }
+                }
+            },
             "post": {
                 "description": "Add content of material",
                 "consumes": [
@@ -1600,6 +1691,164 @@ const docTemplate = `{
                 }
             }
         },
+        "/quiz/{id}": {
+            "get": {
+                "description": "Get Quiz Detail",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "quiz"
+                ],
+                "summary": "Get Quiz Detail",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "format": "uuid",
+                        "description": "Quiz id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "allOf": [
+                                {
+                                    "$ref": "#/definitions/web.BaseResponse"
+                                },
+                                {
+                                    "type": "object",
+                                    "properties": {
+                                        "data": {
+                                            "$ref": "#/definitions/quiz.Quiz"
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    }
+                }
+            }
+        },
+        "/quiz/{id}/solution": {
+            "get": {
+                "description": "Take a quiz",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "quiz"
+                ],
+                "summary": "Get Quiz Solution",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Authenticate User (any role)",
+                        "name": "Authorization",
+                        "in": "header",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "format": "uuid",
+                        "description": "Quiz id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "allOf": [
+                                {
+                                    "$ref": "#/definitions/web.BaseResponse"
+                                },
+                                {
+                                    "type": "object",
+                                    "properties": {
+                                        "data": {
+                                            "$ref": "#/definitions/quiz.QuizDetail"
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    }
+                }
+            }
+        },
+        "/quiz/{id}/take": {
+            "post": {
+                "description": "Finish quiz session and get the score",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "quiz"
+                ],
+                "summary": "Finish Quiz",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Authenticate User (any role)",
+                        "name": "Authorization",
+                        "in": "header",
+                        "required": true
+                    },
+                    {
+                        "description": "Quiz Finish payload",
+                        "name": "data",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/quiz.FinishQuizPayload"
+                        }
+                    },
+                    {
+                        "type": "string",
+                        "format": "uuid",
+                        "description": "Quiz id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "allOf": [
+                                {
+                                    "$ref": "#/definitions/web.BaseResponse"
+                                },
+                                {
+                                    "type": "object",
+                                    "properties": {
+                                        "data": {
+                                            "$ref": "#/definitions/quiz.QuizDetail"
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    }
+                }
+            }
+        },
         "/reset/confirm": {
             "put": {
                 "description": "Do confirmation to reset password",
@@ -2111,6 +2360,134 @@ const docTemplate = `{
                 }
             }
         },
+        "quiz.AnswerOption": {
+            "type": "object",
+            "properties": {
+                "answer": {
+                    "type": "string"
+                },
+                "id": {
+                    "type": "string"
+                },
+                "is_solution": {
+                    "type": "boolean"
+                },
+                "media_id": {
+                    "type": "array",
+                    "items": {
+                        "type": "string"
+                    }
+                }
+            }
+        },
+        "quiz.FinishQuizPayload": {
+            "type": "object",
+            "properties": {
+                "data": {
+                    "type": "array",
+                    "items": {
+                        "$ref": "#/definitions/quiz.Response"
+                    }
+                }
+            }
+        },
+        "quiz.Quiz": {
+            "type": "object",
+            "properties": {
+                "course_id": {
+                    "type": "string"
+                },
+                "creator_email": {
+                    "type": "string"
+                },
+                "id": {
+                    "type": "string"
+                },
+                "nama": {
+                    "type": "string"
+                }
+            }
+        },
+        "quiz.QuizDetail": {
+            "type": "object",
+            "properties": {
+                "course_id": {
+                    "type": "string"
+                },
+                "description": {
+                    "type": "string"
+                },
+                "help": {
+                    "type": "string"
+                },
+                "id": {
+                    "type": "string"
+                },
+                "media": {
+                    "type": "array",
+                    "items": {
+                        "$ref": "#/definitions/quiz.QuizMedia"
+                    }
+                },
+                "name": {
+                    "type": "string"
+                },
+                "problems": {
+                    "type": "array",
+                    "items": {
+                        "$ref": "#/definitions/quiz.QuizProblem"
+                    }
+                }
+            }
+        },
+        "quiz.QuizMedia": {
+            "type": "object",
+            "properties": {
+                "id": {
+                    "type": "string"
+                },
+                "type": {
+                    "type": "string"
+                },
+                "url": {
+                    "type": "string"
+                }
+            }
+        },
+        "quiz.QuizProblem": {
+            "type": "object",
+            "properties": {
+                "answers": {
+                    "type": "array",
+                    "items": {
+                        "$ref": "#/definitions/quiz.AnswerOption"
+                    }
+                },
+                "id": {
+                    "type": "string"
+                },
+                "media_id": {
+                    "type": "array",
+                    "items": {
+                        "type": "string"
+                    }
+                },
+                "question": {
+                    "type": "string"
+                }
+            }
+        },
+        "quiz.Response": {
+            "type": "object",
+            "properties": {
+                "answer_id": {
+                    "type": "string"
+                },
+                "problem_id": {
+                    "type": "string"
+                }
+            }
+        },
         "refresh.RefreshResponsePayload": {
             "description": "Refresh endpoint response when process success",
             "type": "object",
diff --git a/docs/swagger.json b/docs/swagger.json
index 42693b034ca16e65ac3ffa432905cdbcaaeff8c3..883d3d799d994fab91eb3dbf9e1aee4d8483c36f 100644
--- a/docs/swagger.json
+++ b/docs/swagger.json
@@ -1432,7 +1432,98 @@
                 }
             }
         },
+        "/course/{id}/quiz": {
+            "get": {
+                "description": "Get all cours",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "course"
+                ],
+                "summary": "Get Course quiz",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "format": "uuid",
+                        "description": "Course id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "allOf": [
+                                {
+                                    "$ref": "#/definitions/web.BaseResponse"
+                                },
+                                {
+                                    "type": "object",
+                                    "properties": {
+                                        "data": {
+                                            "type": "array",
+                                            "items": {
+                                                "$ref": "#/definitions/quiz.Quiz"
+                                            }
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    }
+                }
+            }
+        },
         "/material/{id}": {
+            "get": {
+                "description": "Get material detail",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "content"
+                ],
+                "summary": "Get material detail",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "example": "IF3270",
+                        "description": "Material id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "allOf": [
+                                {
+                                    "$ref": "#/definitions/web.BaseResponse"
+                                },
+                                {
+                                    "type": "object",
+                                    "properties": {
+                                        "data": {
+                                            "$ref": "#/definitions/material.Material"
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    }
+                }
+            },
             "post": {
                 "description": "Add content of material",
                 "consumes": [
@@ -1591,6 +1682,164 @@
                 }
             }
         },
+        "/quiz/{id}": {
+            "get": {
+                "description": "Get Quiz Detail",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "quiz"
+                ],
+                "summary": "Get Quiz Detail",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "format": "uuid",
+                        "description": "Quiz id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "allOf": [
+                                {
+                                    "$ref": "#/definitions/web.BaseResponse"
+                                },
+                                {
+                                    "type": "object",
+                                    "properties": {
+                                        "data": {
+                                            "$ref": "#/definitions/quiz.Quiz"
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    }
+                }
+            }
+        },
+        "/quiz/{id}/solution": {
+            "get": {
+                "description": "Take a quiz",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "quiz"
+                ],
+                "summary": "Get Quiz Solution",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Authenticate User (any role)",
+                        "name": "Authorization",
+                        "in": "header",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "format": "uuid",
+                        "description": "Quiz id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "allOf": [
+                                {
+                                    "$ref": "#/definitions/web.BaseResponse"
+                                },
+                                {
+                                    "type": "object",
+                                    "properties": {
+                                        "data": {
+                                            "$ref": "#/definitions/quiz.QuizDetail"
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    }
+                }
+            }
+        },
+        "/quiz/{id}/take": {
+            "post": {
+                "description": "Finish quiz session and get the score",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "quiz"
+                ],
+                "summary": "Finish Quiz",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Authenticate User (any role)",
+                        "name": "Authorization",
+                        "in": "header",
+                        "required": true
+                    },
+                    {
+                        "description": "Quiz Finish payload",
+                        "name": "data",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/quiz.FinishQuizPayload"
+                        }
+                    },
+                    {
+                        "type": "string",
+                        "format": "uuid",
+                        "description": "Quiz id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "allOf": [
+                                {
+                                    "$ref": "#/definitions/web.BaseResponse"
+                                },
+                                {
+                                    "type": "object",
+                                    "properties": {
+                                        "data": {
+                                            "$ref": "#/definitions/quiz.QuizDetail"
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    }
+                }
+            }
+        },
         "/reset/confirm": {
             "put": {
                 "description": "Do confirmation to reset password",
@@ -2102,6 +2351,134 @@
                 }
             }
         },
+        "quiz.AnswerOption": {
+            "type": "object",
+            "properties": {
+                "answer": {
+                    "type": "string"
+                },
+                "id": {
+                    "type": "string"
+                },
+                "is_solution": {
+                    "type": "boolean"
+                },
+                "media_id": {
+                    "type": "array",
+                    "items": {
+                        "type": "string"
+                    }
+                }
+            }
+        },
+        "quiz.FinishQuizPayload": {
+            "type": "object",
+            "properties": {
+                "data": {
+                    "type": "array",
+                    "items": {
+                        "$ref": "#/definitions/quiz.Response"
+                    }
+                }
+            }
+        },
+        "quiz.Quiz": {
+            "type": "object",
+            "properties": {
+                "course_id": {
+                    "type": "string"
+                },
+                "creator_email": {
+                    "type": "string"
+                },
+                "id": {
+                    "type": "string"
+                },
+                "nama": {
+                    "type": "string"
+                }
+            }
+        },
+        "quiz.QuizDetail": {
+            "type": "object",
+            "properties": {
+                "course_id": {
+                    "type": "string"
+                },
+                "description": {
+                    "type": "string"
+                },
+                "help": {
+                    "type": "string"
+                },
+                "id": {
+                    "type": "string"
+                },
+                "media": {
+                    "type": "array",
+                    "items": {
+                        "$ref": "#/definitions/quiz.QuizMedia"
+                    }
+                },
+                "name": {
+                    "type": "string"
+                },
+                "problems": {
+                    "type": "array",
+                    "items": {
+                        "$ref": "#/definitions/quiz.QuizProblem"
+                    }
+                }
+            }
+        },
+        "quiz.QuizMedia": {
+            "type": "object",
+            "properties": {
+                "id": {
+                    "type": "string"
+                },
+                "type": {
+                    "type": "string"
+                },
+                "url": {
+                    "type": "string"
+                }
+            }
+        },
+        "quiz.QuizProblem": {
+            "type": "object",
+            "properties": {
+                "answers": {
+                    "type": "array",
+                    "items": {
+                        "$ref": "#/definitions/quiz.AnswerOption"
+                    }
+                },
+                "id": {
+                    "type": "string"
+                },
+                "media_id": {
+                    "type": "array",
+                    "items": {
+                        "type": "string"
+                    }
+                },
+                "question": {
+                    "type": "string"
+                }
+            }
+        },
+        "quiz.Response": {
+            "type": "object",
+            "properties": {
+                "answer_id": {
+                    "type": "string"
+                },
+                "problem_id": {
+                    "type": "string"
+                }
+            }
+        },
         "refresh.RefreshResponsePayload": {
             "description": "Refresh endpoint response when process success",
             "type": "object",
diff --git a/docs/swagger.yaml b/docs/swagger.yaml
index 8356f559d011d170cbb138f0e37af8c463827c83..58ea9348fb473426c5890cff161bd98360f62bf0 100644
--- a/docs/swagger.yaml
+++ b/docs/swagger.yaml
@@ -298,6 +298,89 @@ definitions:
       upload_link:
         type: string
     type: object
+  quiz.AnswerOption:
+    properties:
+      answer:
+        type: string
+      id:
+        type: string
+      is_solution:
+        type: boolean
+      media_id:
+        items:
+          type: string
+        type: array
+    type: object
+  quiz.FinishQuizPayload:
+    properties:
+      data:
+        items:
+          $ref: '#/definitions/quiz.Response'
+        type: array
+    type: object
+  quiz.Quiz:
+    properties:
+      course_id:
+        type: string
+      creator_email:
+        type: string
+      id:
+        type: string
+      nama:
+        type: string
+    type: object
+  quiz.QuizDetail:
+    properties:
+      course_id:
+        type: string
+      description:
+        type: string
+      help:
+        type: string
+      id:
+        type: string
+      media:
+        items:
+          $ref: '#/definitions/quiz.QuizMedia'
+        type: array
+      name:
+        type: string
+      problems:
+        items:
+          $ref: '#/definitions/quiz.QuizProblem'
+        type: array
+    type: object
+  quiz.QuizMedia:
+    properties:
+      id:
+        type: string
+      type:
+        type: string
+      url:
+        type: string
+    type: object
+  quiz.QuizProblem:
+    properties:
+      answers:
+        items:
+          $ref: '#/definitions/quiz.AnswerOption'
+        type: array
+      id:
+        type: string
+      media_id:
+        items:
+          type: string
+        type: array
+      question:
+        type: string
+    type: object
+  quiz.Response:
+    properties:
+      answer_id:
+        type: string
+      problem_id:
+        type: string
+    type: object
   refresh.RefreshResponsePayload:
     description: Refresh endpoint response when process success
     properties:
@@ -917,6 +1000,35 @@ paths:
       summary: Get materials
       tags:
       - content
+  /course/{id}/quiz:
+    get:
+      consumes:
+      - application/json
+      description: Get all cours
+      parameters:
+      - description: Course id
+        format: uuid
+        in: path
+        name: id
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            allOf:
+            - $ref: '#/definitions/web.BaseResponse'
+            - properties:
+                data:
+                  items:
+                    $ref: '#/definitions/quiz.Quiz'
+                  type: array
+              type: object
+      summary: Get Course quiz
+      tags:
+      - course
   /course/faculty:
     get:
       description: Retrieves a list of all faculties
@@ -1332,6 +1444,32 @@ paths:
       summary: Delete material
       tags:
       - content
+    get:
+      consumes:
+      - application/json
+      description: Get material detail
+      parameters:
+      - description: Material id
+        example: IF3270
+        in: path
+        name: id
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            allOf:
+            - $ref: '#/definitions/web.BaseResponse'
+            - properties:
+                data:
+                  $ref: '#/definitions/material.Material'
+              type: object
+      summary: Get material detail
+      tags:
+      - content
     post:
       consumes:
       - application/json
@@ -1410,6 +1548,103 @@ paths:
       summary: Delete Content
       tags:
       - content
+  /quiz/{id}:
+    get:
+      consumes:
+      - application/json
+      description: Get Quiz Detail
+      parameters:
+      - description: Quiz id
+        format: uuid
+        in: path
+        name: id
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            allOf:
+            - $ref: '#/definitions/web.BaseResponse'
+            - properties:
+                data:
+                  $ref: '#/definitions/quiz.Quiz'
+              type: object
+      summary: Get Quiz Detail
+      tags:
+      - quiz
+  /quiz/{id}/solution:
+    get:
+      consumes:
+      - application/json
+      description: Take a quiz
+      parameters:
+      - description: Authenticate User (any role)
+        in: header
+        name: Authorization
+        required: true
+        type: string
+      - description: Quiz id
+        format: uuid
+        in: path
+        name: id
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            allOf:
+            - $ref: '#/definitions/web.BaseResponse'
+            - properties:
+                data:
+                  $ref: '#/definitions/quiz.QuizDetail'
+              type: object
+      summary: Get Quiz Solution
+      tags:
+      - quiz
+  /quiz/{id}/take:
+    post:
+      consumes:
+      - application/json
+      description: Finish quiz session and get the score
+      parameters:
+      - description: Authenticate User (any role)
+        in: header
+        name: Authorization
+        required: true
+        type: string
+      - description: Quiz Finish payload
+        in: body
+        name: data
+        required: true
+        schema:
+          $ref: '#/definitions/quiz.FinishQuizPayload'
+      - description: Quiz id
+        format: uuid
+        in: path
+        name: id
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            allOf:
+            - $ref: '#/definitions/web.BaseResponse'
+            - properties:
+                data:
+                  $ref: '#/definitions/quiz.QuizDetail'
+              type: object
+      summary: Finish Quiz
+      tags:
+      - quiz
   /reset/confirm:
     put:
       description: Do confirmation to reset password
diff --git a/handler/di.go b/handler/di.go
index 643a1fcfdc45a1bb37cd1e2e197932b5e320cb55..399ef81ecbb23c9d70b0431c6129610c92d5dadc 100644
--- a/handler/di.go
+++ b/handler/di.go
@@ -7,6 +7,7 @@ import (
 	"gitlab.informatika.org/ocw/ocw-backend/handler/common"
 	"gitlab.informatika.org/ocw/ocw-backend/handler/course"
 	"gitlab.informatika.org/ocw/ocw-backend/handler/material"
+	"gitlab.informatika.org/ocw/ocw-backend/handler/quiz"
 	"gitlab.informatika.org/ocw/ocw-backend/handler/reset"
 	"gitlab.informatika.org/ocw/ocw-backend/handler/swagger"
 )
@@ -39,4 +40,8 @@ var HandlerSet = wire.NewSet(
 	// Material
 	wire.Struct(new(material.MaterialHandlerImpl), "*"),
 	wire.Bind(new(material.MaterialHandler), new(*material.MaterialHandlerImpl)),
+
+	// Quiz
+	wire.Struct(new(quiz.QuizHandlerImpl), "*"),
+	wire.Bind(new(quiz.QuizHandler), new(*quiz.QuizHandlerImpl)),
 )
diff --git a/handler/material/detail_material.go b/handler/material/detail_material.go
new file mode 100644
index 0000000000000000000000000000000000000000..87c428d09d68ddea8b6741dac355083f0082125b
--- /dev/null
+++ b/handler/material/detail_material.go
@@ -0,0 +1,57 @@
+package material
+
+import (
+	"net/http"
+
+	"github.com/go-chi/chi/v5"
+	"github.com/google/uuid"
+	"gitlab.informatika.org/ocw/ocw-backend/model/web"
+)
+
+// Index godoc
+//
+//	@Tags			content
+//	@Summary		Get material detail
+//	@Description	Get material detail
+//	@Produce		json
+//	@Accept			json
+//	@Param			id	path		string	true	"Material id"	example(IF3270)
+//	@Success		200	{object}	web.BaseResponse{data=material.Material}
+//	@Router			/material/{id} [get]
+func (m MaterialHandlerImpl) DetailMaterial(w http.ResponseWriter, r *http.Request) {
+	idString := chi.URLParam(r, "material-id")
+
+	if idString == "" {
+		payload := m.WrapperUtil.ErrorResponseWrap("material id is required", nil)
+		m.HttpUtil.WriteJson(w, http.StatusUnsupportedMediaType, payload)
+		return
+	}
+
+	id, err := uuid.Parse(idString)
+
+	if err != nil {
+		// invalid uuid
+		payload := m.WrapperUtil.ErrorResponseWrap(err.Error(), nil)
+		m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload)
+		return
+	}
+
+	res, err := m.MaterialService.GetById(id)
+
+	if err != nil {
+		respErr, ok := err.(web.ResponseError)
+		if ok {
+			payload := m.WrapperUtil.ErrorResponseWrap(respErr.Error(), respErr)
+
+			m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload)
+		} else {
+			payload := m.WrapperUtil.ErrorResponseWrap("internal server error", nil)
+			m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload)
+		}
+		return
+	}
+
+	responsePayload := m.WrapperUtil.SuccessResponseWrap(res)
+	m.HttpUtil.WriteSuccessJson(w, responsePayload)
+
+}
diff --git a/handler/material/get_material.go b/handler/material/get_material.go
index bc1009f92b06b087eeee36b8806257a5f5af0558..0ce5b5ab59c0aff2c604bb3965093ff3e9d242aa 100644
--- a/handler/material/get_material.go
+++ b/handler/material/get_material.go
@@ -33,11 +33,7 @@ func (m MaterialHandlerImpl) GetMaterial(w http.ResponseWriter, r *http.Request)
 		if ok {
 			payload := m.WrapperUtil.ErrorResponseWrap(respErr.Error(), respErr)
 
-			if respErr.Code != "NOT_OWNER" {
-				m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload)
-			} else {
-				m.HttpUtil.WriteJson(w, http.StatusForbidden, payload)
-			}
+			m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload)
 		} else {
 			payload := m.WrapperUtil.ErrorResponseWrap("internal server error", nil)
 			m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload)
diff --git a/handler/material/impl.go b/handler/material/impl.go
index 647ce941833b3c696b66271c41dd2565bb23a874..f8aab59ca45cb8ce12796599e6b197e1a0d8c418 100644
--- a/handler/material/impl.go
+++ b/handler/material/impl.go
@@ -12,7 +12,7 @@ type MaterialHandlerImpl struct {
 	material.MaterialService
 	material.MaterialContentService
 	httputil.HttpUtil
+	logger.Logger
 	wrapper.WrapperUtil
 	course.CourseRepository
-	logger.Logger
 }
diff --git a/handler/material/types.go b/handler/material/types.go
index 1c7cb4534dbb5780a6e3fbe5bd38f31d7bbb6369..8d968e0f8332c295dcc480310efa3461457d8654 100644
--- a/handler/material/types.go
+++ b/handler/material/types.go
@@ -7,6 +7,7 @@ type MaterialHandler interface {
 	DeleteContent(w http.ResponseWriter, r *http.Request)
 
 	CreateMaterial(w http.ResponseWriter, r *http.Request)
+	DetailMaterial(w http.ResponseWriter, r *http.Request)
 	DeleteMaterial(w http.ResponseWriter, r *http.Request)
 	GetMaterial(w http.ResponseWriter, r *http.Request)
 }
diff --git a/handler/quiz/get.go b/handler/quiz/get.go
new file mode 100644
index 0000000000000000000000000000000000000000..799ca822a991240a0c6febccb29640720f1b5e67
--- /dev/null
+++ b/handler/quiz/get.go
@@ -0,0 +1,57 @@
+package quiz
+
+import (
+	"net/http"
+
+	"github.com/go-chi/chi/v5"
+	"github.com/google/uuid"
+	"gitlab.informatika.org/ocw/ocw-backend/model/web"
+)
+
+// Index godoc
+//
+//	@Tags					quiz
+//	@Summary			Get Quiz Detail
+//	@Description	Get Quiz Detail
+//	@Produce			json
+//	@Accept				json
+//	@Param				id path string true "Quiz id" Format(uuid)
+//	@Success			200	{object}	web.BaseResponse{data=quiz.Quiz}
+//	@Router				/quiz/{id} [get]
+func (m QuizHandlerImpl) GetQuizDetail(w http.ResponseWriter, r *http.Request) {
+	quizId := chi.URLParam(r, "id")
+
+	if quizId == "" {
+		payload := m.WrapperUtil.ErrorResponseWrap("quiz id is required", nil)
+		m.HttpUtil.WriteJson(w, http.StatusUnsupportedMediaType, payload)
+		return
+	}
+
+	id, err := uuid.Parse(quizId)
+
+	if err != nil {
+		// invalid uuid
+		payload := m.WrapperUtil.ErrorResponseWrap(err.Error(), nil)
+		m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload)
+		return
+	}
+
+	result, err := m.QuizService.GetQuizDetail(id)
+
+	if err != nil {
+		respErr, ok := err.(web.ResponseError)
+		if ok {
+			payload := m.WrapperUtil.ErrorResponseWrap(respErr.Error(), respErr)
+
+			m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload)
+		} else {
+			payload := m.WrapperUtil.ErrorResponseWrap("internal server error", nil)
+			m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload)
+		}
+		return
+	}
+
+	responsePayload := m.WrapperUtil.SuccessResponseWrap(result)
+	m.HttpUtil.WriteSuccessJson(w, responsePayload)
+
+}
diff --git a/handler/quiz/impl.go b/handler/quiz/impl.go
new file mode 100644
index 0000000000000000000000000000000000000000..f95f7e5f0ca56fe2c605c5c3692eb5c4dab1008a
--- /dev/null
+++ b/handler/quiz/impl.go
@@ -0,0 +1,15 @@
+package quiz
+
+import (
+	"gitlab.informatika.org/ocw/ocw-backend/service/logger"
+	"gitlab.informatika.org/ocw/ocw-backend/service/quiz"
+	"gitlab.informatika.org/ocw/ocw-backend/utils/httputil"
+	"gitlab.informatika.org/ocw/ocw-backend/utils/wrapper"
+)
+
+type QuizHandlerImpl struct {
+	quiz.QuizService
+	wrapper.WrapperUtil
+	httputil.HttpUtil
+	logger.Logger
+}
diff --git a/handler/quiz/list.go b/handler/quiz/list.go
new file mode 100644
index 0000000000000000000000000000000000000000..544894fcc9985ee8ebea4f75fd3032da1ff0fd2d
--- /dev/null
+++ b/handler/quiz/list.go
@@ -0,0 +1,50 @@
+package quiz
+
+import (
+	"net/http"
+
+	"github.com/go-chi/chi/v5"
+	"gitlab.informatika.org/ocw/ocw-backend/model/web"
+)
+
+// Index godoc
+//
+//	@Tags					course
+//	@Summary			Get Course quiz
+//	@Description	Get all cours
+//	@Produce			json
+//	@Accept				json
+//	@Param				id path string true "Course id" Format(uuid)
+//	@Success			200	{object}	web.BaseResponse{data=[]quiz.Quiz}
+//	@Router				/course/{id}/quiz [get]
+func (m QuizHandlerImpl) GetAllQuizes(w http.ResponseWriter, r *http.Request) {
+	courseId := chi.URLParam(r, "id")
+
+	if courseId == "" {
+		payload := m.WrapperUtil.ErrorResponseWrap("course id is required", nil)
+		m.HttpUtil.WriteJson(w, http.StatusUnsupportedMediaType, payload)
+		return
+	}
+
+	result, err := m.QuizService.ListAllQuiz(courseId)
+
+	if err != nil {
+		respErr, ok := err.(web.ResponseError)
+		if ok {
+			payload := m.WrapperUtil.ErrorResponseWrap(respErr.Error(), respErr)
+
+			if respErr.Code != "NOT_OWNER" {
+				m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload)
+			} else {
+				m.HttpUtil.WriteJson(w, http.StatusForbidden, payload)
+			}
+		} else {
+			payload := m.WrapperUtil.ErrorResponseWrap("internal server error", nil)
+			m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload)
+		}
+		return
+	}
+
+	responsePayload := m.WrapperUtil.SuccessResponseWrap(result)
+	m.HttpUtil.WriteSuccessJson(w, responsePayload)
+}
diff --git a/handler/quiz/take.go b/handler/quiz/take.go
new file mode 100644
index 0000000000000000000000000000000000000000..530f4f77823001b1a5027b76d610b8c9ccafcaad
--- /dev/null
+++ b/handler/quiz/take.go
@@ -0,0 +1,219 @@
+package quiz
+
+import (
+	"net/http"
+
+	"github.com/go-chi/chi/v5"
+	"github.com/go-playground/validator/v10"
+	"github.com/google/uuid"
+	"gitlab.informatika.org/ocw/ocw-backend/middleware/guard"
+	"gitlab.informatika.org/ocw/ocw-backend/model/web"
+	authToken "gitlab.informatika.org/ocw/ocw-backend/model/web/auth/token"
+	"gitlab.informatika.org/ocw/ocw-backend/model/web/quiz"
+)
+
+// Index godoc
+//
+//	@Tags					quiz
+//	@Summary			Take Quiz
+//	@Description	Take a quiz
+//	@Produce			json
+//	@Accept				json
+//	@Param				Authorization	header		string							true "Authenticate User (any role)"
+//	@Param				id path string true "Quiz id" Format(uuid)
+//	@Success			200	{object}	web.BaseResponse{data=quiz.QuizDetail}
+//	@Router				/quiz/{id}/take [post]
+func (m QuizHandlerImpl) TakeQuiz(w http.ResponseWriter, r *http.Request) {
+	rawQuizId := chi.URLParam(r, "id")
+
+	if rawQuizId == "" {
+		payload := m.WrapperUtil.ErrorResponseWrap("quiz id is required", nil)
+		m.HttpUtil.WriteJson(w, http.StatusUnsupportedMediaType, payload)
+		return
+	}
+
+	quizId, err := uuid.Parse(rawQuizId)
+
+	if err != nil {
+		payload := m.WrapperUtil.ErrorResponseWrap("quiz id is not valid", nil)
+		m.HttpUtil.WriteJson(w, http.StatusUnsupportedMediaType, payload)
+		return
+	}
+
+	user, ok := r.Context().Value(guard.UserContext).(authToken.UserClaim)
+
+	if !ok {
+		m.Logger.Error("Context is not found")
+		payload := m.WrapperUtil.ErrorResponseWrap("internal server error", nil)
+		m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload)
+		return
+	}
+
+	detail, err := m.DoTakeQuiz(r.Context(), quizId, user.Email)
+
+	if err != nil {
+		respErr, ok := err.(web.ResponseError)
+		if ok {
+			payload := m.WrapperUtil.ErrorResponseWrap(respErr.Error(), respErr)
+			m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload)
+		} else {
+			payload := m.WrapperUtil.ErrorResponseWrap("internal server error", nil)
+			m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload)
+		}
+		return
+	}
+
+	responsePayload := m.WrapperUtil.SuccessResponseWrap(detail)
+	m.HttpUtil.WriteSuccessJson(w, responsePayload)
+}
+
+// Index godoc
+//
+//	@Tags					quiz
+//	@Summary			Get Quiz Solution
+//	@Description	Take a quiz
+//	@Produce			json
+//	@Accept				json
+//	@Param				Authorization	header		string							true "Authenticate User (any role)"
+//	@Param				id path string true "Quiz id" Format(uuid)
+//	@Success			200	{object}	web.BaseResponse{data=quiz.QuizDetail}
+//	@Router				/quiz/{id}/solution [get]
+func (m QuizHandlerImpl) GetQuizSolution(w http.ResponseWriter, r *http.Request) {
+	rawQuizId := chi.URLParam(r, "id")
+
+	if rawQuizId == "" {
+		payload := m.WrapperUtil.ErrorResponseWrap("quiz id is required", nil)
+		m.HttpUtil.WriteJson(w, http.StatusUnsupportedMediaType, payload)
+		return
+	}
+
+	quizId, err := uuid.Parse(rawQuizId)
+
+	if err != nil {
+		payload := m.WrapperUtil.ErrorResponseWrap("quiz id is not valid", nil)
+		m.HttpUtil.WriteJson(w, http.StatusUnsupportedMediaType, payload)
+		return
+	}
+
+	user, ok := r.Context().Value(guard.UserContext).(authToken.UserClaim)
+
+	if !ok {
+		m.Logger.Error("Context is not found")
+		payload := m.WrapperUtil.ErrorResponseWrap("internal server error", nil)
+		m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload)
+		return
+	}
+
+	detail, err := m.GetSolutionQuiz(r.Context(), quizId, user.Email)
+
+	if err != nil {
+		respErr, ok := err.(web.ResponseError)
+		if ok {
+			if respErr.Code == "ERR_NOT_ALLOWED" {
+				payload := m.WrapperUtil.ErrorResponseWrap(respErr.Error(), respErr)
+				m.HttpUtil.WriteJson(w, http.StatusForbidden, payload)
+			} else {
+				payload := m.WrapperUtil.ErrorResponseWrap(respErr.Error(), respErr)
+				m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload)
+			}
+		} else {
+			payload := m.WrapperUtil.ErrorResponseWrap("internal server error", nil)
+			m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload)
+		}
+		return
+	}
+
+	responsePayload := m.WrapperUtil.SuccessResponseWrap(detail)
+	m.HttpUtil.WriteSuccessJson(w, responsePayload)
+}
+
+// Index godoc
+//
+//	@Tags					quiz
+//	@Summary			Finish Quiz
+//	@Description	Finish quiz session and get the score
+//	@Produce			json
+//	@Accept				json
+//	@Param				Authorization	header		string										true "Authenticate User (any role)"
+//	@Param			  data	body		quiz.FinishQuizPayload							true	"Quiz Finish payload"
+//	@Param				id path string true "Quiz id" Format(uuid)
+//	@Success			200	{object}	web.BaseResponse{data=quiz.QuizDetail}
+//	@Router				/quiz/{id}/take [post]
+func (m QuizHandlerImpl) FinishQuiz(w http.ResponseWriter, r *http.Request) {
+	payload := quiz.FinishQuizPayload{}
+
+	/* Get user */
+	user, ok := r.Context().Value(guard.UserContext).(authToken.UserClaim)
+
+	if !ok {
+		m.Logger.Error("Context is not found")
+		payload := m.WrapperUtil.ErrorResponseWrap("internal server error", nil)
+		m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload)
+		return
+	}
+	/* Get user */
+
+	/* Validate input */
+	validate := validator.New()
+
+	if r.Header.Get("Content-Type") != "application/json" {
+		payload := m.WrapperUtil.ErrorResponseWrap("this service only receive json input", nil)
+		m.HttpUtil.WriteJson(w, http.StatusUnsupportedMediaType, payload)
+		return
+	}
+
+	if err := m.HttpUtil.ParseJson(r, &payload); err != nil {
+		payload := m.WrapperUtil.ErrorResponseWrap("invalid json input", err.Error())
+		m.HttpUtil.WriteJson(w, http.StatusUnprocessableEntity, payload)
+		return
+	}
+
+	if err := validate.Struct(payload); err != nil {
+		if _, ok := err.(*validator.InvalidValidationError); ok {
+			payload := m.WrapperUtil.ErrorResponseWrap(err.Error(), nil)
+			m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload)
+			return
+		}
+
+		errPayload := web.NewResponseErrorFromValidator(err.(validator.ValidationErrors))
+		payload := m.WrapperUtil.ErrorResponseWrap(errPayload.Error(), errPayload)
+		m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload)
+		return
+	}
+	/* End of validate */
+
+	/* Get quiz id */
+	rawQuizId := chi.URLParam(r, "id")
+
+	if rawQuizId == "" {
+		payload := m.WrapperUtil.ErrorResponseWrap("quiz id is required", nil)
+		m.HttpUtil.WriteJson(w, http.StatusUnsupportedMediaType, payload)
+		return
+	}
+
+	quizId, err := uuid.Parse(rawQuizId)
+
+	if err != nil {
+		payload := m.WrapperUtil.ErrorResponseWrap("quiz id is not valid", nil)
+		m.HttpUtil.WriteJson(w, http.StatusUnsupportedMediaType, payload)
+		return
+	}
+	/* end of get quiz id */
+
+	res, err := m.DoFinishQuiz(r.Context(), quizId, user.Email, payload.Data)
+
+	if err != nil {
+		respErr, ok := err.(web.ResponseError)
+		if ok {
+			payload := m.WrapperUtil.ErrorResponseWrap(respErr.Error(), respErr)
+			m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload)
+		} else {
+			payload := m.WrapperUtil.ErrorResponseWrap("internal server error", nil)
+			m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload)
+		}
+		return
+	}
+
+	responsePayload := m.WrapperUtil.SuccessResponseWrap(res)
+	m.HttpUtil.WriteSuccessJson(w, responsePayload)
+}
diff --git a/handler/quiz/type.go b/handler/quiz/type.go
new file mode 100644
index 0000000000000000000000000000000000000000..8c3bf6f5567cf7f04be9e42b67558279a0f6159d
--- /dev/null
+++ b/handler/quiz/type.go
@@ -0,0 +1,12 @@
+package quiz
+
+import "net/http"
+
+type QuizHandler interface {
+	GetAllQuizes(w http.ResponseWriter, r *http.Request)
+	GetQuizDetail(w http.ResponseWriter, r *http.Request)
+
+	TakeQuiz(w http.ResponseWriter, r *http.Request)
+	GetQuizSolution(w http.ResponseWriter, r *http.Request)
+	FinishQuiz(w http.ResponseWriter, r *http.Request)
+}
diff --git a/middleware/guard/builder.go b/middleware/guard/builder.go
index 75850ebf45a8ef08662c76311156cd2a067a4ff3..c0f06a9e6c2324734c1db6f64e5b5eb8e34f46d8 100644
--- a/middleware/guard/builder.go
+++ b/middleware/guard/builder.go
@@ -10,7 +10,9 @@ import (
 )
 
 type GuardBuilder struct {
-	GuardMiddleware
+	token.TokenUtil
+	logger.Logger
+	wrapper.WrapperUtil
 }
 
 func NewBuilder(
@@ -19,25 +21,19 @@ func NewBuilder(
 	wrapper wrapper.WrapperUtil,
 ) *GuardBuilder {
 	return &GuardBuilder{
-		GuardMiddleware{
-			Token:       token,
-			Role:        []user.UserRole{},
-			Logger:      logger,
-			WrapperUtil: wrapper,
-		},
+		token,
+		logger,
+		wrapper,
 	}
 }
 
-func (g *GuardBuilder) AddRole(role ...user.UserRole) *GuardBuilder {
-	g.GuardMiddleware.Role = role
-	return g
-}
-
-func (g *GuardBuilder) Build() func(http.Handler) http.Handler {
-	return g.GuardMiddleware.Handle
-}
+func (g *GuardBuilder) Build(role ...user.UserRole) func(http.Handler) http.Handler {
+	handler := &GuardMiddleware{
+		Token:       g.TokenUtil,
+		Role:        role,
+		Logger:      g.Logger,
+		WrapperUtil: g.WrapperUtil,
+	}
 
-func (g *GuardBuilder) BuildSimple(role user.UserRole) func(http.Handler) http.Handler {
-	g.AddRole(role)
-	return g.Build()
+	return handler.Handle
 }
diff --git a/model/domain/quiz/options.go b/model/domain/quiz/options.go
deleted file mode 100644
index d2bdddbf7da440ce8b48385cd7a5d5b0f118bcef..0000000000000000000000000000000000000000
--- a/model/domain/quiz/options.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package quiz
-
-import "github.com/google/uuid"
-
-type AnswerOption struct {
-	Id            uuid.UUID `gorm:"primaryKey" json:"id"`
-	QuizProblemId uuid.UUID `gorm:"primaryKey" json:"problem_id"`
-	Statement     string    `json:"statement"`
-	IsAnswer      bool      `json:"isAnswer"`
-}
-
-func (AnswerOption) TableName() string {
-	return "quiz_choice_answer"
-}
diff --git a/model/domain/quiz/problem.go b/model/domain/quiz/problem.go
new file mode 100644
index 0000000000000000000000000000000000000000..7794b7f4355177f0ce5a948d11c6ff3f62aa89de
--- /dev/null
+++ b/model/domain/quiz/problem.go
@@ -0,0 +1,38 @@
+package quiz
+
+import "github.com/google/uuid"
+
+type QuizMedia struct {
+	Id   uuid.UUID `json:"id"`
+	Url  string    `json:"url"`
+	Type string    `json:"type"`
+}
+
+type AnswerOption struct {
+	Id         uuid.UUID   `json:"id"`
+	MediaId    []uuid.UUID `json:"media_id"`
+	Answer     string      `json:"answer"`
+	IsSolution *bool       `json:"is_solution"`
+}
+
+type QuizProblem struct {
+	Id       uuid.UUID      `json:"id"`
+	MediaId  []uuid.UUID    `json:"media_id"`
+	Question string         `json:"question"`
+	Answer   []AnswerOption `json:"answers"`
+}
+
+type QuizDetail struct {
+	Id          uuid.UUID     `json:"id"`
+	Name        string        `json:"name"`
+	CourseId    string        `json:"course_id"`
+	Description string        `json:"description"`
+	Help        string        `json:"help"`
+	Media       []QuizMedia   `json:"media"`
+	Problems    []QuizProblem `json:"problems"`
+}
+
+type Response struct {
+	ProblemId uuid.UUID `json:"problem_id"`
+	AnswerId  uuid.UUID `json:"answer_id"`
+}
diff --git a/model/domain/quiz/problem_type.go b/model/domain/quiz/problem_type.go
deleted file mode 100644
index c03621526c635dc524f1caee1518665931b80d10..0000000000000000000000000000000000000000
--- a/model/domain/quiz/problem_type.go
+++ /dev/null
@@ -1,67 +0,0 @@
-package quiz
-
-import (
-	"database/sql/driver"
-	"encoding/json"
-	"errors"
-	"fmt"
-)
-
-type ProblemType int
-
-const (
-	Choice ProblemType = iota
-)
-
-var roleMapping = map[ProblemType]string{
-	Choice: "choice",
-}
-
-func (ur *ProblemType) Scan(value interface{}) error {
-	val := value.(string)
-
-	for key, label := range roleMapping {
-		if label == val {
-			*ur = key
-			return nil
-		}
-	}
-
-	return fmt.Errorf("invalid user role")
-}
-
-func (u ProblemType) Value() (driver.Value, error) {
-	value, ok := roleMapping[u]
-
-	if !ok {
-		return nil, fmt.Errorf("invalid user role")
-	}
-
-	return value, nil
-}
-
-func (u *ProblemType) UnmarshalJSON(b []byte) error {
-	var s string
-	if err := json.Unmarshal(b, &s); err != nil {
-		return err
-	}
-
-	for key, label := range roleMapping {
-		if label == s {
-			*u = key
-			return nil
-		}
-	}
-
-	return fmt.Errorf("unkown role, given %s", s)
-}
-
-func (u ProblemType) MarshalJSON() ([]byte, error) {
-	s, ok := roleMapping[u]
-
-	if !ok {
-		return nil, errors.New("unkown user role")
-	}
-
-	return json.Marshal(s)
-}
diff --git a/model/domain/quiz/quiz.go b/model/domain/quiz/quiz.go
index f4dcce5a8155d1c0a670dd962bd549233d95b2d8..896536cc66c3ffbca9840836c446019b139e8074 100644
--- a/model/domain/quiz/quiz.go
+++ b/model/domain/quiz/quiz.go
@@ -2,18 +2,14 @@ package quiz
 
 import (
 	"github.com/google/uuid"
-	"gitlab.informatika.org/ocw/ocw-backend/model/domain/course"
-	"gitlab.informatika.org/ocw/ocw-backend/model/domain/user"
 )
 
 type Quiz struct {
-	Id           uuid.UUID     `gorm:"primaryKey" json:"id"`
-	Name         string        `json:"name"`
-	CourseId     string        `json:"course_id"`
-	CreatorEmail string        `json:"creator_email"`
-	Creator      user.User     `gorm:"foreignKey:CreatorEmail;references:Email" json:"creator"`
-	Course       course.Course `gorm:"foreignKey:CourseId;references:Id" json:"course"`
-	Problems     []QuizProblem `gorm:"foreignKey:QuizId;references:Id" json:"problems"`
+	Id           uuid.UUID `gorm:"primaryKey" json:"id"`
+	Name         string    `json:"nama"`
+	CourseId     string    `json:"course_id"`
+	CreatorEmail string    `json:"creator_email"`
+	QuizPath     string    `json:"-"`
 }
 
 func (Quiz) TableName() string {
diff --git a/model/domain/quiz/quiz_problem.go b/model/domain/quiz/quiz_problem.go
deleted file mode 100644
index 1bcdcf880ad9eaeeadd164cf60d253fd9eedb8db..0000000000000000000000000000000000000000
--- a/model/domain/quiz/quiz_problem.go
+++ /dev/null
@@ -1,15 +0,0 @@
-package quiz
-
-import "github.com/google/uuid"
-
-type QuizProblem struct {
-	Id        uuid.UUID      `gorm:"primaryKey" json:"id"`
-	Statement string         `json:"statement"`
-	Type      ProblemType    `json:"type"`
-	QuizId    uuid.UUID      `json:"quiz_id"`
-	Options   []AnswerOption `gorm:"foreignKey:QuizProblemId;references:Id" json:"options"`
-}
-
-func (QuizProblem) TableName() string {
-	return "quiz_problem"
-}
diff --git a/model/domain/quiz/take.go b/model/domain/quiz/take.go
index 2c707ab601da28e630420015393607aef0f9d228..399989d2db63432372643f615caecafee0cbc896 100644
--- a/model/domain/quiz/take.go
+++ b/model/domain/quiz/take.go
@@ -1,22 +1,18 @@
 package quiz
 
 import (
-	"os/user"
 	"time"
 
 	"github.com/google/uuid"
 )
 
 type QuizTake struct {
-	Id            uuid.UUID `gorm:"primaryKey" json:"id"`
-	QuizId        uuid.UUID `json:"quiz_id"`
-	Email         string    `json:"email"`
-	StartTime     time.Time `json:"start"`
-	IsFinished    bool      `json:"finished"`
-	Score         int       `json:"score"`
-	Quiz          `gorm:"foreignKey:QuizId;references:Id" json:"quiz"`
-	user.User     `gorm:"foreignKey:Email;references:Email" json:"user"`
-	ChoiceAnswers []TakeChoiceAnswer `gorm:"foreignKey:QuizTakeId;references:Id" json:"-"`
+	Id         uuid.UUID `gorm:"primaryKey" json:"id"`
+	QuizId     uuid.UUID `json:"quiz_id"`
+	Email      string    `json:"email"`
+	StartTime  time.Time `json:"start"`
+	IsFinished bool      `json:"finished"`
+	Score      int       `json:"score"`
 }
 
 func (QuizTake) TableName() string {
diff --git a/model/domain/quiz/take_choice_answer.go b/model/domain/quiz/take_choice_answer.go
deleted file mode 100644
index 25e76746d8951251d3dbdee694986205ce364d53..0000000000000000000000000000000000000000
--- a/model/domain/quiz/take_choice_answer.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package quiz
-
-import "github.com/google/uuid"
-
-type TakeChoiceAnswer struct {
-	QuizTakeId    uuid.UUID `gorm:"primaryKey"`
-	AnswerChoice  uuid.UUID
-	QuizProblemId uuid.UUID `gorm:"primaryKey"`
-	AnswerOption  `gorm:"foreignKey:AnswerChoice,QuizProblemId;references:Id,QuizProblemId"`
-}
-
-func (TakeChoiceAnswer) TableName() string {
-	return "quiz_take_choice_answer"
-}
diff --git a/model/web/quiz/finish.go b/model/web/quiz/finish.go
new file mode 100644
index 0000000000000000000000000000000000000000..80c7335963d5977527460cf5ada30201865723e4
--- /dev/null
+++ b/model/web/quiz/finish.go
@@ -0,0 +1,7 @@
+package quiz
+
+import "gitlab.informatika.org/ocw/ocw-backend/model/domain/quiz"
+
+type FinishQuizPayload struct {
+	Data []quiz.Response `json:"data"`
+}
diff --git a/provider/storage/manager.go b/provider/storage/manager.go
index cc87edd73acda90734470345e5fd3ea1230a1280..cfeae57144fe4d23729a2382984360253e5d4659 100644
--- a/provider/storage/manager.go
+++ b/provider/storage/manager.go
@@ -9,3 +9,16 @@ import (
 func (s S3Storage) Delete(ctx context.Context, path string) error {
 	return s.minio.RemoveObject(ctx, s.env.BucketName, path, minio.RemoveObjectOptions{})
 }
+
+func (s S3Storage) Get(ctx context.Context, path string) ([]byte, error) {
+	result := []byte{}
+	obj, err := s.minio.GetObject(ctx, s.env.BucketName, path, minio.GetObjectOptions{})
+
+	if err != nil {
+		return result, err
+	}
+
+	_, err = obj.Read(result)
+
+	return result, err
+}
diff --git a/provider/storage/type.go b/provider/storage/type.go
index dc7cdd2f97597d8760d26aa2b75791989a12ff9f..19f2a46fb4d9044839660ba4234f47f5f661f0d2 100644
--- a/provider/storage/type.go
+++ b/provider/storage/type.go
@@ -9,4 +9,5 @@ type Storage interface {
 	CreatePutSignedLink(ctx context.Context, path string) (string, error)
 	CreateGetSignedLink(ctx context.Context, path string, reqParam url.Values) (string, error)
 	Delete(ctx context.Context, path string) error
+	Get(ctx context.Context, path string) ([]byte, error)
 }
diff --git a/repository/di.go b/repository/di.go
index 9136c031788ba4f56d8d3bfe4c46f2864b641571..b564bd5406f7d94427c5c0f80ccc518bc23fd5b2 100644
--- a/repository/di.go
+++ b/repository/di.go
@@ -2,12 +2,13 @@ package repository
 
 import (
 	"github.com/google/wire"
-	"gitlab.informatika.org/ocw/ocw-backend/repository/user"
-	"gitlab.informatika.org/ocw/ocw-backend/repository/course"
 	"gitlab.informatika.org/ocw/ocw-backend/repository/cache"
 	"gitlab.informatika.org/ocw/ocw-backend/repository/content"
+	"gitlab.informatika.org/ocw/ocw-backend/repository/course"
 	"gitlab.informatika.org/ocw/ocw-backend/repository/material"
+	"gitlab.informatika.org/ocw/ocw-backend/repository/quiz"
 	"gitlab.informatika.org/ocw/ocw-backend/repository/transaction"
+	"gitlab.informatika.org/ocw/ocw-backend/repository/user"
 )
 
 var RepositoryBasicSet = wire.NewSet(
@@ -37,6 +38,9 @@ var RepositoryBasicSet = wire.NewSet(
 	transaction.NewBuilder,
 	wire.Bind(new(transaction.Transaction), new(*transaction.TransactionRepositoryImpl)),
 	wire.Bind(new(transaction.TransactionBuilder), new(*transaction.TransactionBuilderImpl)),
+
+	quiz.New,
+	wire.Bind(new(quiz.QuizRepository), new(*quiz.QuizRepositoryImpl)),
 )
 
 var RepositorySet = wire.NewSet(
diff --git a/repository/material/material.go b/repository/material/material.go
index 9cecd65f3fec546c254cfd731fde21660b4f684e..87cbd435c379d505d4be048f8d1d5e878ffba019 100644
--- a/repository/material/material.go
+++ b/repository/material/material.go
@@ -20,6 +20,12 @@ func NewMaterial(
 	return &MaterialRepositoryImpl{builder, db.Connect()}
 }
 
+func (m MaterialRepositoryImpl) Get(materialId uuid.UUID) (*material.Material, error) {
+	res := &material.Material{}
+	err := m.db.Preload("Contents").Where("id = ?", materialId).Find(res).Error
+	return res, err
+}
+
 func (m MaterialRepositoryImpl) IsUserContributor(id uuid.UUID, email string) (bool, error) {
 	err := m.db.Where("creator_email = ? AND id = ?", email, id).Find(&material.Material{}).Error
 	if err != nil {
diff --git a/repository/material/type.go b/repository/material/type.go
index 4a0d0920746e05e4ccdfead1096db39dda8402db..4014763d006c08c2eabeb294a8f043ae023d0b17 100644
--- a/repository/material/type.go
+++ b/repository/material/type.go
@@ -9,6 +9,7 @@ import (
 type MaterialRepository interface {
 	New(courseId string, creatorEmail string, name string) (uuid.UUID, error)
 	Delete(id uuid.UUID) error
+	Get(materialId uuid.UUID) (*material.Material, error)
 	GetAll(courseId string) ([]material.Material, error)
 
 	IsUserContributor(id uuid.UUID, email string) (bool, error)
diff --git a/repository/quiz/impl.go b/repository/quiz/impl.go
new file mode 100644
index 0000000000000000000000000000000000000000..e51495422f1a10911dd47bf00b1764eb541446ab
--- /dev/null
+++ b/repository/quiz/impl.go
@@ -0,0 +1,97 @@
+package quiz
+
+import (
+	"errors"
+	"time"
+
+	"github.com/google/uuid"
+	"gitlab.informatika.org/ocw/ocw-backend/model/domain/quiz"
+	"gitlab.informatika.org/ocw/ocw-backend/model/web"
+	"gitlab.informatika.org/ocw/ocw-backend/provider/db"
+	"gorm.io/gorm"
+)
+
+type QuizRepositoryImpl struct {
+	db *gorm.DB
+}
+
+func New(
+	db db.Database,
+) *QuizRepositoryImpl {
+	return &QuizRepositoryImpl{db.Connect()}
+}
+
+func (q *QuizRepositoryImpl) GetQuizes(courseId string) ([]quiz.Quiz, error) {
+	result := &[]quiz.Quiz{}
+	err := q.db.Where("course_id = ?", courseId).Find(result).Error
+
+	return *result, err
+}
+
+func (q *QuizRepositoryImpl) GetQuizDetail(quizId uuid.UUID) (*quiz.Quiz, error) {
+	result := &quiz.Quiz{}
+	err := q.db.Where("id = ?", quizId).First(result).Error
+
+	if errors.Is(err, gorm.ErrRecordNotFound) {
+		return nil, web.NewResponseError("Record not found", "ERR_NOT_FOUND")
+	}
+
+	return result, nil
+}
+
+func (q *QuizRepositoryImpl) UpdateScore(takeId uuid.UUID, score int) error {
+	return q.db.
+		Model(&quiz.QuizTake{}).
+		Update("score", score).
+		Update("is_finished", true).
+		Where("id = ?", takeId).Error
+}
+
+func (q *QuizRepositoryImpl) NewTake(quizId uuid.UUID, userEmail string) (uuid.UUID, error) {
+	id := uuid.New()
+	err := q.db.Create(
+		&quiz.QuizTake{
+			Id:         id,
+			Email:      userEmail,
+			StartTime:  time.Now(),
+			QuizId:     quizId,
+			IsFinished: false,
+			Score:      0,
+		},
+	).Error
+
+	return id, err
+}
+
+func (q *QuizRepositoryImpl) IsActiveTake(quizId uuid.UUID, userEmail string) (bool, error) {
+	result := struct{ cnt int }{}
+	err := q.db.
+		Select("COUNT(*) as cnt").
+		Where("quiz_id = ? AND email = ? AND is_finished = false", quizId, userEmail).
+		Find(result).
+		Error
+
+	if err != nil {
+		return false, nil
+	}
+
+	return result.cnt > 0, nil
+}
+
+func (q *QuizRepositoryImpl) GetAllTake(quizId uuid.UUID, userEmail string) ([]quiz.QuizTake, error) {
+	result := []quiz.QuizTake{}
+	err := q.db.
+		Where("quiz_id = ? AND email = ?", quizId, userEmail).
+		Find(result).Error
+
+	return result, err
+}
+
+func (q *QuizRepositoryImpl) GetLastTake(quizId uuid.UUID, userEmail string) (*quiz.QuizTake, error) {
+	result := &quiz.QuizTake{}
+	err := q.db.
+		Where("quiz_id = ? AND email = ?", quizId, userEmail).
+		Last(result).Error
+
+	return result, err
+}
diff --git a/repository/quiz/type.go b/repository/quiz/type.go
new file mode 100644
index 0000000000000000000000000000000000000000..1b4e0cbeca391ec136dfc8c696d1c058d49e55e4
--- /dev/null
+++ b/repository/quiz/type.go
@@ -0,0 +1,16 @@
+package quiz
+
+import (
+	"github.com/google/uuid"
+	"gitlab.informatika.org/ocw/ocw-backend/model/domain/quiz"
+)
+
+type QuizRepository interface {
+	GetQuizes(courseId string) ([]quiz.Quiz, error)
+	GetQuizDetail(quizId uuid.UUID) (*quiz.Quiz, error)
+	UpdateScore(takeId uuid.UUID, score int) error
+	NewTake(quizId uuid.UUID, userEmail string) (uuid.UUID, error)
+	IsActiveTake(quizId uuid.UUID, userEmail string) (bool, error)
+	GetAllTake(quizId uuid.UUID, userEmail string) ([]quiz.QuizTake, error)
+	GetLastTake(quizId uuid.UUID, userEmail string) (*quiz.QuizTake, error)
+}
diff --git a/routes/admin/route.go b/routes/admin/route.go
index d1ff589b86faa181e6e671bf7111ab3efed0b239..d1fe8e2213ad86d7b7bdd1ecf4fcac90ab8f60a5 100644
--- a/routes/admin/route.go
+++ b/routes/admin/route.go
@@ -14,7 +14,7 @@ type AdminRoutes struct {
 
 func (adr AdminRoutes) Register(r chi.Router) {
 	r.Route("/admin", func(r chi.Router) {
-		r.Use(adr.GuardBuilder.BuildSimple(user.Admin))
+		r.Use(adr.GuardBuilder.Build(user.Admin))
 
 		r.Get("/user", adr.AdminHandler.GetAllUser)
 		r.Get("/user/{email}", adr.AdminHandler.GetUserByEmail)
diff --git a/routes/course/route.go b/routes/course/route.go
index 23cb9f533b686bbf87655fcf1186ff319f1a0f6a..43a8305f4187ad99c9e7baadea375a59f373ad37 100644
--- a/routes/course/route.go
+++ b/routes/course/route.go
@@ -36,7 +36,7 @@ func (c CourseRoutes) Register(r chi.Router) {
 	})
 
 	r.Route("/course/{id}/material", func(r chi.Router) {
-		r.Use(c.BuildSimple(user.Contributor))
+		r.Use(c.Build(user.Contributor))
 		r.Post("/", c.MaterialHandler.CreateMaterial)
 	})
 }
diff --git a/routes/di.go b/routes/di.go
index e05148661337dd26d5400800ca389644f93aa3be..ddae40f95c5cde473aadc4f6d26a357c98b5f089 100644
--- a/routes/di.go
+++ b/routes/di.go
@@ -7,6 +7,7 @@ import (
 	"gitlab.informatika.org/ocw/ocw-backend/routes/common"
 	"gitlab.informatika.org/ocw/ocw-backend/routes/course"
 	"gitlab.informatika.org/ocw/ocw-backend/routes/material"
+	"gitlab.informatika.org/ocw/ocw-backend/routes/quiz"
 	"gitlab.informatika.org/ocw/ocw-backend/routes/reset"
 	"gitlab.informatika.org/ocw/ocw-backend/routes/swagger"
 )
@@ -19,6 +20,7 @@ var routesCollectionSet = wire.NewSet(
 	wire.Struct(new(reset.ResetRoutes), "*"),
 	wire.Struct(new(course.CourseRoutes), "*"),
 	wire.Struct(new(material.MaterialRoutes), "*"),
+	wire.Struct(new(quiz.QuizRoutes), "*"),
 )
 
 var RoutesSet = wire.NewSet(
diff --git a/routes/material/route.go b/routes/material/route.go
index 31fc398e8a45b45537ac0c72b661c5f404fb239b..41126ecf7b9b60c1f56f9bd77208e9486db51ef0 100644
--- a/routes/material/route.go
+++ b/routes/material/route.go
@@ -14,13 +14,17 @@ type MaterialRoutes struct {
 
 func (c MaterialRoutes) Register(r chi.Router) {
 	r.Route("/material/{material-id}", func(r chi.Router) {
-		r.Use(c.GuardBuilder.BuildSimple(user.Contributor))
+		r.Get("/", c.DetailMaterial)
 
-		// Add
-		r.Post("/content", c.AddContent)
+		r.Route("/", func(r chi.Router) {
+			r.Use(c.GuardBuilder.Build(user.Contributor))
 
-		// Delete
-		r.Delete("/", c.DeleteMaterial)
-		r.Delete("/content/{content-id}", c.DeleteContent)
+			// Add
+			r.Post("/content", c.AddContent)
+
+			// Delete
+			r.Delete("/", c.DeleteMaterial)
+			r.Delete("/content/{content-id}", c.DeleteContent)
+		})
 	})
 }
diff --git a/routes/quiz/route.go b/routes/quiz/route.go
new file mode 100644
index 0000000000000000000000000000000000000000..b89269412c9add8ccc39a190a965f187a13c15b9
--- /dev/null
+++ b/routes/quiz/route.go
@@ -0,0 +1,39 @@
+package quiz
+
+import (
+	"github.com/go-chi/chi/v5"
+	"gitlab.informatika.org/ocw/ocw-backend/handler/quiz"
+	"gitlab.informatika.org/ocw/ocw-backend/middleware/guard"
+	"gitlab.informatika.org/ocw/ocw-backend/model/domain/user"
+)
+
+type QuizRoutes struct {
+	quiz.QuizHandler
+	*guard.GuardBuilder
+}
+
+func (q QuizRoutes) Register(r chi.Router) {
+	r.Get("/course/{id}/quiz", q.QuizHandler.GetAllQuizes)
+	r.Get("/quiz/{id}", q.QuizHandler.GetQuizDetail)
+
+	guard := q.GuardBuilder.Build(
+		user.Student,
+		user.Contributor,
+		user.Admin,
+	)
+
+	r.Route("/quiz/{id}/take", func(r chi.Router) {
+		r.Use(guard)
+		r.Post("/", q.QuizHandler.TakeQuiz)
+	})
+
+	r.Route("/quiz/{id}/finish", func(r chi.Router) {
+		r.Use(guard)
+		r.Post("/", q.QuizHandler.FinishQuiz)
+	})
+
+	r.Route("/quiz/{id}/solution", func(r chi.Router) {
+		r.Use(guard)
+		r.Get("/", q.QuizHandler.GetQuizSolution)
+	})
+}
diff --git a/routes/routes.go b/routes/routes.go
index 63e1d79fb448d35384bc6f062d360cdb2a7a258f..096f4d5005e2bfe01f82bfe53552edc99b1d0258 100644
--- a/routes/routes.go
+++ b/routes/routes.go
@@ -6,6 +6,7 @@ import (
 	"gitlab.informatika.org/ocw/ocw-backend/routes/common"
 	"gitlab.informatika.org/ocw/ocw-backend/routes/course"
 	"gitlab.informatika.org/ocw/ocw-backend/routes/material"
+	"gitlab.informatika.org/ocw/ocw-backend/routes/quiz"
 	"gitlab.informatika.org/ocw/ocw-backend/routes/reset"
 	"gitlab.informatika.org/ocw/ocw-backend/routes/swagger"
 
@@ -19,6 +20,7 @@ type AppRouter struct {
 	common.CommonRoutes
 	auth.AuthRoutes
 	reset.ResetRoutes
+	quiz.QuizRoutes
 	course.CourseRoutes
 	material.MaterialRoutes
 
diff --git a/service/course/add.go b/service/course/add.go
index dc42d5698abbb725b15e52d533ffd7ef9c719226..a94a59b6e56c8d465adf46559d3877dff50b34e0 100644
--- a/service/course/add.go
+++ b/service/course/add.go
@@ -132,7 +132,7 @@ func (c CourseServiceImpl) AddFaculty(payload faculty.AddFacultyRequestPayload)
 
 	// Unauthorized Role
 	if claim.Role != user.Admin {
-		return web.NewResponseErrorFromError(err, web.UnauthorizedAccess)
+		return web.NewResponseError("user is not allowed to access this resources", web.UnauthorizedAccess)
 	}
 
 	id, err := uuid.NewUUID()
diff --git a/service/di.go b/service/di.go
index 6cdb051667f746ccdc3ff0244fd29bdf19f69561..3a133f5fc0ccdaba5c7260c52fa6a068306cf8a2 100644
--- a/service/di.go
+++ b/service/di.go
@@ -5,13 +5,14 @@ import (
 	"gitlab.informatika.org/ocw/ocw-backend/service/admin"
 	"gitlab.informatika.org/ocw/ocw-backend/service/auth"
 	"gitlab.informatika.org/ocw/ocw-backend/service/common"
+	"gitlab.informatika.org/ocw/ocw-backend/service/course"
 	"gitlab.informatika.org/ocw/ocw-backend/service/logger"
 	"gitlab.informatika.org/ocw/ocw-backend/service/logger/hooks"
 	"gitlab.informatika.org/ocw/ocw-backend/service/material"
+	"gitlab.informatika.org/ocw/ocw-backend/service/quiz"
 	"gitlab.informatika.org/ocw/ocw-backend/service/reporter"
 	"gitlab.informatika.org/ocw/ocw-backend/service/reset"
 	"gitlab.informatika.org/ocw/ocw-backend/service/verification"
-	"gitlab.informatika.org/ocw/ocw-backend/service/course"
 )
 
 var ServiceTestSet = wire.NewSet(
@@ -64,6 +65,12 @@ var ServiceTestSet = wire.NewSet(
 		wire.Bind(new(material.MaterialContentService), new(*material.MaterialContentServiceImpl)),
 		wire.Bind(new(material.MaterialService), new(*material.MaterialServiceImpl)),
 	),
+
+	// Quiz service
+	wire.NewSet(
+		wire.Struct(new(quiz.QuizServiceImpl), "*"),
+		wire.Bind(new(quiz.QuizService), new(*quiz.QuizServiceImpl)),
+	),
 )
 
 var ServiceSet = wire.NewSet(
diff --git a/service/material/impl.go b/service/material/impl.go
index 5d59d826c1d80689e479b198b9b714ec94ef1bd2..c50b67586123fcf70acf3c2c0a4ee7f42b53034c 100644
--- a/service/material/impl.go
+++ b/service/material/impl.go
@@ -22,6 +22,16 @@ func (m MaterialServiceImpl) Get(courseId string) ([]materialDomain.Material, er
 	return materials, err
 }
 
+func (m MaterialServiceImpl) GetById(materialId uuid.UUID) (*materialDomain.Material, error) {
+	material, err := m.MaterialRepository.Get(materialId)
+
+	if errors.Is(err, gorm.ErrRecordNotFound) {
+		return nil, web.NewResponseErrorFromError(err, "ID_NOT_FOUND")
+	}
+
+	return material, err
+}
+
 func (m MaterialServiceImpl) Create(courseId string, email string, name string) (uuid.UUID, error) {
 	isSuccess := false
 	tx := m.TransactionBuilder.Build()
diff --git a/service/material/type.go b/service/material/type.go
index 1e129d48229c705cd961b4cb8d33c35459b414b9..ca617ce6fdce82217b439b2f4b1f0a0b58d3545d 100644
--- a/service/material/type.go
+++ b/service/material/type.go
@@ -9,6 +9,7 @@ type MaterialService interface {
 	Create(courseId string, email string, name string) (uuid.UUID, error)
 	Delete(materialId uuid.UUID, email string) error
 	Get(courseId string) ([]material.Material, error)
+	GetById(materialId uuid.UUID) (*material.Material, error)
 }
 
 type MaterialContentService interface {
diff --git a/service/quiz/impl.go b/service/quiz/impl.go
new file mode 100644
index 0000000000000000000000000000000000000000..3898cd448978c6604f71f68e7561077c9c396a00
--- /dev/null
+++ b/service/quiz/impl.go
@@ -0,0 +1,172 @@
+package quiz
+
+import (
+	"bytes"
+	"context"
+	"encoding/json"
+
+	"github.com/google/uuid"
+	"gitlab.informatika.org/ocw/ocw-backend/model/domain/quiz"
+	"gitlab.informatika.org/ocw/ocw-backend/model/web"
+	"gitlab.informatika.org/ocw/ocw-backend/provider/storage"
+	quizRepo "gitlab.informatika.org/ocw/ocw-backend/repository/quiz"
+)
+
+type QuizServiceImpl struct {
+	quizRepo.QuizRepository
+	storage.Storage
+}
+
+func (q QuizServiceImpl) ListAllQuiz(courseId string) ([]quiz.Quiz, error) {
+	return q.QuizRepository.GetQuizes(courseId)
+}
+
+func (q QuizServiceImpl) GetQuizDetail(quizId uuid.UUID) (*quiz.Quiz, error) {
+	return q.QuizRepository.GetQuizDetail(quizId)
+}
+
+func (q QuizServiceImpl) getQuizDetail(ctx context.Context, quizId uuid.UUID) (*quiz.QuizDetail, error) {
+	detail, err := q.QuizRepository.GetQuizDetail(quizId)
+
+	if err != nil {
+		return nil, err
+	}
+
+	payload, err := q.Storage.Get(ctx, detail.QuizPath)
+
+	if err != nil {
+		return nil, err
+	}
+
+	result := &quiz.QuizDetail{}
+
+	decoder := json.NewDecoder(bytes.NewReader(payload))
+	err = decoder.Decode(result)
+
+	return result, err
+}
+
+func (q QuizServiceImpl) DoTakeQuiz(ctx context.Context, quizId uuid.UUID, email string) (*quiz.QuizDetail, error) {
+	result, err := q.getQuizDetail(ctx, quizId)
+
+	if err != nil {
+		return nil, err
+	}
+
+	taken, err := q.IsActiveTake(quizId, email)
+
+	if err != nil {
+		return nil, err
+	}
+
+	if !taken {
+		_, err = q.NewTake(quizId, email)
+
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	for i := range result.Problems {
+		for j := range result.Problems[i].Answer {
+			result.Problems[i].Answer[j].IsSolution = nil
+		}
+	}
+
+	return result, nil
+}
+
+func (q QuizServiceImpl) GetSolutionQuiz(ctx context.Context, quizId uuid.UUID, email string) (*quiz.QuizDetail, error) {
+	result, err := q.getQuizDetail(ctx, quizId)
+
+	if err != nil {
+		return nil, err
+	}
+
+	_, err = q.GetLastTake(quizId, email)
+
+	if err != nil {
+		return nil, err
+	}
+
+	taken, err := q.IsActiveTake(quizId, email)
+
+	if err != nil {
+		return nil, err
+	}
+
+	if taken {
+		return nil, web.NewResponseError("user is not allow to access this data", "ERR_NOT_ALLOWED")
+	}
+
+	return result, nil
+}
+
+func (q QuizServiceImpl) checkAnswer(detail *quiz.QuizDetail, studentAnswer []quiz.Response) float64 {
+	answerDict := map[uuid.UUID][]uuid.UUID{}
+	totalProblem := len(detail.Problems)
+
+	for _, problem := range detail.Problems {
+		correctAnswerId := []uuid.UUID{}
+		for _, answer := range problem.Answer {
+			if *answer.IsSolution {
+				correctAnswerId = append(correctAnswerId, answer.Id)
+			}
+		}
+
+		answerDict[problem.Id] = correctAnswerId
+	}
+
+	correctAnswer := 0
+	for _, responseItem := range studentAnswer {
+		numCorrect := 0
+
+		for _, correctId := range answerDict[responseItem.ProblemId] {
+			if responseItem.AnswerId == correctId {
+				numCorrect++
+			}
+		}
+
+		if numCorrect == len(answerDict[responseItem.ProblemId]) {
+			correctAnswer++
+		}
+	}
+
+	return float64(correctAnswer) / float64(totalProblem) * 100
+}
+
+func (q QuizServiceImpl) DoFinishQuiz(ctx context.Context, quizId uuid.UUID, email string, studentAnswer []quiz.Response) (*quiz.QuizTake, error) {
+	taken, err := q.IsActiveTake(quizId, email)
+
+	if err != nil {
+		return nil, err
+	}
+
+	if !taken {
+		return nil, web.NewResponseError("user not yet do take the quiz", "NOT_TAKEN_QUIZ_YET")
+	}
+
+	result, err := q.getQuizDetail(ctx, quizId)
+
+	if err != nil {
+		return nil, err
+	}
+
+	score := q.checkAnswer(result, studentAnswer)
+
+	data, err := q.QuizRepository.GetLastTake(quizId, email)
+	data.IsFinished = true
+	data.Score = int(score)
+
+	if err != nil {
+		return nil, err
+	}
+
+	err = q.QuizRepository.UpdateScore(data.Id, int(score))
+
+	if err != nil {
+		return nil, err
+	}
+
+	return data, nil
+}
diff --git a/service/quiz/type.go b/service/quiz/type.go
new file mode 100644
index 0000000000000000000000000000000000000000..7c97e4965500fb25f75c19a4117c536559fcf9a8
--- /dev/null
+++ b/service/quiz/type.go
@@ -0,0 +1,17 @@
+package quiz
+
+import (
+	"context"
+
+	"github.com/google/uuid"
+	"gitlab.informatika.org/ocw/ocw-backend/model/domain/quiz"
+)
+
+type QuizService interface {
+	ListAllQuiz(courseId string) ([]quiz.Quiz, error)
+	GetQuizDetail(quizId uuid.UUID) (*quiz.Quiz, error)
+
+	DoTakeQuiz(ctx context.Context, quizId uuid.UUID, email string) (*quiz.QuizDetail, error)
+	DoFinishQuiz(ctx context.Context, quizId uuid.UUID, email string, studentAnswer []quiz.Response) (*quiz.QuizTake, error)
+	GetSolutionQuiz(ctx context.Context, quizId uuid.UUID, email string) (*quiz.QuizDetail, error)
+}