diff --git a/src/main/java/coop/libriciel/ipcore/business/auth/DelegationBusinessService.java b/src/main/java/coop/libriciel/ipcore/business/auth/DelegationBusinessService.java
new file mode 100644
index 0000000000000000000000000000000000000000..7e4e02aaaba2d39ea8a3c4246961e8a7fca2730a
--- /dev/null
+++ b/src/main/java/coop/libriciel/ipcore/business/auth/DelegationBusinessService.java
@@ -0,0 +1,98 @@
+/*
+ * iparapheur Core
+ * Copyright (C) 2018-2025 Libriciel-SCOP
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package coop.libriciel.ipcore.business.auth;
+
+import coop.libriciel.ipcore.model.auth.Desk;
+import coop.libriciel.ipcore.model.database.Subtype;
+import coop.libriciel.ipcore.model.database.Tenant;
+import coop.libriciel.ipcore.model.database.Type;
+import coop.libriciel.ipcore.model.permission.DelegationDto;
+import coop.libriciel.ipcore.services.auth.AuthServiceInterface;
+import coop.libriciel.ipcore.services.database.SubtypeRepository;
+import coop.libriciel.ipcore.services.database.TypeRepository;
+import coop.libriciel.ipcore.utils.LocalizedStatusException;
+import lombok.extern.log4j.Log4j2;
+import org.apache.commons.lang3.StringUtils;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+
+import static org.springframework.http.HttpStatus.*;
+
+
+@Log4j2
+@Service
+public class DelegationBusinessService {
+
+
+    // <editor-fold desc="Beans">
+
+
+    private final AuthServiceInterface authService;
+    private final SubtypeRepository subtypeRepository;
+    private final TypeRepository typeRepository;
+
+
+    public DelegationBusinessService(AuthServiceInterface authService,
+                                     SubtypeRepository subtypeRepository,
+                                     TypeRepository typeRepository) {
+        this.authService = authService;
+        this.subtypeRepository = subtypeRepository;
+        this.typeRepository = typeRepository;
+    }
+
+
+    // </editor-fold desc="Beans">
+
+
+    public void checkDelegationIntegrity(@NotNull Tenant tenant, @NotNull Desk desk, DelegationDto delegationDto) {
+
+        if (StringUtils.equals(desk.getId(), delegationDto.getSubstituteDeskId())) {
+            throw new LocalizedStatusException(NOT_ACCEPTABLE, "message.redundant_delegation_target");
+        }
+
+        log.debug("tenant:{}, desk:{}", tenant.getId(), delegationDto.getSubstituteDeskId());
+
+        Optional.ofNullable(delegationDto.getSubstituteDeskId())
+                .map(id -> authService.findDeskByIdNoException(tenant.getId(), id))
+                .map(Desk::getId)
+                .orElseThrow(() -> new LocalizedStatusException(NOT_FOUND, "message.unknown_desk_id"));
+
+        if (StringUtils.isEmpty(delegationDto.getTypeId()) && StringUtils.isNotEmpty(delegationDto.getSubtypeId())) {
+            log.error("The type is mandatory on a subtype-restricted delegation");
+            throw new LocalizedStatusException(NOT_FOUND, "messages.unknown_subtype_id");
+        }
+
+        if (StringUtils.isNotEmpty(delegationDto.getTypeId()) && StringUtils.isEmpty(delegationDto.getSubtypeId())) {
+            typeRepository.findByIdAndTenant_Id(delegationDto.getTypeId(), tenant.getId())
+                    .map(Type::getId)
+                    .orElseThrow(() -> new LocalizedStatusException(NOT_FOUND, "messages.unknown_type_id"));
+        }
+
+        if (StringUtils.isNoneEmpty(delegationDto.getTypeId(), delegationDto.getSubtypeId())) {
+            subtypeRepository.findByIdAndParentType_IdAndTenant_Id(delegationDto.getSubtypeId(), delegationDto.getTypeId(), tenant.getId())
+                    .map(Subtype::getId)
+                    .orElseThrow(() -> new LocalizedStatusException(NOT_FOUND, "messages.unknown_subtype_id"));
+        }
+    }
+
+
+}
diff --git a/src/main/java/coop/libriciel/ipcore/business/auth/DeskBusinessService.java b/src/main/java/coop/libriciel/ipcore/business/auth/DeskBusinessService.java
index 46245f04545325d67c98cb83d43cf8f8d64b564d..5f5470cdb97deebff225e6ad5396c7e8a18b9098 100644
--- a/src/main/java/coop/libriciel/ipcore/business/auth/DeskBusinessService.java
+++ b/src/main/java/coop/libriciel/ipcore/business/auth/DeskBusinessService.java
@@ -146,19 +146,7 @@ public class DeskBusinessService {
 
         // Fetch real models
 
-        Set<String> deskIds = Stream
-                .concat(delegationDtoList.stream().map(DelegationDto::getDelegatingDeskId),
-                        delegationDtoList.stream().map(DelegationDto::getSubstituteDeskId))
-                .collect(toSet());
-
-        // TODO: this, with both delegation and delegating desks
-        //  updateInnerValues(
-        //      delegationDtoList.stream().collect(toMap(
-        //          delegationDto -> () -> singletonList(delegationDto.getDelegatingDeskId()),
-        //          delegationDto -> desks -> delegationDto.setDelegatingDesk(desks.stream().findFirst().orElse(null))
-        //      ))
-        //  );
-
+        Set<String> deskIds = delegationDtoList.stream().map(DelegationDto::getSubstituteDeskId).collect(toSet());
         Map<String, String> deskNames = authService.getDeskNames(deskIds);
 
         Set<String> typeIds = delegationDtoList.stream().map(DelegationDto::getTypeId).collect(toSet());
@@ -181,9 +169,6 @@ public class DeskBusinessService {
 
         delegationDtoList.forEach(delegationDto -> {
 
-            String delegatingDeskId = delegationDto.getDelegatingDeskId();
-            delegationDto.setDelegatingDesk(new DeskRepresentation(delegatingDeskId, deskNames.get(delegatingDeskId)));
-
             String substituteDeskId = delegationDto.getSubstituteDeskId();
             delegationDto.setSubstituteDesk(new DeskRepresentation(substituteDeskId, deskNames.get(substituteDeskId)));
 
diff --git a/src/main/java/coop/libriciel/ipcore/business/typology/TypologyBusinessService.java b/src/main/java/coop/libriciel/ipcore/business/typology/TypologyBusinessService.java
index 9da20b093d2ae57e308e67995d6495ddbc26daa4..5bf4d696f550b9efb054d91bb54824e1ad2b6cef 100644
--- a/src/main/java/coop/libriciel/ipcore/business/typology/TypologyBusinessService.java
+++ b/src/main/java/coop/libriciel/ipcore/business/typology/TypologyBusinessService.java
@@ -232,14 +232,18 @@ public class TypologyBusinessService {
                 tenantId,
                 delegationList.stream()
                         .collect(toMap(
-                                delegationDto -> delegationDto::getTypeId,
+                                delegationDto -> () -> Optional.ofNullable(delegationDto.getType())
+                                        .map(TypeRepresentation::getId)
+                                        .orElse(null),
                                 delegationDto -> type -> Optional.ofNullable(type)
                                         .map(t -> returnRedactedResponse(modelMapper, t, TypeRepresentation::new))
                                         .ifPresent(delegationDto::setType)
                         )),
                 delegationList.stream()
                         .collect(toMap(
-                                delegationDto -> delegationDto::getSubtypeId,
+                                delegationDto -> () -> Optional.ofNullable(delegationDto.getSubtype())
+                                        .map(SubtypeRepresentation::getId)
+                                        .orElse(null),
                                 delegationDto -> subtype -> Optional.ofNullable(subtype)
                                         .map(st -> returnRedactedResponse(modelMapper, st, SubtypeRepresentation::new))
                                         .ifPresent(delegationDto::setSubtype)
diff --git a/src/main/java/coop/libriciel/ipcore/configuration/ModelMapperConfigurer.java b/src/main/java/coop/libriciel/ipcore/configuration/ModelMapperConfigurer.java
index c80c2ef2ac7b70f0dc09c8bda7aaed080b788578..af4ed1384533d25f84728283ab68e0e61f43fc5b 100644
--- a/src/main/java/coop/libriciel/ipcore/configuration/ModelMapperConfigurer.java
+++ b/src/main/java/coop/libriciel/ipcore/configuration/ModelMapperConfigurer.java
@@ -21,16 +21,13 @@ package coop.libriciel.ipcore.configuration;
 
 import coop.libriciel.ipcore.model.auth.Desk;
 import coop.libriciel.ipcore.model.auth.DeskRepresentation;
-import coop.libriciel.ipcore.model.auth.User;
 import coop.libriciel.ipcore.model.auth.requests.DeskDto;
 import coop.libriciel.ipcore.model.auth.requests.UserRepresentation;
 import coop.libriciel.ipcore.model.crypto.CertificateInformations;
 import coop.libriciel.ipcore.model.crypto.SealCertificate;
 import coop.libriciel.ipcore.model.crypto.SealCertificateDto;
 import coop.libriciel.ipcore.model.database.*;
-import coop.libriciel.ipcore.model.database.requests.SubtypeDto;
-import coop.libriciel.ipcore.model.database.requests.SubtypeLayerDto;
-import coop.libriciel.ipcore.model.database.requests.SubtypeMetadataDto;
+import coop.libriciel.ipcore.model.database.requests.*;
 import coop.libriciel.ipcore.model.database.userPreferences.*;
 import coop.libriciel.ipcore.model.pdfstamp.Layer;
 import coop.libriciel.ipcore.model.permission.Delegation;
@@ -135,23 +132,45 @@ public class ModelMapperConfigurer {
                     .map(TypologyEntity::getId)
                     .orElse(null);
 
+    public static final Converter<TypeRepresentation, String> TYPE_REPRESENTATION_TO_ID_CONVERTER =
+            context -> Optional.ofNullable(context.getSource())
+                    .map(TypeRepresentation::getId)
+                    .orElse(null);
+
     public static final Converter<String, Type> ID_TO_TYPE_CONVERTER =
             context -> Optional.ofNullable(context.getSource())
                     .filter(StringUtils::isNotEmpty)
                     .map(i -> Type.builder().id(i).build())
                     .orElse(null);
 
+    public static final Converter<String, TypeRepresentation> ID_TO_TYPE_REPRESENTATION_CONVERTER =
+            context -> Optional.ofNullable(context.getSource())
+                    .filter(StringUtils::isNotEmpty)
+                    .map(i -> new TypeRepresentation(i, null, null))
+                    .orElse(null);
+
     public static final Converter<Subtype, String> SUBTYPE_TO_ID_CONVERTER =
             context -> Optional.ofNullable(context.getSource())
                     .map(TypologyEntity::getId)
                     .orElse(null);
 
+    public static final Converter<SubtypeRepresentation, String> SUBTYPE_REPRESENTATION_TO_ID_CONVERTER =
+            context -> Optional.ofNullable(context.getSource())
+                    .map(SubtypeRepresentation::getId)
+                    .orElse(null);
+
     public static final Converter<String, Subtype> ID_TO_SUBTYPE_CONVERTER =
             context -> Optional.ofNullable(context.getSource())
                     .filter(StringUtils::isNotEmpty)
                     .map(i -> Subtype.builder().id(i).build())
                     .orElse(null);
 
+    public static final Converter<String, SubtypeRepresentation> ID_TO_SUBTYPE_REPRESENTATION_CONVERTER =
+            context -> Optional.ofNullable(context.getSource())
+                    .filter(StringUtils::isNotEmpty)
+                    .map(i -> new SubtypeRepresentation(i, null))
+                    .orElse(null);
+
     private static final Converter<Layer, String> LAYER_TO_ID_CONVERTER =
             context -> Optional.ofNullable(context.getSource())
                     .map(Layer::getId)
@@ -174,22 +193,11 @@ public class ModelMapperConfigurer {
                     .map(i -> Metadata.builder().id(i).build())
                     .orElse(null);
 
-    public static final Converter<User, String> USER_TO_ID_CONVERTER =
-            context -> Optional.ofNullable(context.getSource())
-                    .map(User::getId)
-                    .orElse(null);
-
     public static final Converter<UserRepresentation, String> USER_REP_TO_ID_CONVERTER =
             context -> Optional.ofNullable(context.getSource())
                     .map(UserRepresentation::getId)
                     .orElse(null);
 
-    public static final Converter<String, User> ID_TO_USER_CONVERTER =
-            context -> Optional.ofNullable(context.getSource())
-                    .filter(StringUtils::isNotEmpty)
-                    .map(User::new)
-                    .orElse(null);
-
     public static final Converter<String, UserRepresentation> ID_TO_USER_REP_CONVERTER =
             context -> Optional.ofNullable(context.getSource())
                     .filter(StringUtils::isNotEmpty)
@@ -443,24 +451,20 @@ public class ModelMapperConfigurer {
     private void setupDelegationDto(@NotNull ModelMapper modelMapper) {
 
         modelMapper.typeMap(Delegation.class, DelegationDto.class)
-                .addMappings(m -> m.using(DESK_REPRESENTATION_TO_ID_CONVERTER)
-                        .map(Delegation::getDelegatingDesk, DelegationDto::setDelegatingDeskId))
-                .addMappings(m -> m.using(DESK_REPRESENTATION_TO_ID_CONVERTER)
-                        .map(Delegation::getSubstituteDesk, DelegationDto::setSubstituteDeskId))
-                .addMappings(m -> m.using(TYPE_TO_ID_CONVERTER)
-                        .map(Delegation::getType, DelegationDto::setTypeId))
-                .addMappings(m -> m.using(SUBTYPE_TO_ID_CONVERTER)
-                        .map(Delegation::getSubtype, DelegationDto::setSubtypeId));
+                .addMappings(m -> m.using(ID_TO_DESK_REPRESENTATION_CONVERTER)
+                        .map(Delegation::getSubstituteDeskId, DelegationDto::setSubstituteDesk))
+                .addMappings(m -> m.using(ID_TO_TYPE_REPRESENTATION_CONVERTER)
+                        .map(Delegation::getTypeId, DelegationDto::setType))
+                .addMappings(m -> m.using(ID_TO_SUBTYPE_REPRESENTATION_CONVERTER)
+                        .map(Delegation::getSubtypeId, DelegationDto::setSubtype));
 
         modelMapper.emptyTypeMap(DelegationDto.class, Delegation.class)
-                .addMappings(m -> m.using(ID_TO_DESK_REPRESENTATION_CONVERTER)
-                        .map(DelegationDto::getDelegatingDeskId, Delegation::setDelegatingDesk))
-                .addMappings(m -> m.using(ID_TO_DESK_REPRESENTATION_CONVERTER)
-                        .map(DelegationDto::getSubstituteDeskId, Delegation::setSubstituteDesk))
-                .addMappings(m -> m.using(ID_TO_TYPE_CONVERTER)
-                        .map(DelegationDto::getTypeId, Delegation::setType))
-                .addMappings(m -> m.using(ID_TO_SUBTYPE_CONVERTER)
-                        .map(DelegationDto::getSubtypeId, Delegation::setSubtype))
+                .addMappings(m -> m.using(DESK_REPRESENTATION_TO_ID_CONVERTER)
+                        .map(DelegationDto::getSubstituteDesk, Delegation::setSubstituteDeskId))
+                .addMappings(m -> m.using(TYPE_REPRESENTATION_TO_ID_CONVERTER)
+                        .map(DelegationDto::getType, Delegation::setTypeId))
+                .addMappings(m -> m.using(SUBTYPE_REPRESENTATION_TO_ID_CONVERTER)
+                        .map(DelegationDto::getSubtype, Delegation::setSubtypeId))
                 .implicitMappings();
     }
 
diff --git a/src/main/java/coop/libriciel/ipcore/controller/DeskController.java b/src/main/java/coop/libriciel/ipcore/controller/DeskController.java
index ae7ea9d961e38235e4243f919755d96020e07d49..3a837ff1d2b7fdc43faa6109eb5bf0890c52388a 100644
--- a/src/main/java/coop/libriciel/ipcore/controller/DeskController.java
+++ b/src/main/java/coop/libriciel/ipcore/controller/DeskController.java
@@ -19,6 +19,7 @@
 
 package coop.libriciel.ipcore.controller;
 
+import coop.libriciel.ipcore.business.auth.DelegationBusinessService;
 import coop.libriciel.ipcore.business.auth.DeskBusinessService;
 import coop.libriciel.ipcore.model.auth.Desk;
 import coop.libriciel.ipcore.model.auth.DeskRepresentation;
@@ -104,6 +105,7 @@ public class DeskController {
 
 
     private final AuthServiceInterface authService;
+    private final DelegationBusinessService delegationBusinessService;
     private final DeskBusinessService deskBusinessService;
     private final MetadataRepository metadataRepository;
     private final ModelMapper modelMapper;
@@ -114,6 +116,7 @@ public class DeskController {
 
     @Autowired
     public DeskController(AuthServiceInterface authService,
+                          DelegationBusinessService delegationBusinessService,
                           DeskBusinessService deskBusinessService,
                           MetadataRepository metadataRepository,
                           ModelMapper modelMapper,
@@ -121,6 +124,7 @@ public class DeskController {
                           StatsServiceInterface statsService,
                           WorkflowServiceInterface workflowService) {
         this.authService = authService;
+        this.delegationBusinessService = delegationBusinessService;
         this.deskBusinessService = deskBusinessService;
         this.metadataRepository = metadataRepository;
         this.modelMapper = modelMapper;
@@ -370,17 +374,19 @@ public class DeskController {
                                           @TenantResolved Tenant tenant,
                                           @Parameter(description = DeskRepresentation.API_DOC_ID_VALUE)
                                           @PathVariable(name = DeskRepresentation.API_PATH) String deskId,
+                                          @DeskResolved Desk desk,
                                           @Parameter(description = DelegationDto.API_DOC, required = true)
                                           @RequestBody DelegationDto delegationDto) {
 
         log.info("createDelegation delegating:{} substitute:{}", deskId, delegationDto.getSubstituteDeskId());
         log.info("                 type:{} subtype:{}", delegationDto.getTypeId(), delegationDto.getSubtypeId());
 
+        // Integrity check
+
         // TODO: Check if the current user is the desk owner or a delegationManager
+        delegationBusinessService.checkDelegationIntegrity(tenant, desk, delegationDto);
 
-        if (StringUtils.equals(deskId, delegationDto.getSubstituteDeskId())) {
-            throw new LocalizedStatusException(NOT_ACCEPTABLE, "message.redundant_delegation_target");
-        }
+        // Actual register
 
         permissionService.addDelegation(
                 tenant.getId(),
@@ -397,6 +403,54 @@ public class DeskController {
     }
 
 
+    @PutMapping(API_INTERNAL + "/admin/tenant/{tenantId}/desk/{deskId}/delegation/{delegationId}")
+    @Operation(summary = "Update a delegation from target Desk")
+    @PreAuthorize(PREAUTHORIZE_TENANT_MEMBER_OR_MORE)
+    @ApiResponses(value = {
+            @ApiResponse(responseCode = CODE_200),
+            @ApiResponse(responseCode = CODE_401, content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
+            @ApiResponse(responseCode = CODE_403, content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
+            @ApiResponse(responseCode = CODE_404, content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
+    })
+    public DelegationDto updateDelegation(@Parameter(description = Tenant.API_DOC_ID_VALUE)
+                                          @PathVariable(name = Tenant.API_PATH) String tenantId,
+                                          @TenantResolved Tenant tenant,
+                                          @Parameter(description = DeskRepresentation.API_DOC_ID_VALUE)
+                                          @PathVariable(name = DeskRepresentation.API_PATH) String deskId,
+                                          @DeskResolved Desk desk,
+                                          @Parameter(description = DelegationDto.API_DOC_ID_VALUE)
+                                          @PathVariable(name = DelegationDto.API_PATH) String delegationId,
+                                          @DelegationResolved Delegation existingDelegation,
+                                          @Parameter(description = DelegationDto.API_DOC, required = true)
+                                          @RequestBody DelegationDto updatedDelegation) {
+
+        log.info("updateDelegationAsAdmin - delegating desk : {} ", deskId);
+        log.trace("updateDelegationAsAdmin - existingDelegation:{} delegationUpdate:{}", existingDelegation, updatedDelegation);
+
+        // Integrity check
+
+        // TODO: Check if the current user is the desk owner or a delegationManager
+        delegationBusinessService.checkDelegationIntegrity(tenant, desk, updatedDelegation);
+
+        // Actual update
+
+        permissionService.deleteDelegation(tenant.getId(), existingDelegation);
+
+        permissionService.addDelegation(
+                tenant.getId(),
+                updatedDelegation.getSubstituteDeskId(),
+                deskId,
+                updatedDelegation.getTypeId(),
+                updatedDelegation.getSubtypeId(),
+                updatedDelegation.getStart(),
+                updatedDelegation.getEnd()
+        );
+
+        statsService.registerAdminAction(tenant, DESK, UPDATE, deskId);
+        return updatedDelegation;
+    }
+
+
     @DeleteMapping(API_INTERNAL + "/tenant/{tenantId}/desk/{deskId}/delegation/{delegationId}")
     @Operation(summary = "Remove an active or planned delegation from target Desk")
     @PreAuthorize(PREAUTHORIZE_TENANT_MEMBER_OR_MORE)
@@ -420,17 +474,7 @@ public class DeskController {
 
         // TODO: Check if the current user is the desk owner or a delegationManager
 
-        permissionService.deleteDelegation(
-                delegationId,
-                tenant.getId(),
-                delegation.getSubstituteDesk().getId(),
-                deskId,
-                Optional.ofNullable(delegation.getType()).map(Type::getId).orElse(null),
-                Optional.ofNullable(delegation.getSubtype()).map(Subtype::getId).orElse(null),
-                delegation.getStart(),
-                delegation.getEnd()
-        );
-
+        permissionService.deleteDelegation(tenant.getId(), delegation);
         statsService.registerAdminAction(tenant, DESK, UPDATE, deskId);
     }
 
@@ -460,7 +504,6 @@ public class DeskController {
                 .map(delegation -> modelMapper.map(delegation, DelegationDto.class))
                 .collect(toMutableList());
 
-        delegationsDtoList.forEach(dto -> dto.setDelegatingDeskId(deskId));
         deskBusinessService.updateInnerDeskValues(tenantId, delegationsDtoList);
 
         // TODO: Check if the current user is the desk owner or a delegationManager
diff --git a/src/main/java/coop/libriciel/ipcore/controller/admin/AdminDeskController.java b/src/main/java/coop/libriciel/ipcore/controller/admin/AdminDeskController.java
index 48429976f3f7b970829113ba84ab06bfda1afe45..bc80d147d4621d296b95b197b1a6f022f9188b4d 100644
--- a/src/main/java/coop/libriciel/ipcore/controller/admin/AdminDeskController.java
+++ b/src/main/java/coop/libriciel/ipcore/controller/admin/AdminDeskController.java
@@ -19,6 +19,7 @@
 
 package coop.libriciel.ipcore.controller.admin;
 
+import coop.libriciel.ipcore.business.auth.DelegationBusinessService;
 import coop.libriciel.ipcore.business.auth.DeskBusinessService;
 import coop.libriciel.ipcore.business.auth.UserBusinessService;
 import coop.libriciel.ipcore.business.database.MetadataBusinessService;
@@ -27,13 +28,10 @@ import coop.libriciel.ipcore.model.auth.Desk;
 import coop.libriciel.ipcore.model.auth.DeskRepresentation;
 import coop.libriciel.ipcore.model.auth.HierarchisedDeskRepresentation;
 import coop.libriciel.ipcore.model.auth.requests.DeskDto;
-import coop.libriciel.ipcore.model.database.Subtype;
 import coop.libriciel.ipcore.model.database.Tenant;
-import coop.libriciel.ipcore.model.database.Type;
 import coop.libriciel.ipcore.model.permission.Delegation;
 import coop.libriciel.ipcore.model.permission.DelegationDto;
 import coop.libriciel.ipcore.model.permission.DelegationSortBy;
-import coop.libriciel.ipcore.model.permission.DelegationUpdateDto;
 import coop.libriciel.ipcore.model.workflow.Folder;
 import coop.libriciel.ipcore.model.workflow.WorkflowDefinition;
 import coop.libriciel.ipcore.services.auth.AuthServiceInterface;
@@ -70,7 +68,6 @@ import org.springframework.web.bind.annotation.*;
 
 import java.util.Comparator;
 import java.util.List;
-import java.util.Optional;
 
 import static coop.libriciel.ipcore.IpCoreApplication.*;
 import static coop.libriciel.ipcore.business.database.MetadataBusinessService.METADATA_REPRESENTATION_NAME_COMPARATOR;
@@ -100,6 +97,7 @@ public class AdminDeskController {
 
 
     private final AuthServiceInterface authService;
+    private final DelegationBusinessService delegationBusinessService;
     private final DeskBusinessService deskBusinessService;
     private final MetadataBusinessService metadataBusinessService;
     private final ModelMapper modelMapper;
@@ -112,6 +110,7 @@ public class AdminDeskController {
 
     @Autowired
     public AdminDeskController(AuthServiceInterface authService,
+                               DelegationBusinessService delegationBusinessService,
                                DeskBusinessService deskBusinessService,
                                MetadataBusinessService metadataBusinessService,
                                ModelMapper modelMapper,
@@ -121,6 +120,7 @@ public class AdminDeskController {
                                UserBusinessService userBusinessService,
                                WorkflowServiceInterface workflowService) {
         this.authService = authService;
+        this.delegationBusinessService = delegationBusinessService;
         this.deskBusinessService = deskBusinessService;
         this.metadataBusinessService = metadataBusinessService;
         this.modelMapper = modelMapper;
@@ -354,7 +354,7 @@ public class AdminDeskController {
 
         if (!workflowsUsingDeskId.isEmpty()) {
             Long workflowsNb = workflowsUsingDeskId.getTotalElements();
-            log.error("deleteDesk error: the desk is used by " + workflowsNb + " workflow(s)");
+            log.error("deleteDesk error: the desk is used by {} workflow(s)", workflowsNb);
             throw new LocalizedStatusException(NOT_ACCEPTABLE,
                     "message.cannot_delete_a_desk_used_by_workflows",
                     workflowsNb,
@@ -487,25 +487,33 @@ public class AdminDeskController {
                                                  @PathVariable(name = Tenant.API_PATH) String tenantId,
                                                  @TenantResolved Tenant tenant,
                                                  @Parameter(description = "Delegating desk Id")
-                                                 @PathVariable String deskId,
+                                                 @PathVariable(name = DeskRepresentation.API_PATH) String deskId,
+                                                 @DeskResolved Desk desk,
                                                  @Parameter(description = DelegationDto.API_DOC, required = true)
                                                  @RequestBody DelegationDto delegationDto) {
 
         log.info("createDelegationAsAdmin delegating:{} substitute:{}", deskId, delegationDto.getSubstituteDeskId());
         log.info("                        type:{} subtype:{}", delegationDto.getTypeId(), delegationDto.getSubtypeId());
 
+        // Integrity check
+
         if (!permissionService.currentUserHasAdminRightOnDesk(tenant.getId(), deskId)) {
             log.debug("User is not super admin, tenant admin nor functional admin for this desk, abort");
             throw new LocalizedStatusException(FORBIDDEN, "message.you_can_t_administrate_this_desk");
         }
 
-        if (StringUtils.equals(deskId, delegationDto.getSubstituteDeskId())) {
-            throw new LocalizedStatusException(NOT_ACCEPTABLE, "message.redundant_delegation_target");
-        }
+        delegationBusinessService.checkDelegationIntegrity(tenant, desk, delegationDto);
+
+        // Actual save
 
         permissionService.addDelegation(
-                tenant.getId(), delegationDto.getSubstituteDeskId(), deskId,
-                delegationDto.getTypeId(), delegationDto.getSubtypeId(), delegationDto.getStart(), delegationDto.getEnd()
+                tenant.getId(),
+                delegationDto.getSubstituteDeskId(),
+                deskId,
+                delegationDto.getTypeId(),
+                delegationDto.getSubtypeId(),
+                delegationDto.getStart(),
+                delegationDto.getEnd()
         );
 
         statsService.registerAdminAction(tenant, DESK, UPDATE, deskId);
@@ -526,7 +534,8 @@ public class AdminDeskController {
                                               @PathVariable(name = Tenant.API_PATH) String tenantId,
                                               @TenantResolved Tenant tenant,
                                               @Parameter(description = "Delegating desk Id")
-                                              @PathVariable String deskId,
+                                              @PathVariable(name = DeskRepresentation.API_PATH) String deskId,
+                                              @DeskResolved Desk desk,
                                               @Parameter(description = DelegationDto.API_DOC_ID_VALUE)
                                               @PathVariable String delegationId,
                                               @DelegationResolved Delegation delegation) {
@@ -541,7 +550,6 @@ public class AdminDeskController {
         // Fetch missing values
 
         DelegationDto delegationDto = modelMapper.map(delegation, DelegationDto.class);
-        delegationDto.setDelegatingDeskId(deskId);
         deskBusinessService.updateInnerDeskValues(tenantId, singletonList(delegationDto));
         typologyBusinessService.updateInnerValues(tenantId, singletonList(delegationDto));
 
@@ -550,6 +558,58 @@ public class AdminDeskController {
     }
 
 
+    @PutMapping(API_PROVISIONING_V1 + "/admin/tenant/{tenantId}/desk/{deskId}/delegation/{delegationId}")
+    @Operation(summary = "Update a delegation from target Desk")
+    @PreAuthorize(PREAUTHORIZE_TENANT_FUNCTIONAL_ADMIN_OR_MORE)
+    @ApiResponses(value = {
+            @ApiResponse(responseCode = CODE_200),
+            @ApiResponse(responseCode = CODE_401, content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
+            @ApiResponse(responseCode = CODE_403, content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
+            @ApiResponse(responseCode = CODE_404, content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
+    })
+    public DelegationDto updateDelegationAsAdmin(@Parameter(description = Tenant.API_DOC_ID_VALUE)
+                                                 @PathVariable(name = Tenant.API_PATH) String tenantId,
+                                                 @TenantResolved Tenant tenant,
+                                                 @Parameter(description = DeskRepresentation.API_DOC_ID_VALUE)
+                                                 @PathVariable(name = DeskRepresentation.API_PATH) String deskId,
+                                                 @DeskResolved Desk desk,
+                                                 @Parameter(description = DelegationDto.API_DOC_ID_VALUE)
+                                                 @PathVariable(name = DelegationDto.API_PATH) String delegationId,
+                                                 @DelegationResolved Delegation existingDelegation,
+                                                 @Parameter(description = DelegationDto.API_DOC, required = true)
+                                                 @RequestBody DelegationDto updatedDelegation) {
+
+        log.info("updateDelegationAsAdmin - delegating desk : {} ", deskId);
+        log.trace("updateDelegationAsAdmin - existingDelegation:{} delegationUpdate:{}", existingDelegation, updatedDelegation);
+
+        // Integrity check
+
+        if (!permissionService.currentUserHasAdminRightOnDesk(tenant.getId(), deskId)) {
+            log.error("User is not super admin, tenant admin nor functional admin for this desk, abort");
+            throw new LocalizedStatusException(FORBIDDEN, "message.you_can_t_administrate_this_desk");
+        }
+
+        delegationBusinessService.checkDelegationIntegrity(tenant, desk, updatedDelegation);
+
+        // Actual update
+
+        permissionService.deleteDelegation(tenant.getId(), existingDelegation);
+
+        permissionService.addDelegation(
+                tenant.getId(),
+                updatedDelegation.getSubstituteDeskId(),
+                deskId,
+                updatedDelegation.getTypeId(),
+                updatedDelegation.getSubtypeId(),
+                updatedDelegation.getStart(),
+                updatedDelegation.getEnd()
+        );
+
+        statsService.registerAdminAction(tenant, DESK, UPDATE, deskId);
+        return updatedDelegation;
+    }
+
+
     @DeleteMapping(API_PROVISIONING_V1 + "/admin/tenant/{tenantId}/desk/{deskId}/delegation/{delegationId}")
     @Operation(summary = "Remove an active or planned delegation from target Desk")
     @PreAuthorize(PREAUTHORIZE_TENANT_FUNCTIONAL_ADMIN_OR_MORE)
@@ -576,26 +636,13 @@ public class AdminDeskController {
             throw new LocalizedStatusException(FORBIDDEN, "message.you_can_t_administrate_this_desk");
         }
 
-        permissionService.deleteDelegation(
-                delegationId,
-                tenant.getId(),
-                delegation.getSubstituteDesk().getId(),
-                deskId,
-                Optional.ofNullable(delegation.getType()).map(Type::getId).orElse(null),
-                Optional.ofNullable(delegation.getSubtype()).map(Subtype::getId).orElse(null),
-                delegation.getStart(),
-                delegation.getEnd()
-        );
-
+        permissionService.deleteDelegation(tenant.getId(), delegation);
         statsService.registerAdminAction(tenant, DESK, UPDATE, deskId);
     }
 
 
     @GetMapping(API_PROVISIONING_V1 + "/admin/tenant/{tenantId}/desk/{deskId}/delegation")
-    @Operation(
-            summary = "List delegations (active and planned) for given delegating desk",
-            description = DelegationSortBy.Constants.API_DOC_SORT_BY_VALUES
-    )
+    @Operation(summary = "List delegations (active and planned) for given delegating desk", description = DelegationSortBy.Constants.API_DOC_SORT_BY_VALUES)
     @PreAuthorize(PREAUTHORIZE_TENANT_FUNCTIONAL_ADMIN_OR_MORE)
     @ApiResponses(value = {
             @ApiResponse(responseCode = CODE_200),
@@ -622,11 +669,10 @@ public class AdminDeskController {
 
         List<DelegationDto> delegationsDtoList = permissionService.getDelegations(tenant.getId(), desk.getId()).stream()
                 .map(delegation -> modelMapper.map(delegation, DelegationDto.class))
-                .collect(toMutableList());;
+                .collect(toMutableList());
 
         // Fetch missing values
 
-        delegationsDtoList.forEach(dto -> dto.setDelegatingDeskId(desk.getId()));
         deskBusinessService.updateInnerDeskValues(tenant.getId(), delegationsDtoList);
         typologyBusinessService.updateInnerValues(tenant.getId(), delegationsDtoList);
 
diff --git a/src/main/java/coop/libriciel/ipcore/model/permission/Delegation.java b/src/main/java/coop/libriciel/ipcore/model/permission/Delegation.java
index d34631c8608d65f7863c643c66b4dc76aa738369..0661fe84192d582b8a0e067a658ae4e71e772798 100644
--- a/src/main/java/coop/libriciel/ipcore/model/permission/Delegation.java
+++ b/src/main/java/coop/libriciel/ipcore/model/permission/Delegation.java
@@ -19,9 +19,6 @@
 
 package coop.libriciel.ipcore.model.permission;
 
-import coop.libriciel.ipcore.model.auth.DeskRepresentation;
-import coop.libriciel.ipcore.model.database.Subtype;
-import coop.libriciel.ipcore.model.database.Type;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
@@ -42,11 +39,11 @@ public class Delegation {
 
     private @Id String id;
 
-    private @NotNull DeskRepresentation substituteDesk;
-    private @NotNull DeskRepresentation delegatingDesk;
+    private @NotNull String substituteDeskId;
+    private @NotNull String delegatingDeskId;
 
-    private @Nullable Type type;
-    private @Nullable Subtype subtype;
+    private @Nullable String typeId;
+    private @Nullable String subtypeId;
 
     private @Nullable Date start;
     private @Nullable Date end;
diff --git a/src/main/java/coop/libriciel/ipcore/model/permission/DelegationDto.java b/src/main/java/coop/libriciel/ipcore/model/permission/DelegationDto.java
index 87131901933dc8b80310e4b98ed004f13671573d..1c01f776ade9a691f4d01d9aeef890b5f4961d4d 100644
--- a/src/main/java/coop/libriciel/ipcore/model/permission/DelegationDto.java
+++ b/src/main/java/coop/libriciel/ipcore/model/permission/DelegationDto.java
@@ -58,14 +58,6 @@ public class DelegationDto {
     @Schema(accessMode = AccessMode.READ_ONLY)
     private DeskRepresentation substituteDesk;
 
-    @JsonProperty(access = Access.WRITE_ONLY)
-    @Schema(accessMode = AccessMode.WRITE_ONLY)
-    private String delegatingDeskId;
-
-    @JsonProperty(access = Access.READ_ONLY)
-    @Schema(accessMode = AccessMode.READ_ONLY)
-    private DeskRepresentation delegatingDesk;
-
     @JsonProperty(access = Access.WRITE_ONLY)
     @Schema(accessMode = AccessMode.WRITE_ONLY, nullable = true)
     private String typeId;
diff --git a/src/main/java/coop/libriciel/ipcore/model/permission/DelegationSortBy.java b/src/main/java/coop/libriciel/ipcore/model/permission/DelegationSortBy.java
index e6f989a8552c6d8522b2f0efa89caca79736025d..ea87ffdbccdbd8f08b89c4d868cf210ae385710f 100644
--- a/src/main/java/coop/libriciel/ipcore/model/permission/DelegationSortBy.java
+++ b/src/main/java/coop/libriciel/ipcore/model/permission/DelegationSortBy.java
@@ -60,7 +60,7 @@ public enum DelegationSortBy {
     public static Comparator<DelegationDto> generateDelegationDtoComparator(@NotNull DelegationSortBy sortBy, boolean asc) {
         return switch (sortBy) {
             case SUBSTITUTE_DESK -> comparing(
-                    (Function<DelegationDto, String>) dto -> Optional.ofNullable(dto.getDelegatingDesk()).map(DeskRepresentation::getName).orElse(null),
+                    (Function<DelegationDto, String>) dto -> Optional.ofNullable(dto.getSubstituteDesk()).map(DeskRepresentation::getName).orElse(null),
                     asc ? nullsLast(naturalOrder()) : nullsFirst(reverseOrder())
             );
             case START -> comparing(DelegationDto::getStart, asc ? nullsLast(naturalOrder()) : nullsFirst(reverseOrder()));
diff --git a/src/main/java/coop/libriciel/ipcore/model/permission/DelegationUpdateDto.java b/src/main/java/coop/libriciel/ipcore/model/permission/DelegationUpdateDto.java
deleted file mode 100644
index 485e40f7db863870add985f091da4ddc350a6aef..0000000000000000000000000000000000000000
--- a/src/main/java/coop/libriciel/ipcore/model/permission/DelegationUpdateDto.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * iparapheur Core
- * Copyright (C) 2018-2025 Libriciel-SCOP
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, version 3.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package coop.libriciel.ipcore.model.permission;
-
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-
-@Data
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-public class DelegationUpdateDto {
-
-    DelegationDto previousDelegationData;
-    DelegationDto updatedDelegationData;
-}
diff --git a/src/main/java/coop/libriciel/ipcore/services/auth/KeycloakService.java b/src/main/java/coop/libriciel/ipcore/services/auth/KeycloakService.java
index 97bf94019f29a302953bddb6c3fa78359fb8c1d3..f940ea2a8c559105c5ad375b3ad28357a754d805 100644
--- a/src/main/java/coop/libriciel/ipcore/services/auth/KeycloakService.java
+++ b/src/main/java/coop/libriciel/ipcore/services/auth/KeycloakService.java
@@ -1430,9 +1430,7 @@ public class KeycloakService implements AuthServiceInterface {
         RoleRepresentation role;
 
         try {
-            role = realmResource
-                    .rolesById()
-                    .getRole(id);
+            role = realmResource.rolesById().getRole(id);
         } catch (Exception e) {
             return null;
         }
diff --git a/src/main/java/coop/libriciel/ipcore/services/permission/KeycloakResourceService.java b/src/main/java/coop/libriciel/ipcore/services/permission/KeycloakResourceService.java
index bc9c959330c3e388a4e88a31beae8518ab510ea2..c72b90556f92d0e890887da783958249b6bb8411 100644
--- a/src/main/java/coop/libriciel/ipcore/services/permission/KeycloakResourceService.java
+++ b/src/main/java/coop/libriciel/ipcore/services/permission/KeycloakResourceService.java
@@ -1776,56 +1776,55 @@ public class KeycloakResourceService implements PermissionServiceInterface {
                 .filter(p -> StringUtils.equals(p.getId(), id))
                 .map(this::toDelegationList)
                 .flatMap(Collection::stream)
+                .peek(delegation -> delegation.setDelegatingDeskId(deskId))
                 .findFirst()
                 .orElse(null);
     }
 
 
     @Override
-    public void deleteDelegation(@NotNull String delegationId,
-                                 @NotNull String tenantId,
-                                 @Nullable String substituteDeskId,
-                                 @NotNull String delegatingDeskId,
-                                 @Nullable String typeId,
-                                 @Nullable String subtypeId,
-                                 @Nullable Date beginDate,
-                                 @Nullable Date endDate) {
-
-        log.debug("deleteDelegation id:{}", delegationId);
-
-        ScopePermissionResource permissionResource = scopePermissionsClient.findById(delegationId);
+    public void deleteDelegation(@NotNull String tenantId,
+                                 @NotNull Delegation delegation) {
+
+        log.debug("deleteDelegation id:{}", delegation.getId());
+
+        ScopePermissionResource permissionResource = scopePermissionsClient.findById(delegation.getId());
         ScopePermissionRepresentation permissionRepresentation = permissionResource.toRepresentation();
         Set<String> associatedPoliciesNames = permissionResource.associatedPolicies().stream()
                 .map(AbstractPolicyRepresentation::getName)
                 .collect(toSet());
 
-        if (substituteDeskId == null) {
+        if (delegation.getSubstituteDeskId() == null) {
             // probably a delegation to a deleted desk, we just double-check that only target calendar policies remain
             if (associatedPoliciesNames.stream().map(DELEGATION_CALENDAR_POLICY_INTERNAL_NAME_PATTERN::matcher).allMatch(Matcher::matches)) {
-                associatedPoliciesNames.forEach(aggregateTimePolicyInternalName -> {
-                    deleteSpecificTimePolicyAndOwnerFromAggregate(aggregateTimePolicyInternalName, beginDate, endDate, "NULL", associatedPoliciesNames);
-                });
+                associatedPoliciesNames.forEach(aggregateTimePolicyInternalName -> deleteSpecificTimePolicyAndOwnerFromAggregate(
+                        aggregateTimePolicyInternalName,
+                        delegation.getStart(),
+                        delegation.getEnd(),
+                        "NULL",
+                        associatedPoliciesNames
+                ));
             }
         } else {
-            boolean isTimedDelegation = ObjectUtils.anyNotNull(beginDate, endDate);
+            boolean isTimedDelegation = ObjectUtils.anyNotNull(delegation.getStart(), delegation.getEnd());
 
-            Map<String, String> substituteOwnerPolicySubstitutions = Map.of(TENANT_PLACEHOLDER, tenantId, DESK_PLACEHOLDER, substituteDeskId);
+            Map<String, String> substituteOwnerPolicySubstitutions = Map.of(TENANT_PLACEHOLDER, tenantId, DESK_PLACEHOLDER, delegation.getSubstituteDeskId());
             String substituteOwnerPolicyName = StringSubstitutor.replace(POLICY_DESK_OWNER_INTERNAL_NAME, substituteOwnerPolicySubstitutions);
 
             if (isTimedDelegation) {
 
                 Map<String, String> substitutions = Map.of(
                         TENANT_PLACEHOLDER, tenantId,
-                        DESK_PLACEHOLDER, delegatingDeskId,
-                        TARGET_DESK_PLACEHOLDER, substituteDeskId,
-                        TARGET_TYPE_PLACEHOLDER, firstNonNull(typeId, EMPTY),
-                        TARGET_SUBTYPE_PLACEHOLDER, firstNonNull(subtypeId, EMPTY)
+                        DESK_PLACEHOLDER, delegation.getDelegatingDeskId(),
+                        TARGET_DESK_PLACEHOLDER, delegation.getSubstituteDeskId(),
+                        TARGET_TYPE_PLACEHOLDER, firstNonNull(delegation.getTypeId(), EMPTY),
+                        TARGET_SUBTYPE_PLACEHOLDER, firstNonNull(delegation.getSubtypeId(), EMPTY)
                 );
 
                 String aggregateTimePolicyInternalNameTemplate;
-                if (subtypeId != null) {
+                if (delegation.getSubtypeId() != null) {
                     aggregateTimePolicyInternalNameTemplate = POLICY_DESK_TARGET_SUBTYPE_CALENDAR_INTERNAL_NAME;
-                } else if (typeId != null) {
+                } else if (delegation.getTypeId() != null) {
                     aggregateTimePolicyInternalNameTemplate = POLICY_DESK_TARGET_TYPE_CALENDAR_INTERNAL_NAME;
                 } else {
                     aggregateTimePolicyInternalNameTemplate = POLICY_DESK_TARGET_CALENDAR_INTERNAL_NAME;
@@ -1838,8 +1837,8 @@ public class KeycloakResourceService implements PermissionServiceInterface {
                 }
 
                 deleteSpecificTimePolicyAndOwnerFromAggregate(aggregateTimePolicyInternalName,
-                        beginDate,
-                        endDate,
+                        delegation.getStart(),
+                        delegation.getEnd(),
                         substituteOwnerPolicyName,
                         associatedPoliciesNames);
             } else {
@@ -2011,9 +2010,9 @@ public class KeycloakResourceService implements PermissionServiceInterface {
             return singletonList(
                     Delegation.builder()
                             .id(policyRepresentation.getId())
-                            .substituteDesk(new DeskRepresentation(substituteDeskId))
-                            .type(new Type(typeScope.getRight()))
-                            .subtype(new Subtype(subtypeScope.getRight()))
+                            .substituteDeskId(substituteDeskId)
+                            .typeId(typeScope.getRight())
+                            .subtypeId(subtypeScope.getRight())
                             .build()
             );
         } else {
@@ -2021,11 +2020,11 @@ public class KeycloakResourceService implements PermissionServiceInterface {
                     .stream()
                     .map(i -> Delegation.builder()
                             .id(policyRepresentation.getId())
-                            .substituteDesk(new DeskRepresentation(substituteDeskId))
+                            .substituteDeskId(substituteDeskId)
                             .start(i.getLeft())
                             .end(i.getRight())
-                            .type(Optional.ofNullable(typeScope.getRight()).map(Type::new).orElse(null))
-                            .subtype(Optional.ofNullable(subtypeScope.getRight()).map(Subtype::new).orElse(null))
+                            .typeId(typeScope.getRight())
+                            .subtypeId(subtypeScope.getRight())
                             .build())
                     .toList();
         }
diff --git a/src/main/java/coop/libriciel/ipcore/services/permission/NonePermissionService.java b/src/main/java/coop/libriciel/ipcore/services/permission/NonePermissionService.java
index 31c3d8bd3990dbd943660e973d5f9e92fb6fdf14..167f39cd1623fa2c66cc7ea0b8d61126b5735cf5 100644
--- a/src/main/java/coop/libriciel/ipcore/services/permission/NonePermissionService.java
+++ b/src/main/java/coop/libriciel/ipcore/services/permission/NonePermissionService.java
@@ -211,14 +211,8 @@ public class NonePermissionService implements PermissionServiceInterface {
 
 
     @Override
-    public void deleteDelegation(@NotNull String delegationId,
-                                 @NotNull String tenantId,
-                                 @NotNull String substituteDeskId,
-                                 @NotNull String delegatingDeskId,
-                                 @Nullable String typeId,
-                                 @Nullable String subtypeId,
-                                 @Nullable Date beginDate,
-                                 @Nullable Date endDate) {
+    public void deleteDelegation(@NotNull String tenantId,
+                                 @NotNull Delegation delegation) {
         throw new NoPermissionServiceError();
     }
 
diff --git a/src/main/java/coop/libriciel/ipcore/services/permission/PermissionServiceInterface.java b/src/main/java/coop/libriciel/ipcore/services/permission/PermissionServiceInterface.java
index 455b52b0cb999dc48d3f3871656660eb74fc365a..fa94dee30ede95e7f16c7f3d56bb23a1347d5808 100644
--- a/src/main/java/coop/libriciel/ipcore/services/permission/PermissionServiceInterface.java
+++ b/src/main/java/coop/libriciel/ipcore/services/permission/PermissionServiceInterface.java
@@ -134,28 +134,35 @@ public interface PermissionServiceInterface {
                                                          @Nullable String typeId,
                                                          @Nullable String subtypeId);
 
+
     boolean currentUserHasViewingRightOnSomeDeskIn(@NotNull String tenantId,
                                                    @NotNull Set<String> deskIds,
                                                    @Nullable String typeId,
                                                    @Nullable String subtypeId);
 
+
     boolean currentUserHasArchivingRightOnSomeDeskIn(@NotNull String tenantId,
                                                      @NotNull Set<String> deskIds,
                                                      @Nullable String typeId,
                                                      @Nullable String subtypeId);
 
+
     boolean currentUserHasChainingRightOnSomeDeskIn(@NotNull String tenantId,
                                                     @NotNull Set<String> deskIds,
                                                     @Nullable String typeId,
                                                     @Nullable String subtypeId);
 
+
     boolean currentUserHasFolderCreationRightOnSomeDeskIn(@NotNull String tenantId,
                                                           @NotNull Set<String> deskIds);
 
+
     boolean currentUserHasAdminRightOnDesk(@NotNull String tenantId, @NotNull String deskIds);
 
+
     boolean currentUserHasAdminRightsOnTenant(@NotNull String tenantId);
 
+
     boolean currentUserHasFolderActionRightOnSomeDeskIn(@NotNull String tenantId,
                                                         @NotNull Set<String> deskIds,
                                                         @Nullable String typeId,
@@ -206,8 +213,10 @@ public interface PermissionServiceInterface {
     
     @NotNull Set<String> getAllDesksHavingDelegationFrom(@NotNull String tenantId, @NotNull Set<String> deskIds);
 
+
     @NotNull List<DeskRepresentation> getAllDesksHavingDelegationTo(@NotNull Set<String> deskIds);
 
+
     void createSubtypeResource(@NotNull String tenantId, @NotNull String subtypeId);
 
 
@@ -224,14 +233,8 @@ public interface PermissionServiceInterface {
     @Nullable Delegation getDelegation(@NotNull String deskId, @NotNull String id);
 
 
-    void deleteDelegation(@NotNull String delegationId,
-                          @NotNull String tenantId,
-                          @NotNull String substituteDeskId,
-                          @NotNull String delegatingDeskId,
-                          @Nullable String typeId,
-                          @Nullable String subtypeId,
-                          @Nullable Date beginDate,
-                          @Nullable Date endDate);
+    void deleteDelegation(@NotNull String tenantId,
+                          @NotNull Delegation delegation);
 
 
     @NotNull List<Delegation> getDelegations(@NotNull String tenantId, @NotNull String delegatingDeskId);
diff --git a/src/main/java/coop/libriciel/ipcore/services/resolvers/DeskResolver.java b/src/main/java/coop/libriciel/ipcore/services/resolvers/DeskResolver.java
index 6ba7d9bc7aabbf3ec0c4cde38202ff1e0daa8740..8f4c6a022320398e775579f2a5e7e02c2b220744 100644
--- a/src/main/java/coop/libriciel/ipcore/services/resolvers/DeskResolver.java
+++ b/src/main/java/coop/libriciel/ipcore/services/resolvers/DeskResolver.java
@@ -102,7 +102,7 @@ public class DeskResolver implements HandlerMethodArgumentResolver {
             throw new LocalizedStatusException(NOT_FOUND, "message.unknown_desk_id");
         }
 
-        return Optional.ofNullable(authService.findDeskById(tenantId, deskId))
+        return Optional.ofNullable(authService.findDeskByIdNoException(tenantId, deskId))
                 .orElseThrow(() -> new LocalizedStatusException(NOT_FOUND, "message.unknown_desk_id"));
     }
 
diff --git a/src/test/java/coop/libriciel/ipcore/controller/admin/AdminDeskControllerTest.java b/src/test/java/coop/libriciel/ipcore/controller/admin/AdminDeskControllerTest.java
index 8fd94aa7200010404218e063c19318e5ce55df8c..f711d7a277a371303c843b98b962555e8a7861aa 100644
--- a/src/test/java/coop/libriciel/ipcore/controller/admin/AdminDeskControllerTest.java
+++ b/src/test/java/coop/libriciel/ipcore/controller/admin/AdminDeskControllerTest.java
@@ -19,6 +19,7 @@
 
 package coop.libriciel.ipcore.controller.admin;
 
+import coop.libriciel.ipcore.business.auth.DelegationBusinessService;
 import coop.libriciel.ipcore.business.auth.DeskBusinessService;
 import coop.libriciel.ipcore.business.auth.UserBusinessService;
 import coop.libriciel.ipcore.business.database.MetadataBusinessService;
@@ -76,6 +77,7 @@ class AdminDeskControllerTest {
 
         adminDeskController = new AdminDeskController(
                 authServiceMock,
+                mock(DelegationBusinessService.class),
                 mock(DeskBusinessService.class),
                 mock(MetadataBusinessService.class),
                 mock(ModelMapper.class),
diff --git a/src/test/java/coop/libriciel/ipcore/model/permission/DelegationDtoTest.java b/src/test/java/coop/libriciel/ipcore/model/permission/DelegationDtoTest.java
index 9a69096e164869d152332ff87a2e362f385aa7b3..d3a64bb7336ef6ef08dec227e9cc03802d191c4c 100644
--- a/src/test/java/coop/libriciel/ipcore/model/permission/DelegationDtoTest.java
+++ b/src/test/java/coop/libriciel/ipcore/model/permission/DelegationDtoTest.java
@@ -20,8 +20,8 @@
 package coop.libriciel.ipcore.model.permission;
 
 import coop.libriciel.ipcore.model.auth.DeskRepresentation;
-import coop.libriciel.ipcore.model.database.Subtype;
-import coop.libriciel.ipcore.model.database.Type;
+import coop.libriciel.ipcore.model.database.requests.SubtypeRepresentation;
+import coop.libriciel.ipcore.model.database.requests.TypeRepresentation;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 import org.modelmapper.ModelMapper;
@@ -47,11 +47,9 @@ class DelegationDtoTest {
     private void assertDeepEquals(Delegation entity, DelegationDto dto) {
         assertEquals(entity.getId(), dto.getId());
 
-        assertEquals(Optional.ofNullable(entity.getSubstituteDesk()).map(DeskRepresentation::getId).orElse(null), dto.getSubstituteDeskId());
-        assertEquals(Optional.ofNullable(entity.getDelegatingDesk()).map(DeskRepresentation::getId).orElse(null), dto.getDelegatingDeskId());
-
-        assertEquals(Optional.ofNullable(entity.getType()).map(Type::getId).orElse(null), dto.getTypeId());
-        assertEquals(Optional.ofNullable(entity.getSubtype()).map(Subtype::getId).orElse(null), dto.getSubtypeId());
+        assertEquals(Optional.ofNullable(dto.getSubstituteDesk()).map(DeskRepresentation::getId).orElse(null), entity.getSubstituteDeskId());
+        assertEquals(Optional.ofNullable(dto.getType()).map(TypeRepresentation::getId).orElse(null), entity.getTypeId());
+        assertEquals(Optional.ofNullable(dto.getSubtype()).map(SubtypeRepresentation::getId).orElse(null), entity.getSubtypeId());
 
         assertEquals(entity.getStart(), dto.getStart());
         assertEquals(entity.getEnd(), dto.getEnd());
@@ -67,10 +65,10 @@ class DelegationDtoTest {
 
             Delegation entity = new Delegation();
             entity.setId(UUID.randomUUID().toString());
-            entity.setSubstituteDesk(new DeskRepresentation(UUID.randomUUID().toString(), null));
-            entity.setDelegatingDesk(new DeskRepresentation(UUID.randomUUID().toString(), null));
-            entity.setType(new Type(UUID.randomUUID().toString()));
-            entity.setSubtype(new Subtype(UUID.randomUUID().toString()));
+            entity.setSubstituteDeskId(UUID.randomUUID().toString());
+            entity.setDelegatingDeskId(UUID.randomUUID().toString());
+            entity.setTypeId(UUID.randomUUID().toString());
+            entity.setSubtypeId(UUID.randomUUID().toString());
             entity.setStart(new Date(new Random().nextLong()));
             entity.setEnd(new Date(new Random().nextLong()));
 
@@ -104,7 +102,6 @@ class DelegationDtoTest {
             DelegationDto dto = new DelegationDto();
             dto.setId(UUID.randomUUID().toString());
             dto.setSubstituteDeskId(UUID.randomUUID().toString());
-            dto.setDelegatingDeskId(UUID.randomUUID().toString());
             dto.setTypeId(UUID.randomUUID().toString());
             dto.setSubtypeId(UUID.randomUUID().toString());
             dto.setStart(new Date(new Random().nextLong()));
diff --git a/src/test/java/coop/libriciel/ipcore/services/permission/DummyPermissionService.java b/src/test/java/coop/libriciel/ipcore/services/permission/DummyPermissionService.java
index b09608667d0951c4de3d81621f036c413e3d9da7..d696bc56ea10f9299d9478d980104ca6519a0209 100644
--- a/src/test/java/coop/libriciel/ipcore/services/permission/DummyPermissionService.java
+++ b/src/test/java/coop/libriciel/ipcore/services/permission/DummyPermissionService.java
@@ -189,14 +189,8 @@ public class DummyPermissionService implements PermissionServiceInterface {
 
 
     @Override
-    public void deleteDelegation(@NotNull String delegationId,
-                                 @NotNull String tenantId,
-                                 @NotNull String substituteDeskId,
-                                 @NotNull String delegatingDeskId,
-                                 @Nullable String typeId,
-                                 @Nullable String subtypeId,
-                                 @Nullable Date beginDate,
-                                 @Nullable Date endDate) {}
+    public void deleteDelegation(@NotNull String tenantId,
+                                 @NotNull Delegation delegation) {}
 
 
     @Override
diff --git a/src/test/resources/ip-core-openapi-provisioning.json b/src/test/resources/ip-core-openapi-provisioning.json
index a63f51a8bf621e8fdc4e2736402e72bdf915b9c4..eaf0d0d3bb9bf704a13969d6200caad8fbe3d928 100644
--- a/src/test/resources/ip-core-openapi-provisioning.json
+++ b/src/test/resources/ip-core-openapi-provisioning.json
@@ -35,13 +35,6 @@
       "DelegationDto": {
         "description": "target substitute desk, optional type/subtype; and schedule containing Dates and actions to perform.",
         "properties": {
-          "delegatingDesk": {
-            "$ref": "#/components/schemas/DeskRepresentation"
-          },
-          "delegatingDeskId": {
-            "type": "string",
-            "writeOnly": true
-          },
           "end": {
             "format": "date-time",
             "nullable": true,
@@ -3184,6 +3177,94 @@
         "tags": [
           "admin-desk"
         ]
+      },
+      "put": {
+        "operationId": "updateDelegationAsAdmin",
+        "parameters": [
+          {
+            "description": "Tenant id",
+            "in": "path",
+            "name": "tenantId",
+            "required": true,
+            "schema": {
+              "type": "string"
+            }
+          },
+          {
+            "description": "Desk id",
+            "in": "path",
+            "name": "deskId",
+            "required": true,
+            "schema": {
+              "type": "string"
+            }
+          },
+          {
+            "description": "Delegation Id",
+            "in": "path",
+            "name": "delegationId",
+            "required": true,
+            "schema": {
+              "type": "string"
+            }
+          }
+        ],
+        "requestBody": {
+          "content": {
+            "application/json": {
+              "schema": {
+                "$ref": "#/components/schemas/DelegationDto"
+              }
+            }
+          },
+          "required": true
+        },
+        "responses": {
+          "200": {
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/DelegationDto"
+                }
+              }
+            },
+            "description": "OK"
+          },
+          "401": {
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/ErrorResponse"
+                }
+              }
+            },
+            "description": "Unauthorized"
+          },
+          "403": {
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/ErrorResponse"
+                }
+              }
+            },
+            "description": "Forbidden"
+          },
+          "404": {
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/ErrorResponse"
+                }
+              }
+            },
+            "description": "Not Found"
+          }
+        },
+        "summary": "Update a delegation from target Desk",
+        "tags": [
+          "admin-desk"
+        ]
       }
     },
     "/api/provisioning/v1/admin/tenant/{tenantId}/folder/{folderId}": {