diff --git a/src/www/ui/api/Controllers/UploadController.php b/src/www/ui/api/Controllers/UploadController.php
index 3b5f3f31d0..8a15476c3a 100644
--- a/src/www/ui/api/Controllers/UploadController.php
+++ b/src/www/ui/api/Controllers/UploadController.php
@@ -98,6 +98,11 @@ class UploadController extends RestController
    */
   const CONTAINER_PARAM = "containers";
 
+  /**
+   * Valid status inputs
+   */
+  const VALID_STATUS = ["open", "inprogress", "closed", "rejected"];
+
   public function __construct($container)
   {
     parent::__construct($container);
@@ -300,20 +305,7 @@ public function deleteUpload($request, $response, $args)
   }
 
   /**
-   * Copy a given upload to a new folder
-   *
-   * @param ServerRequestInterface $request
-   * @param ResponseInterface $response
-   * @param array $args
-   * @return ResponseInterface
-   */
-  public function copyUpload($request, $response, $args)
-  {
-    return $this->changeUpload($request, $response, $args, true);
-  }
-
-  /**
-   * Move a given upload to a new folder
+   * Move or copy a given upload to a new folder
    *
    * @param ServerRequestInterface $request
    * @param ResponseInterface $response
@@ -322,7 +314,13 @@ public function copyUpload($request, $response, $args)
    */
   public function moveUpload($request, $response, $args)
   {
-    return $this->changeUpload($request, $response, $args, false);
+    $action = $request->getHeaderLine('action');
+    if (strtolower($action) == "move") {
+      $copy = false;
+    } else {
+      $copy = true;
+    }
+    return $this->changeUpload($request, $response, $args, $copy);
   }
 
   /**
@@ -443,6 +441,86 @@ public function getUploadLicenses($request, $response, $args)
     return $response->withJson($licenseList, 200);
   }
 
+  /**
+   * Update an upload
+   *
+   * @param ServerRequestInterface $request
+   * @param ResponseInterface $response
+   * @param array $args
+   * @return ResponseInterface
+   */
+  public function updateUpload($request, $response, $args)
+  {
+    $id = intval($args['id']);
+    $query = $request->getQueryParams();
+    $userDao = $this->restHelper->getUserDao();
+    $userId = $this->restHelper->getUserId();
+    $groupId = $this->restHelper->getGroupId();
+
+    $perm = $userDao->isAdvisorOrAdmin($userId, $groupId);
+    if (!$perm) {
+      $error = new Info(403, "Not advisor or admin of current group. " .
+        "Can not update upload.", InfoType::ERROR);
+      return $response->withJson($error->getArray(), $error->getCode());
+    }
+    $uploadBrowseProxy = new UploadBrowseProxy(
+      $groupId,
+      $perm,
+      $this->dbHelper->getDbManager()
+    );
+
+    $assignee = null;
+    $status = null;
+    $comment = null;
+
+    $returnVal = true;
+    // Handle assignee info
+    if (array_key_exists(self::FILTER_ASSIGNEE, $query)) {
+      $assignee = filter_var($query[self::FILTER_ASSIGNEE], FILTER_VALIDATE_INT);
+      $userList = $userDao->getUserChoices($groupId);
+      if (!array_key_exists($assignee, $userList)) {
+        $returnVal = new Info(
+          404,
+          "New assignee does not have permisison on upload.",
+          InfoType::ERROR
+        );
+      } else {
+        $uploadBrowseProxy->updateTable("assignee", $id, $assignee);
+      }
+    }
+    // Handle new status
+    if (
+      array_key_exists(self::FILTER_STATUS, $query) &&
+      in_array(strtolower($query[self::FILTER_STATUS]), self::VALID_STATUS) &&
+      $returnVal === true
+    ) {
+      $newStatus = strtolower($query[self::FILTER_STATUS]);
+      $comment = '';
+      if (in_array($newStatus, ["closed", "rejected"])) {
+        $body = $request->getBody();
+        $comment = $body->getContents();
+        $body->close();
+      }
+      $status = 0;
+      if ($newStatus == self::VALID_STATUS[1]) {
+        $status = UploadStatus::IN_PROGRESS;
+      } elseif ($newStatus == self::VALID_STATUS[2]) {
+        $status = UploadStatus::CLOSED;
+      } elseif ($newStatus == self::VALID_STATUS[3]) {
+        $status = UploadStatus::REJECTED;
+      } else {
+        $status = UploadStatus::OPEN;
+      }
+      $uploadBrowseProxy->setStatusAndComment($id, $status, $comment);
+    }
+    if ($returnVal !== true) {
+      return $response->withJson($returnVal->getArray(), $returnVal->getCode());
+    }
+
+    $returnVal = new Info(202, "Upload updated successfully.", InfoType::INFO);
+    return $response->withJson($returnVal->getArray(), $returnVal->getCode());
+  }
+
   /**
    * Check if upload is accessible
    * @param integer $groupId Group ID
diff --git a/src/www/ui/api/documentation/openapi.yaml b/src/www/ui/api/documentation/openapi.yaml
index 49af0b98ed..8f7fea77db 100644
--- a/src/www/ui/api/documentation/openapi.yaml
+++ b/src/www/ui/api/documentation/openapi.yaml
@@ -15,7 +15,7 @@ openapi: 3.0.2
 info:
   title: FOSSology API
   description: Automate your fossology instance using REST API
-  version: 1.3.4
+  version: 1.4.0
   contact:
     email: fossology@fossology.org
   license:
@@ -221,21 +221,43 @@ paths:
         default:
           $ref: '#/components/responses/defaultResponse'
     patch:
-      operationId: moveUploadById
+      operationId: updateUploadById
       tags:
         - Upload
         - Organize
-      description: Move upload from one folder to other
+      description: Update an upload information
       parameters:
-        - name: folderId
-          description: Folder Id, where upload should be moved to
-          in: header
-          required: true
+        - name: status
+          description: New status of the upload
+          in: query
+          required: false
+          schema:
+            type: string
+            enum:
+              - Open
+              - InProgress
+              - Closed
+              - Rejected
+          example: Closed
+        - name: assignee
+          description: New assignee for the project
+          in: query
+          required: false
           schema:
             type: integer
+      requestBody:
+        description: >
+          Comment on the status, required for Closed and Rejected states.
+          Ignored for others.
+        content:
+          text/plain:
+            schema:
+              description: The comment for new status
+              type: string
+              example: "The upload cleared for use."
       responses:
         '202':
-          description: Upload will be moved
+          description: Upload will be updated
           content:
             application/json:
               schema:
@@ -243,11 +265,11 @@ paths:
         default:
           $ref: '#/components/responses/defaultResponse'
     put:
-      operationId: copyUploadById
+      operationId: moveUploadById
       tags:
         - Upload
         - Organize
-      description: Can be used to copy uploads
+      description: Copy or move an upload by id
       parameters:
         - name: folderId
           description: Folder Id, where upload should be copied to
@@ -255,9 +277,19 @@ paths:
           required: true
           schema:
             type: integer
+        - name: action
+          in: header
+          required: true
+          description: Action to be performed
+          schema:
+            type: string
+            enum:
+              - copy
+              - move
+      summary: Copy/Move an upload
       responses:
         '202':
-          description: Upload will be copied
+          description: Upload will be copied/moved
           content:
             application/json:
               schema:
diff --git a/src/www/ui/api/index.php b/src/www/ui/api/index.php
index c888ca9d5e..2b087de1e8 100644
--- a/src/www/ui/api/index.php
+++ b/src/www/ui/api/index.php
@@ -133,8 +133,8 @@
   function (){
     $this->get('[/{id:\\d+}]', UploadController::class . ':getUploads');
     $this->delete('/{id:\\d+}', UploadController::class . ':deleteUpload');
-    $this->patch('/{id:\\d+}', UploadController::class . ':moveUpload');
-    $this->put('/{id:\\d+}', UploadController::class . ':copyUpload');
+    $this->patch('/{id:\\d+}', UploadController::class . ':updateUpload');
+    $this->put('/{id:\\d+}', UploadController::class . ':moveUpload');
     $this->post('', UploadController::class . ':postUpload');
     $this->get('/{id:\\d+}/summary', UploadController::class . ':getUploadSummary');
     $this->get('/{id:\\d+}/licenses', UploadController::class . ':getUploadLicenses');
diff --git a/src/www/ui_tests/api/Controllers/UploadControllerTest.php b/src/www/ui_tests/api/Controllers/UploadControllerTest.php
index 952bb66fbf..fdbb156665 100644
--- a/src/www/ui_tests/api/Controllers/UploadControllerTest.php
+++ b/src/www/ui_tests/api/Controllers/UploadControllerTest.php
@@ -44,6 +44,7 @@
 use Slim\Http\Uri;
 use Fossology\Lib\Dao\FolderDao;
 use Fossology\Lib\Dao\AgentDao;
+use Fossology\Lib\Dao\UserDao;
 use Fossology\UI\Api\Models\Hash;
 
 function TryToDelete($uploadpk, $user_pk, $group_pk, $uploadDao)
@@ -141,6 +142,7 @@ protected function setUp()
     $this->uploadDao = M::mock(UploadDao::class);
     $this->folderDao = M::mock(FolderDao::class);
     $this->agentDao = M::mock(AgentDao::class);
+    $this->userDao = M::mock(UserDao::class);
 
     $this->dbManager->shouldReceive('getSingleRow')
       ->withArgs([M::any(), [$this->groupId, UploadStatus::OPEN,
@@ -154,6 +156,8 @@ protected function setUp()
       ->andReturn($this->uploadDao);
     $this->restHelper->shouldReceive('getFolderDao')
       ->andReturn($this->folderDao);
+    $this->restHelper->shouldReceive('getUserDao')
+      ->andReturn($this->userDao);
 
     $container->shouldReceive('get')->withArgs(array(
       'helper.restHelper'))->andReturn($this->restHelper);
@@ -486,7 +490,7 @@ public function testGetSingleUploadNotUnpacked()
 
   /**
    * @test
-   * -# Test for UploadController::copyUpload()
+   * -# Test for UploadController::moveUpload() for a copy action
    * -# Check if response status is 202
    */
   public function testCopyUpload()
