diff --git a/arkindex/documents/api/elements.py b/arkindex/documents/api/elements.py
index 37c61d797499767d1a512732c148a5614e1e5ed5..7024a304eee36fd9216191c439caf98623c24761 100644
--- a/arkindex/documents/api/elements.py
+++ b/arkindex/documents/api/elements.py
@@ -1436,7 +1436,9 @@ class AllowedMetaDataEdit(CorpusACLMixin, RetrieveUpdateDestroyAPIView):
 @extend_schema(tags=['elements'])
 class ElementTypeCreate(CreateAPIView):
     """
-    Create a new element type for a specific corpus
+    Create a new element type on a corpus.
+
+    This requires admin access to the corpus.
     """
     serializer_class = ElementTypeSerializer
     permission_classes = (IsVerifiedOrReadOnly, )
@@ -1448,13 +1450,15 @@ class ElementTypeCreate(CreateAPIView):
 )
 class ElementTypeUpdate(UpdateAPIView):
     """
-    Update an existing element type
+    Update an existing element type.
+
+    This requires admin access to the element type's corpus.
     """
     serializer_class = ElementTypeLightSerializer
     permission_classes = (IsVerifiedOrReadOnly, )
 
     def get_queryset(self):
-        return ElementType.objects.filter(corpus__in=Corpus.objects.readable(self.request.user))
+        return ElementType.objects.filter(corpus__in=Corpus.objects.admin(self.request.user))
 
 
 @extend_schema_view(
diff --git a/arkindex/documents/serializers/elements.py b/arkindex/documents/serializers/elements.py
index a295d863725c7c33bdc6e7457f1a659c0f4445dc..569d2b0e055972008850eb7e7f7943bb1d039ed8 100644
--- a/arkindex/documents/serializers/elements.py
+++ b/arkindex/documents/serializers/elements.py
@@ -802,7 +802,7 @@ class ElementTypeSerializer(ElementTypeLightSerializer):
         if not self.context.get('request'):
             # Keep original fields so OpenAPI can build its schemas
             return
-        self.fields['corpus'].queryset = Corpus.objects.writable(self.context['request'].user)
+        self.fields['corpus'].queryset = Corpus.objects.admin(self.context['request'].user)
 
 
 class ElementBulkItemSerializer(serializers.Serializer):
diff --git a/arkindex/documents/tests/test_element_type.py b/arkindex/documents/tests/test_element_type.py
index 6caf058aa8b832259ea2bbe12d8f28ef8186c5f9..8b3e7e1105dd2249b9d3d9472fd17d93d739be34 100644
--- a/arkindex/documents/tests/test_element_type.py
+++ b/arkindex/documents/tests/test_element_type.py
@@ -3,7 +3,7 @@ from rest_framework import status
 
 from arkindex.documents.models import Corpus, ElementType
 from arkindex.project.tests import FixtureAPITestCase
-from arkindex.users.models import User
+from arkindex.users.models import Role, User
 
 
 class TestElementType(FixtureAPITestCase):
@@ -15,7 +15,7 @@ class TestElementType(FixtureAPITestCase):
         cls.user = User.objects.get(email="user@user.fr")
         cls.private_corpus = Corpus.objects.create(name='private')
 
-    def test_create_element_type_requires_login(self):
+    def test_create_requires_login(self):
         response = self.client.post(reverse('api:element-type-create'), {
             'corpus': self.corpus.id,
             'slug': 'New_element_type',
@@ -23,7 +23,7 @@ class TestElementType(FixtureAPITestCase):
         }, format='json')
         self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
 
-    def test_create_element_type_no_access(self):
+    def test_create_no_access(self):
         self.client.force_login(self.user)
         response = self.client.post(reverse('api:element-type-create'), {
             'corpus': self.private_corpus.id,
@@ -32,7 +32,17 @@ class TestElementType(FixtureAPITestCase):
         }, format='json')
         self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
 
-    def test_create_element_type_wrong_slug(self):
+    def test_create_requires_admin(self):
+        self.private_corpus.memberships.create(user=self.user, level=Role.Contributor.value)
+        self.client.force_login(self.user)
+        response = self.client.post(reverse('api:element-type-create'), {
+            'corpus': self.private_corpus.id,
+            'slug': 'New_element_type',
+            'display_name': 'New element type',
+        }, format='json')
+        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+
+    def test_create_wrong_slug(self):
         self.client.force_login(self.superuser)
         response = self.client.post(reverse('api:element-type-create'), {
             'corpus': self.corpus.id,
@@ -41,7 +51,7 @@ class TestElementType(FixtureAPITestCase):
         }, format='json')
         self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
 
-    def test_create_element_type(self):
+    def test_create(self):
         self.client.force_login(self.superuser)
         response = self.client.post(reverse('api:element-type-create'), {
             'corpus': self.corpus.id,
@@ -63,15 +73,25 @@ class TestElementType(FixtureAPITestCase):
             }
         )
 
-    def test_update_element_type_requires_login(self):
+    def test_partial_update_requires_login(self):
         response = self.client.post(reverse('api:element-type', kwargs={'pk': self.element_type.id}), {
             'slug': 'New_element_type',
             'display_name': 'New element type',
         }, format='json')
         self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
 
-    def test_update_element_type_no_access(self):
+    def test_partial_update_no_access(self):
+        private_element_type = ElementType.objects.create(corpus=self.private_corpus, slug='element', display_name='Element')
+        self.client.force_login(self.user)
+        response = self.client.patch(reverse('api:element-type', kwargs={'pk': private_element_type.id}), {
+            'slug': 'New_element_type',
+            'display_name': 'New element type',
+        }, format='json')
+        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
+
+    def test_partial_update_requires_admin(self):
         private_element_type = ElementType.objects.create(corpus=self.private_corpus, slug='element', display_name='Element')
+        self.private_corpus.memberships.create(user=self.user, level=Role.Contributor.value)
         self.client.force_login(self.user)
         response = self.client.patch(reverse('api:element-type', kwargs={'pk': private_element_type.id}), {
             'slug': 'New_element_type',
@@ -79,7 +99,7 @@ class TestElementType(FixtureAPITestCase):
         }, format='json')
         self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
 
-    def test_update_element_type_wrong_slug(self):
+    def test_partial_update_wrong_slug(self):
         element_type = ElementType.objects.create(corpus=self.corpus, slug='new_type', display_name='Element')
         self.client.force_login(self.superuser)
         response = self.client.patch(reverse('api:element-type', kwargs={'pk': self.element_type.id}), {
@@ -88,9 +108,83 @@ class TestElementType(FixtureAPITestCase):
         self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
         self.assertDictEqual(response.json(), {'slug': ['Slug must be unique']})
 
-    def test_update_element_type(self):
+    def test_partial_update(self):
         self.client.force_login(self.superuser)
-        response = self.client.patch(reverse('api:element-type', kwargs={'pk': self.element_type.id}), {
+        cases = [
+            {'slug': 'new_element_type'},
+            {'display_name': 'New element type'},
+            {'folder': True},
+            {
+                'slug': 'new_element_type',
+                'display_name': 'New element type',
+                'folder': False,
+            },
+            {
+                'slug': 'new_element_type',
+                'display_name': 'New element type',
+            }
+        ]
+
+        for update_data in cases:
+            with self.subTest(**update_data):
+                response = self.client.patch(
+                    reverse('api:element-type', kwargs={'pk': self.element_type.id}),
+                    update_data,
+                    format='json'
+                )
+                self.assertEqual(response.status_code, status.HTTP_200_OK)
+                data = response.json()
+                self.element_type.refresh_from_db()
+                self.assertIn('id', data)
+                self.assertDictEqual(
+                    data,
+                    {
+                        'id': str(self.element_type.id),
+                        'slug': self.element_type.slug,
+                        'display_name': self.element_type.display_name,
+                        'folder': self.element_type.folder
+                    }
+                )
+
+    def test_update_requires_login(self):
+        response = self.client.post(reverse('api:element-type', kwargs={'pk': self.element_type.id}), {
+            'slug': 'New_element_type',
+            'display_name': 'New element type',
+        }, format='json')
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+
+    def test_update_no_access(self):
+        private_element_type = ElementType.objects.create(corpus=self.private_corpus, slug='element', display_name='Element')
+        self.client.force_login(self.user)
+        response = self.client.put(reverse('api:element-type', kwargs={'pk': private_element_type.id}), {
+            'slug': 'New_element_type',
+            'display_name': 'New element type',
+        }, format='json')
+        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
+
+    def test_update_requires_admin(self):
+        private_element_type = ElementType.objects.create(corpus=self.private_corpus, slug='element', display_name='Element')
+        self.private_corpus.memberships.create(user=self.user, level=Role.Contributor.value)
+        self.client.force_login(self.user)
+        response = self.client.put(reverse('api:element-type', kwargs={'pk': private_element_type.id}), {
+            'slug': 'New_element_type',
+            'display_name': 'New element type',
+        }, format='json')
+        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
+
+    def test_update_wrong_slug(self):
+        element_type = ElementType.objects.create(corpus=self.corpus, slug='new_type', display_name='Element')
+        self.client.force_login(self.superuser)
+        response = self.client.put(reverse('api:element-type', kwargs={'pk': self.element_type.id}), {
+            'slug': element_type.slug,
+            'display_name': 'New element type',
+        }, format='json')
+        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+        self.assertDictEqual(response.json(), {'slug': ['Slug must be unique']})
+
+    def test_update(self):
+        self.client.force_login(self.superuser)
+        response = self.client.put(reverse('api:element-type', kwargs={'pk': self.element_type.id}), {
             'slug': 'New_element_type',
             'display_name': 'New element type',
         }, format='json')