diff --git a/src/Controller/ApiLabelController.php b/src/Controller/ApiLabelController.php
index f68ae4b54825fc1abe6385b94390c42c250aed45..509e906f2421358e941cafdf16d7e0a39930832d 100644
--- a/src/Controller/ApiLabelController.php
+++ b/src/Controller/ApiLabelController.php
@@ -20,6 +20,7 @@ use Nelmio\ApiDocBundle\Annotation\Security;
 use OpenApi\Annotations as OA;
 use Nelmio\ApiDocBundle\Annotation\Model;
 use Symfony\Component\Serializer\SerializerInterface;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
 
 /**
  * @Route("/api/label")
@@ -349,4 +350,40 @@ class ApiLabelController extends ApiBaseController
 
         return $this->createJsonResponse(200, $selectedGraphies);
     }
+
+    /**
+     * Fusionne le premier label dans le second : on applique le second label à tous les mots-vedettes du premier et on supprime le premier label.
+     *
+     * @Route("/{deleted_label_id}/merge-into/{merged_label_id}", name="api_label_merge", methods={"PUT"})
+     * @ParamConverter("labelDeleted", options={"id" = "deleted_label_id"})
+     * @ParamConverter("labelMerged", options={"id" = "merged_label_id"})
+     *
+     * @OA\Parameter(name="deleted_label_id", in="path", description="id du label à fusionner dans un autre label (label supprimé)", @OA\Schema(type="integer"))
+     * @OA\Parameter(name="merged_label_id", in="path", description="id du label dans lequel sera fusionné l'autre label (label conservé)", @OA\Schema(type="integer"))
+     *
+     * @OA\Response(response=200, description="success", @OA\JsonContent(type="string"))
+     * @OA\Response(response=401, description="error", @OA\JsonContent(type="string"))
+     * @OA\Response(response=403, description="error", @OA\JsonContent(type="string"))
+     * @OA\Response(response=500, description="error", @OA\JsonContent(type="string"))
+     *
+     * @OA\Tag(name="Labels")
+     * @Security(name="OAuth2")
+     */
+    public function merge(Request $request, WiktionaryManager$wiktionaryManager, Label $labelDeleted, Label $labelMerged): Response
+    {
+        $diffs = $labelDeleted->compareWith($labelMerged);
+        if ($diffs) {
+            return $this->createJsonResponse(401, ['error' => sprintf("Fusion impossible : %s", implode(', ', $diffs))]);
+        } else {
+            foreach ($labelDeleted->getHeadwords() as $headword) {
+                $headword->addLabel($labelMerged);
+            }
+            $this->doctrine->getManager()->remove($labelDeleted);
+            $labelDeletedId = $labelDeleted->getId();
+            $this->doctrine->getManager()->flush();
+            $this->addSuccessMessage(sprintf("Fusion effectuée: Label %s appliqué aux mots du label %s. Label %s supprimé.", $labelDeletedId, $labelMerged->getId(), $labelDeletedId));
+        }
+
+        return $this->createJsonResponse();
+    }
 }
diff --git a/src/Entity/Label.php b/src/Entity/Label.php
index 60f32262d016c8e551fe56f3bea1cfe2d67e5c31..d05111f5b0df6c778e378338264c82be75fd56e3 100644
--- a/src/Entity/Label.php
+++ b/src/Entity/Label.php
@@ -98,6 +98,31 @@ class Label
         return $this->getName();
     }
 
+    public function compareWith(Label $label)
+    {
+        $diffs = [];
+        if ($this->getCategory() != $label->getCategory()) {
+            $diffs[] = "Catégories différentes";
+        }
+        if ($this->getName() != $label->getName()) {
+            $diffs[] = "Noms différents";
+        }
+        if ($this->getUser() !== $label->getUser()) {
+            $diffs[] = "User_id différents";
+        }
+        if ($this->getGroup() !== $label->getGroup()) {
+            $diffs[] = "Group_id différents";
+        }
+        if ($this->getGraphyList() !== $label->getGraphyList()) {
+            $diffs[] = "List_id différents";
+        }
+        if ($this->getMilestone() !== $label->getMilestone()) {
+            $diffs[] = "Échéances différentes";
+        }
+
+        return $diffs;
+    }
+
     public function isMorphological()
     {
         return $this->getCategory() === self::LABEL_CATEGORY_MORPHOLOGICAL;