diff --git a/docs/docs.go b/docs/docs.go
index e996242b584017791f99aa699f8d1fda341115c3..74678c232c7ed71bfdf9f00f8a77e1fc1b14475c 100644
--- a/docs/docs.go
+++ b/docs/docs.go
@@ -2225,6 +2225,39 @@ const docTemplate = `{
                 }
             }
         },
+        "/quiz": {
+            "put": {
+                "description": "New Quiz",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "quiz"
+                ],
+                "summary": "New Quiz",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "format": "uuid",
+                        "description": "Quiz id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/web.BaseResponse"
+                        }
+                    }
+                }
+            }
+        },
         "/quiz/{id}": {
             "get": {
                 "description": "Get Quiz Detail",
@@ -2268,6 +2301,37 @@ const docTemplate = `{
                         }
                     }
                 }
+            },
+            "delete": {
+                "description": "Delete Quiz",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "quiz"
+                ],
+                "summary": "Delete Quiz",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "format": "uuid",
+                        "description": "Quiz id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/web.BaseResponse"
+                        }
+                    }
+                }
             }
         },
         "/quiz/{id}/finish": {
@@ -2613,7 +2677,6 @@ const docTemplate = `{
             "description": "Information that should be available when you add a course",
             "type": "object",
             "required": [
-                "abbreviation",
                 "email",
                 "id",
                 "name"
diff --git a/docs/swagger.json b/docs/swagger.json
index 59ed537c3af3c9c5b38d3f1c98a12421d3128acd..7d7802bc413765ed1066d2f8d08270a03e5b116a 100644
--- a/docs/swagger.json
+++ b/docs/swagger.json
@@ -2216,6 +2216,39 @@
                 }
             }
         },
+        "/quiz": {
+            "put": {
+                "description": "New Quiz",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "quiz"
+                ],
+                "summary": "New Quiz",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "format": "uuid",
+                        "description": "Quiz id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/web.BaseResponse"
+                        }
+                    }
+                }
+            }
+        },
         "/quiz/{id}": {
             "get": {
                 "description": "Get Quiz Detail",
@@ -2259,6 +2292,37 @@
                         }
                     }
                 }
+            },
+            "delete": {
+                "description": "Delete Quiz",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "quiz"
+                ],
+                "summary": "Delete Quiz",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "format": "uuid",
+                        "description": "Quiz id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/web.BaseResponse"
+                        }
+                    }
+                }
             }
         },
         "/quiz/{id}/finish": {
@@ -2604,7 +2668,6 @@
             "description": "Information that should be available when you add a course",
             "type": "object",
             "required": [
-                "abbreviation",
                 "email",
                 "id",
                 "name"
diff --git a/docs/swagger.yaml b/docs/swagger.yaml
index 7c62c7b01c3e2db940405858fe0ea0d9c9eb5ba2..b39336da946758345aadf855e855615703b341ab 100644
--- a/docs/swagger.yaml
+++ b/docs/swagger.yaml
@@ -85,7 +85,6 @@ definitions:
         description: Course Name
         type: string
     required:
-    - abbreviation
     - email
     - id
     - name
@@ -1986,7 +1985,50 @@ paths:
       summary: Delete Content
       tags:
       - content
+  /quiz:
+    put:
+      consumes:
+      - application/json
+      description: New Quiz
+      parameters:
+      - description: Quiz id
+        format: uuid
+        in: path
+        name: id
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            $ref: '#/definitions/web.BaseResponse'
+      summary: New Quiz
+      tags:
+      - quiz
   /quiz/{id}:
+    delete:
+      consumes:
+      - application/json
+      description: Delete Quiz
+      parameters:
+      - description: Quiz id
+        format: uuid
+        in: path
+        name: id
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            $ref: '#/definitions/web.BaseResponse'
+      summary: Delete Quiz
+      tags:
+      - quiz
     get:
       consumes:
       - application/json
diff --git a/handler/quiz/delete.go b/handler/quiz/delete.go
new file mode 100644
index 0000000000000000000000000000000000000000..642e1661fa478b38a3b591b74c616acd0e4d6b26
--- /dev/null
+++ b/handler/quiz/delete.go
@@ -0,0 +1,86 @@
+package quiz
+
+import (
+	"net/http"
+	"strings"
+
+	"github.com/go-chi/chi/v5"
+	"github.com/google/uuid"
+	"gitlab.informatika.org/ocw/ocw-backend/model/web/quiz"
+	"gitlab.informatika.org/ocw/ocw-backend/model/web"
+)
+
+// Index godoc
+//
+//	@Tags					quiz
+//	@Summary			Delete Quiz
+//	@Description	Delete Quiz
+//	@Produce			json
+//	@Accept				json
+//	@Param				id path string true "Quiz id" Format(uuid)
+//	@Success			200	{object}	web.BaseResponse
+//	@Router				/quiz/{id} [delete]
+func (m QuizHandlerImpl) DeleteQuiz(w http.ResponseWriter, r *http.Request) {
+	payload := quiz.DeleteRequestPayload{}
+	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
+	}
+
+	// Confirm Valid Website Token
+	validateTokenHeader := r.Header.Get("Authorization")
+
+	if validateTokenHeader == "" {
+		payload := m.WrapperUtil.ErrorResponseWrap("token is required", nil)
+		m.HttpUtil.WriteJson(w, http.StatusForbidden, payload)
+		return
+	}
+
+	token := strings.Split(validateTokenHeader, " ")
+
+	if len(token) != 2 {
+		payload := m.WrapperUtil.ErrorResponseWrap("invalid token", nil)
+		m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload)
+		return
+	}
+
+	if token[0] != "Bearer" {
+		payload := m.WrapperUtil.ErrorResponseWrap("invalid token", nil)
+		m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload)
+		return
+	}
+
+	payload.DeleteToken = token[1]
+	payload.ID = id
+
+	err = m.QuizService.DeleteQuiz(payload)
+
+	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(nil)
+	m.HttpUtil.WriteSuccessJson(w, responsePayload)
+
+}
\ No newline at end of file
diff --git a/handler/quiz/get.go b/handler/quiz/get.go
index 799ca822a991240a0c6febccb29640720f1b5e67..b88bde2955671872d7ad047c2be17541e450b491 100644
--- a/handler/quiz/get.go
+++ b/handler/quiz/get.go
@@ -2,10 +2,12 @@ package quiz
 
 import (
 	"net/http"
+	"strings"
 
 	"github.com/go-chi/chi/v5"
 	"github.com/google/uuid"
 	"gitlab.informatika.org/ocw/ocw-backend/model/web"
+	"gitlab.informatika.org/ocw/ocw-backend/model/web/quiz"
 )
 
 // Index godoc
@@ -55,3 +57,77 @@ func (m QuizHandlerImpl) GetQuizDetail(w http.ResponseWriter, r *http.Request) {
 	m.HttpUtil.WriteSuccessJson(w, responsePayload)
 
 }
+
+// Index godoc
+//
+//	@Tags					quiz
+//	@Summary			Get Quiz Link
+//	@Description	Get Quiz Link
+//	@Produce			json
+//	@Accept				json
+//	@Param				id path string true "Quiz id" Format(uuid)
+//	@Success			200	{object}	web.BaseResponse
+//	@Router				/quiz/link/{id} [get]
+
+func (m QuizHandlerImpl) GetQuizLink(w http.ResponseWriter, r *http.Request) {
+	payload := quiz.UpdateQuizRequestPayload{}
+	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
+	}
+	
+	// Confirm Valid Website Token
+	validateTokenHeader := r.Header.Get("Authorization")
+
+	if validateTokenHeader == "" {
+		payload := m.WrapperUtil.ErrorResponseWrap("token is required", nil)
+		m.HttpUtil.WriteJson(w, http.StatusForbidden, payload)
+		return
+	}
+
+	token := strings.Split(validateTokenHeader, " ")
+
+	if len(token) != 2 {
+		payload := m.WrapperUtil.ErrorResponseWrap("invalid token", nil)
+		m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload)
+		return
+	}
+
+	if token[0] != "Bearer" {
+		payload := m.WrapperUtil.ErrorResponseWrap("invalid token", nil)
+		m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload)
+		return
+	}
+
+	payload.UpdateQuizToken = token[1]
+	payload.ID = id
+	response, err := m.QuizService.GetQuiz(payload)
+
+	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(response)
+	m.HttpUtil.WriteSuccessJson(w, responsePayload)
+}
diff --git a/handler/quiz/new.go b/handler/quiz/new.go
new file mode 100644
index 0000000000000000000000000000000000000000..ca9ce5ed0b1d32b4d8cab8f568ec5a24221bb175
--- /dev/null
+++ b/handler/quiz/new.go
@@ -0,0 +1,94 @@
+package quiz
+
+import (
+	"net/http"
+	"strings"
+
+	"github.com/go-playground/validator/v10"
+	"gitlab.informatika.org/ocw/ocw-backend/model/web"
+	"gitlab.informatika.org/ocw/ocw-backend/model/web/quiz"
+)
+
+// Index godoc
+//
+//	@Tags					quiz
+//	@Summary			New Quiz
+//	@Description	New Quiz
+//	@Produce			json
+//	@Accept				json
+//	@Param				id path string true "Quiz id" Format(uuid)
+//	@Success			200	{object}	web.BaseResponse
+//	@Router				/quiz [put]
+func (m QuizHandlerImpl) NewQuiz(w http.ResponseWriter, r *http.Request) {
+	payload := quiz.AddQuizRequestPayload{}
+
+	// Validate payload
+	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
+	}
+
+	validate := validator.New()
+	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
+	}
+
+	// Confirm Valid Website Token
+	validateTokenHeader := r.Header.Get("Authorization")
+
+	if validateTokenHeader == "" {
+		payload := m.WrapperUtil.ErrorResponseWrap("token is required", nil)
+		m.HttpUtil.WriteJson(w, http.StatusForbidden, payload)
+		return
+	}
+
+	token := strings.Split(validateTokenHeader, " ")
+
+	if len(token) != 2 {
+		payload := m.WrapperUtil.ErrorResponseWrap("invalid token", nil)
+		m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload)
+		return
+	}
+
+	if token[0] != "Bearer" {
+		payload := m.WrapperUtil.ErrorResponseWrap("invalid token", nil)
+		m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload)
+		return
+	}
+
+	payload.AddQuizToken = token[1]
+
+	response, err := m.QuizService.NewQuiz(payload)
+
+	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(response)
+	m.HttpUtil.WriteSuccessJson(w, responsePayload)
+}
\ No newline at end of file
diff --git a/handler/quiz/type.go b/handler/quiz/type.go
index 8c3bf6f5567cf7f04be9e42b67558279a0f6159d..306cc813840a08bbbe4e7379ad436e268aba38cc 100644
--- a/handler/quiz/type.go
+++ b/handler/quiz/type.go
@@ -9,4 +9,7 @@ type QuizHandler interface {
 	TakeQuiz(w http.ResponseWriter, r *http.Request)
 	GetQuizSolution(w http.ResponseWriter, r *http.Request)
 	FinishQuiz(w http.ResponseWriter, r *http.Request)
+	NewQuiz(w http.ResponseWriter, r *http.Request)
+	GetQuizLink(w http.ResponseWriter, r *http.Request)
+	DeleteQuiz(w http.ResponseWriter, r *http.Request)
 }
diff --git a/model/web/course/request.go b/model/web/course/request.go
index 2604b105be490f5b0da70ce19369984e6bca1d61..20b446d8e06a7f4acb316d52e1c16d9e4b4f6607 100644
--- a/model/web/course/request.go
+++ b/model/web/course/request.go
@@ -27,7 +27,7 @@ type AddCourseRequestPayload struct {
 	Email string `json:"email" validate:"required,email" example:"someone@example.com"`
 
 	// Course Name Abbreviation
-	Abbreviation string `json:"abbreviation" validate:"required"`
+	Abbreviation string `json:"abbreviation"`
 }
 
 // DeleteCourse Request Payload
diff --git a/model/web/error_code.go b/model/web/error_code.go
index eb88b0837fa767fbd56e0aa114b4f85811bdfbb7..f150f3f1bf7d5211713144d3c5bfaaac3a11c618 100644
--- a/model/web/error_code.go
+++ b/model/web/error_code.go
@@ -3,18 +3,19 @@ package web
 const (
 	InvalidInput string = "INVALID_INPUT"
 
-	InvalidLogin           string = "INVALID_LOGIN"
-	UnauthorizedAccess     string = "UNAUTHORIZED"
-	InactiveUser           string = "INACTIVE_ACCOUNT"
-	EmailExist             string = "EMAIL_EXIST"
-	EmailNotExist          string = "EMAIL_NOT_EXIST"
-	LinkNotAvailable       string = "LINK_NOT_AVAILABLE"
-	FacultyNotExist        string = "FACULTY_NOT_EXIST"
-	MajorNotExist          string = "MAJOR_NOT_EXIST"
-	CourseNotExist         string = "COURSE_NOT_EXIST"
-	LessonNotExist         string = "LESSON_NOT_EXIST"
+	InvalidLogin       string = "INVALID_LOGIN"
+	UnauthorizedAccess string = "UNAUTHORIZED"
+	InactiveUser       string = "INACTIVE_ACCOUNT"
+	EmailExist         string = "EMAIL_EXIST"
+	EmailNotExist      string = "EMAIL_NOT_EXIST"
+	LinkNotAvailable   string = "LINK_NOT_AVAILABLE"
+	FacultyNotExist    string = "FACULTY_NOT_EXIST"
+	MajorNotExist      string = "MAJOR_NOT_EXIST"
+	CourseNotExist     string = "COURSE_NOT_EXIST"
+	LessonNotExist     string = "LESSON_NOT_EXIST"
 	LessonMaterialNotExist string = "LESSON_MATERIAL_NOT_EXIST"
-	IDExists               string = "ID_ALREADY_EXISTS"
+	IDExists           string = "ID_ALREADY_EXISTS"
+	NotExist           string = "NOT_EXIST"
 
 	TokenError string = "TOKEN_ERROR"
 )
diff --git a/model/web/quiz/request.go b/model/web/quiz/request.go
new file mode 100644
index 0000000000000000000000000000000000000000..7aa66274179f131c3094b5630c7a0c448e9913fe
--- /dev/null
+++ b/model/web/quiz/request.go
@@ -0,0 +1,57 @@
+package quiz
+
+import (
+	"github.com/google/uuid"
+)
+
+// AddQuiz Request Payload
+//
+//	@Description	Information that should be available when you add a quiz
+type AddQuizRequestPayload struct {
+	// Web Token that was appended to the link
+	AddQuizToken string
+
+	// Quiz Name
+	Name string `json:"name" validate:"required"`
+
+	// Course ID
+	CourseID string `json:"course_id" validate:"required"`
+}
+
+// UpdateQuiz Request Payload
+//
+//	@Description	Information that should be available when you update a quiz
+type UpdateQuizRequestPayload struct {
+	// Web Token that was appended to the link
+	UpdateQuizToken string
+
+	// Quiz ID, Set by param
+	ID uuid.UUID `json:"id"`
+}
+
+// DeleteQuiz Request Payload
+//
+//	@Description	Information that should be available when you delete using uuid
+type DeleteRequestPayload struct {
+	// Web Token that was appended to the link
+	DeleteToken string
+
+	// Quiz ID, Set by param
+	ID uuid.UUID
+}
+
+// GetUUID Request Payload
+//
+//	@Description	Information that should be available when you get using uuid
+type GetRequestPayload struct {
+	// Quiz/Problem/Answer ID, provided by query
+	ID uuid.UUID
+}
+
+// Link Response Payload
+//
+//	@Description	Information that you will get upon successful request
+type LinkResponse struct {
+	UploadLink string `json:"upload_link"`
+}
+
diff --git a/repository/quiz/impl.go b/repository/quiz/impl.go
index 72ff3800d9dba38ab9285c9bcea6c5f22ed25118..e43a00e03f3fe6348d69ed92a88cee1b7aad8706 100644
--- a/repository/quiz/impl.go
+++ b/repository/quiz/impl.go
@@ -5,6 +5,7 @@ import (
 	"time"
 
 	"github.com/google/uuid"
+	"gitlab.informatika.org/ocw/ocw-backend/model/domain/course"
 	"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"
@@ -65,6 +66,35 @@ func (q *QuizRepositoryImpl) NewTake(quizId uuid.UUID, userEmail string) (uuid.U
 	return id, err
 }
 
+func (q *QuizRepositoryImpl) IsUserContributor(id string, email string) (bool, error) {
+	err := q.db.Where("id = ? AND email = ?", id, email).Find(&course.Course{}).Error
+
+	if err != nil {
+		return false, err
+	}
+
+	return true, nil
+}
+
+func(q *QuizRepositoryImpl) NewQuiz(quiz quiz.Quiz) error {
+	return q.db.Create(&quiz).Error
+}
+
+func(q *QuizRepositoryImpl) GetQuizPath(quizId uuid.UUID) (string, error) {
+	result := quiz.Quiz{}
+	err := q.db.Where("id = ?", quizId).Find(&result).Error
+
+	if err != nil {
+		return "", err
+	}
+
+	return result.QuizPath, nil
+}
+
+func(q *QuizRepositoryImpl) Delete(quizId uuid.UUID) error {
+	return q.db.Delete(&quiz.Quiz{}, quizId).Error
+}
+
 func (q *QuizRepositoryImpl) IsActiveTake(quizId uuid.UUID, userEmail string) (bool, error) {
 	var result int64 = 0
 	err := q.db.
diff --git a/repository/quiz/type.go b/repository/quiz/type.go
index 1b4e0cbeca391ec136dfc8c696d1c058d49e55e4..c558fc0554b39c81c890d36150cd8d8ee80daf6a 100644
--- a/repository/quiz/type.go
+++ b/repository/quiz/type.go
@@ -10,6 +10,10 @@ type QuizRepository interface {
 	GetQuizDetail(quizId uuid.UUID) (*quiz.Quiz, error)
 	UpdateScore(takeId uuid.UUID, score int) error
 	NewTake(quizId uuid.UUID, userEmail string) (uuid.UUID, error)
+	IsUserContributor(id string, email string) (bool, error)
+	NewQuiz(quiz quiz.Quiz) error
+	GetQuizPath(quizId uuid.UUID) (string, error)
+	Delete(quizId 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/quiz/route.go b/routes/quiz/route.go
index b89269412c9add8ccc39a190a965f187a13c15b9..e706bb19fd348df8e3a0e8d62146eb457f09165a 100644
--- a/routes/quiz/route.go
+++ b/routes/quiz/route.go
@@ -14,7 +14,6 @@ type QuizRoutes struct {
 
 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,
@@ -36,4 +35,18 @@ func (q QuizRoutes) Register(r chi.Router) {
 		r.Use(guard)
 		r.Get("/", q.QuizHandler.GetQuizSolution)
 	})
+
+	r.Route("/quiz/{id}", func(r chi.Router) {
+		r.Get("/", q.QuizHandler.GetQuizDetail)
+		r.Route("/", func(r chi.Router) {
+			r.Use(guard)
+			r.Put("/", q.QuizHandler.NewQuiz)
+			r.Delete("/", q.QuizHandler.DeleteQuiz)
+		})
+	})
+
+	r.Route("/quiz/link/{id}", func(r chi.Router) {
+		r.Use(guard)
+		r.Get("/", q.QuizHandler.GetQuizLink)
+	})
 }
diff --git a/service/quiz/impl.go b/service/quiz/impl.go
index 3898cd448978c6604f71f68e7561077c9c396a00..e6ca76ee036d0601b2e771c3a42666d2e41c277f 100644
--- a/service/quiz/impl.go
+++ b/service/quiz/impl.go
@@ -4,19 +4,34 @@ import (
 	"bytes"
 	"context"
 	"encoding/json"
+	"errors"
+	"fmt"
+	"strings"
 
 	"github.com/google/uuid"
 	"gitlab.informatika.org/ocw/ocw-backend/model/domain/quiz"
+	"gitlab.informatika.org/ocw/ocw-backend/model/domain/user"
 	"gitlab.informatika.org/ocw/ocw-backend/model/web"
+	model "gitlab.informatika.org/ocw/ocw-backend/model/web/quiz"
+	atoken "gitlab.informatika.org/ocw/ocw-backend/model/web/auth/token"
 	"gitlab.informatika.org/ocw/ocw-backend/provider/storage"
 	quizRepo "gitlab.informatika.org/ocw/ocw-backend/repository/quiz"
+	"gitlab.informatika.org/ocw/ocw-backend/service/logger"
+	"gitlab.informatika.org/ocw/ocw-backend/utils/env"
+	"gitlab.informatika.org/ocw/ocw-backend/utils/token"
+	"gorm.io/gorm"
 )
 
 type QuizServiceImpl struct {
 	quizRepo.QuizRepository
 	storage.Storage
+	token.TokenUtil
+	logger.Logger
+	*env.Environment
 }
 
+
+// TODO: should be for admins, make ones for users which doesnt expose minio link
 func (q QuizServiceImpl) ListAllQuiz(courseId string) ([]quiz.Quiz, error) {
 	return q.QuizRepository.GetQuizes(courseId)
 }
@@ -170,3 +185,115 @@ func (q QuizServiceImpl) DoFinishQuiz(ctx context.Context, quizId uuid.UUID, ema
 
 	return data, nil
 }
+
+func (q QuizServiceImpl) isQuizContributor(courseId string, email string) error {
+	_, err := q.QuizRepository.IsUserContributor(courseId, email)
+
+	if err != nil {
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			return web.NewResponseError("course and user combination not found", "NOT_OWNER")
+		}
+
+		return err
+	}
+
+	return nil
+}
+
+func (q QuizServiceImpl) NewQuiz(payload model.AddQuizRequestPayload) (*model.LinkResponse, error) {
+	// Validate Role
+	claim, err := q.TokenUtil.Validate(payload.AddQuizToken, atoken.Access)
+
+	// Invalid Token
+	if err != nil {
+		return &model.LinkResponse{}, web.NewResponseErrorFromError(err, web.TokenError)
+	}
+
+	// Unauthorized Role
+	if claim.Role == user.Student {
+		return &model.LinkResponse{}, web.NewResponseErrorFromError(err, web.UnauthorizedAccess)
+	}
+	
+	// Validate Ownership
+	if err := q.isQuizContributor(payload.CourseID, claim.Email); err != nil {
+		return &model.LinkResponse{}, err
+	}
+
+	path := fmt.Sprintf("%s/%s.json", q.BucketQuizBasePath, strings.ReplaceAll(uuid.New().String(), "-", ""))
+	uploadLink, err := q.Storage.CreatePutSignedLink(context.Background(), path)
+
+	if err != nil {
+		q.Logger.Error("Some error happened when generate link")
+		q.Logger.Error(err.Error())
+		return &model.LinkResponse{}, err
+	}
+
+	return &model.LinkResponse{UploadLink: uploadLink}, nil
+}
+
+func (q QuizServiceImpl) GetQuiz(payload model.UpdateQuizRequestPayload) (*model.LinkResponse, error) {
+	// Validate Role
+	claim, err := q.TokenUtil.Validate(payload.UpdateQuizToken, atoken.Access)
+
+	// Invalid Token
+	if err != nil {
+		return &model.LinkResponse{}, web.NewResponseErrorFromError(err, web.TokenError)
+	}
+
+	// Unauthorized Role
+	if claim.Role == user.Student {
+		return &model.LinkResponse{}, web.NewResponseErrorFromError(err, web.UnauthorizedAccess)
+	}
+
+	// Get Quiz Detail
+	quiz, err := q.QuizRepository.GetQuizDetail(payload.ID)
+
+	if err != nil {
+		return &model.LinkResponse{}, err
+	}
+
+	// Validate Ownership
+	if err := q.isQuizContributor(quiz.CourseId, claim.Email); err != nil {
+		return &model.LinkResponse{}, err
+	}
+
+	uploadLink, err := q.QuizRepository.GetQuizPath(payload.ID)
+
+	if err != nil {
+		q.Logger.Error("Some error happened when retrieving link")
+		q.Logger.Error(err.Error())
+		return &model.LinkResponse{}, err
+	}
+
+	return &model.LinkResponse{UploadLink: uploadLink}, nil
+}
+
+func (q QuizServiceImpl) DeleteQuiz(payload model.DeleteRequestPayload) error {
+	// Validate Role
+	claim, err := q.TokenUtil.Validate(payload.DeleteToken, atoken.Access)
+
+	// Invalid Token
+	if err != nil {
+		return web.NewResponseErrorFromError(err, web.TokenError)
+	}
+
+	// Unauthorized Role
+	if claim.Role == user.Student {
+		return web.NewResponseErrorFromError(err, web.UnauthorizedAccess)
+	}
+
+	// Get Quiz Detail
+	quiz, err := q.QuizRepository.GetQuizDetail(payload.ID)
+
+	if err != nil {
+		return err
+	}
+
+	// Validate Ownership
+	if err := q.isQuizContributor(quiz.CourseId, claim.Email); err != nil {
+		return err
+	}
+
+	return q.QuizRepository.Delete(payload.ID)
+}
+
diff --git a/service/quiz/type.go b/service/quiz/type.go
index 7c97e4965500fb25f75c19a4117c536559fcf9a8..616546c025bfb4d3d1c65896678fc40efa359df0 100644
--- a/service/quiz/type.go
+++ b/service/quiz/type.go
@@ -5,6 +5,7 @@ import (
 
 	"github.com/google/uuid"
 	"gitlab.informatika.org/ocw/ocw-backend/model/domain/quiz"
+	model "gitlab.informatika.org/ocw/ocw-backend/model/web/quiz"
 )
 
 type QuizService interface {
@@ -14,4 +15,8 @@ type QuizService interface {
 	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)
+	isQuizContributor(courseId string, email string) error
+	NewQuiz(payload model.AddQuizRequestPayload) (*model.LinkResponse, error)
+	GetQuiz(payload model.UpdateQuizRequestPayload) (*model.LinkResponse, error)
+	DeleteQuiz(payload model.DeleteRequestPayload) error
 }
diff --git a/utils/env/env.go b/utils/env/env.go
index 85aa99986a2c0a45cb68f07927cbe954137a16e3..50c73820d065affd17ec2d93bf420f994281bdcd 100644
--- a/utils/env/env.go
+++ b/utils/env/env.go
@@ -65,6 +65,7 @@ type Environment struct {
 	BucketSignedGetDuration int64 `env:"BUCKET_SIGNED_GET_DURATION_S" envDefault:"1800"`
 
 	BucketMaterialBasePath string `env:"BUCKET_MATERIAL_BASE_PATH" envDefault:"materials"`
+	BucketQuizBasePath string `env:"BUCKET_MATERIAL_BASE_PATH" envDefault:"quiz"`
 
 	UseBucket bool `env:"USE_BUCKET" envDefault:"true"`
 }