@@ -503,10 +507,11 @@ public function testCopyUpload()
 
     $requestHeaders = new Headers();
     $requestHeaders->set('folderId', $folderId);
+    $requestHeaders->set('action', 'copy');
     $body = new Body(fopen('php://temp', 'r+'));
     $request = new Request("PUT", new Uri("HTTP", "localhost"),
       $requestHeaders, [], [], $body);
-    $actualResponse = $this->uploadController->copyUpload($request,
+    $actualResponse = $this->uploadController->moveUpload($request,
       new Response(), ['id' => $uploadId]);
     $this->assertEquals($expectedResponse->getStatusCode(),
       $actualResponse->getStatusCode());
@@ -530,6 +535,7 @@ public function testMoveUploadInvalidFolder()
 
     $requestHeaders = new Headers();
     $requestHeaders->set('folderId', 'alpha');
+    $requestHeaders->set('action', 'move');
     $body = new Body(fopen('php://temp', 'r+'));
     $request = new Request("PATCH", new Uri("HTTP", "localhost"),
       $requestHeaders, [], [], $body);
@@ -810,4 +816,80 @@ public function testGetUploadLicensesPendingScan()
     $this->assertEquals($expectedResponse->getHeaders(),
       $actualResponse->getHeaders());
   }
+
+  /**
+   * @test
+   * -# Test for UploadController::updateUpload()
+   * -# Check if response status is 202
+   */
+  public function testUpdateUpload()
+  {
+    $upload = 2;
+    $assignee = 4;
+    $status = UploadStatus::REJECTED;
+    $comment = "Not helpful";
+
+    $body = new Body(
+      fopen('data://text/plain;base64,' . base64_encode($comment), 'r+'));
+    $request = new Request("POST", new Uri("HTTP", "localhost", 80,
+      "/uploads/$upload", UploadController::FILTER_STATUS . "=Rejected&" .
+      UploadController::FILTER_ASSIGNEE . "=$assignee"),
+      new Headers(), [], [], $body);
+
+    $this->userDao->shouldReceive('isAdvisorOrAdmin')
+      ->withArgs([$this->userId, $this->groupId])
+      ->andReturn(true);
+    $this->userDao->shouldReceive('getUserChoices')
+      ->withArgs([$this->groupId])
+      ->andReturn([$this->userId => "fossy", $assignee => "friendly-fossy"]);
+    $this->dbManager->shouldReceive('getSingleRow')
+      ->withArgs([M::any(), [$assignee, $this->groupId, $upload], M::any()]);
+    $this->dbManager->shouldReceive('getSingleRow')
+      ->withArgs([M::any(), [$status, $comment, $this->groupId, $upload],
+        M::any()]);
+
+    $info = new Info(202, "Upload updated successfully.", InfoType::INFO);
+    $expectedResponse = (new Response())->withJson($info->getArray(),
+      $info->getCode());
+    $actualResponse = $this->uploadController->updateUpload($request,
+      new Response(), ['id' => $upload]);
+    $this->assertEquals($expectedResponse->getStatusCode(),
+      $actualResponse->getStatusCode());
+    $this->assertEquals($this->getResponseJson($expectedResponse),
+      $this->getResponseJson($actualResponse));
+  }
+
+  /**
+   * @test
+   * -# Test for UploadController::updateUpload() without permission
+   * -# Check if response status is 403
+   */
+  public function testUpdateUploadNoPerm()
+  {
+    $upload = 2;
+    $assignee = 4;
+    $comment = "Not helpful";
+
+    $body = new Body(
+      fopen('data://text/plain;base64,' . base64_encode($comment), 'r+'));
+    $request = new Request("POST", new Uri("HTTP", "localhost", 80,
+      "/uploads/$upload", UploadController::FILTER_STATUS . "=Rejected&" .
+      UploadController::FILTER_ASSIGNEE . "=$assignee"),
+      new Headers(), [], [], $body);
+
+    $this->userDao->shouldReceive('isAdvisorOrAdmin')
+      ->withArgs([$this->userId, $this->groupId])
+      ->andReturn(false);
+
+    $info = new Info(403, "Not advisor or admin of current group. " .
+      "Can not update upload.", InfoType::ERROR);
+    $expectedResponse = (new Response())->withJson($info->getArray(),
+      $info->getCode());
+    $actualResponse = $this->uploadController->updateUpload($request,
+      new Response(), ['id' => $upload]);
+    $this->assertEquals($expectedResponse->getStatusCode(),
+      $actualResponse->getStatusCode());
+    $this->assertEquals($this->getResponseJson($expectedResponse),
+      $this->getResponseJson($actualResponse));
+  }
 }