diff --git a/arkindex/documents/api/elements.py b/arkindex/documents/api/elements.py
index f08ae65497d605abcdba23bca2c3283615d5ffdd..2116376656dfff08d3af6814acdc982f37ecf321 100644
--- a/arkindex/documents/api/elements.py
+++ b/arkindex/documents/api/elements.py
@@ -573,14 +573,12 @@ class ElementsListBase(CorpusACLMixin, DestroyModelMixin, ListAPIView):
 
     @cached_property
     def selected_corpus(self):
-        # Retrieve the corpus and check user access rights only once
+        # Retrieve the corpus only once
         corpus_id = self.kwargs.get("corpus")
         if corpus_id is None:
             return
 
-        corpus = get_object_or_404(Corpus, id=corpus_id)
-        self.check_corpus_access(corpus)
-        return corpus
+        return get_object_or_404(Corpus, id=corpus_id)
 
     @property
     def folder_filter(self):
@@ -1041,6 +1039,7 @@ class CorpusElements(ElementsListBase):
     def get_queryset(self):
         # Should not be possible due to the URL
         assert self.selected_corpus, "Missing corpus ID"
+        self.check_corpus_access(self.selected_corpus)
         return self.selected_corpus.elements.all()
 
     def get_filters(self) -> Q:
diff --git a/arkindex/documents/api/export.py b/arkindex/documents/api/export.py
index c2f6a52f9625714e95a8e6e21e1779fb5e7f42bd..9cec9a8351fa5079d0a623abd3b3394fb0db5e78 100644
--- a/arkindex/documents/api/export.py
+++ b/arkindex/documents/api/export.py
@@ -6,7 +6,7 @@ from django.shortcuts import get_object_or_404
 from django.utils import timezone
 from django.utils.functional import cached_property
 from drf_spectacular.utils import extend_schema, extend_schema_view
-from rest_framework import permissions, serializers, status
+from rest_framework import permissions, status
 from rest_framework.exceptions import PermissionDenied, ValidationError
 from rest_framework.generics import ListCreateAPIView, RetrieveDestroyAPIView
 from rest_framework.response import Response
@@ -78,7 +78,7 @@ class CorpusExportAPIView(ListCreateAPIView):
             Guest access is required on private corpora.
             """
         ),
-        responses={302: serializers.Serializer},
+        responses={302: None},
     ),
     delete=extend_schema(
         operation_id="DestroyExport",
diff --git a/arkindex/ponos/api.py b/arkindex/ponos/api.py
index 368a44055b337a81a81b907d26b48c61e3df505d..a0a43c5ed8f59b9fe496d70a5f958ccbfe6acf04 100644
--- a/arkindex/ponos/api.py
+++ b/arkindex/ponos/api.py
@@ -4,7 +4,7 @@ from textwrap import dedent
 from django.db import transaction
 from django.shortcuts import get_object_or_404, redirect
 from drf_spectacular.utils import extend_schema, extend_schema_view
-from rest_framework import serializers, status
+from rest_framework import status
 from rest_framework.authentication import SessionAuthentication, TokenAuthentication
 from rest_framework.exceptions import NotFound, ValidationError
 from rest_framework.generics import CreateAPIView, ListCreateAPIView, RetrieveUpdateAPIView, UpdateAPIView
@@ -200,12 +200,12 @@ class TaskUpdate(UpdateAPIView):
             The task must be in a final state to be restarted.
             """
         ),
+        request=None,
         responses={201: TaskSerializer},
     ),
 )
 class TaskRestart(ProcessACLMixin, CreateAPIView):
     permission_classes = (IsVerified,)
-    serializer_class = serializers.Serializer
 
     def get_task(self):
         task = get_object_or_404(
diff --git a/arkindex/process/api.py b/arkindex/process/api.py
index 154f1273bac0cdfc23c2dbdad7d3f2261d8b847a..b291def10562450d1cf886f8a777f8e4778e92cc 100644
--- a/arkindex/process/api.py
+++ b/arkindex/process/api.py
@@ -1327,7 +1327,7 @@ class WorkerRunList(ProcessACLMixin, ListCreateAPIView):
             201: WorkerRunSerializer,
             400: OpenApiResponse(
                 response=inline_serializer(
-                    name="Error message with ID",
+                    name="ErrorWithID",
                     fields={
                         "detail": serializers.ListField(child=serializers.CharField(), required=False),
                         "id": serializers.UUIDField(required=False, help_text="ID of an existing worker run")
@@ -1557,6 +1557,7 @@ class WorkerRunDetails(ProcessACLMixin, RetrieveUpdateDestroyAPIView):
             False: ProcessElementLightSerializer,
         },
         resource_type_field_name="with_image",
+        many=True,
     ),
     tags=["process"]
 ))
@@ -2193,6 +2194,7 @@ class S3ImportCreate(CreateAPIView):
     post=extend_schema(
         operation_id="SelectProcessFailures",
         tags=["process"],
+        request=None,
         responses={204: None},
     ),
 )
diff --git a/arkindex/process/tests/test_processes.py b/arkindex/process/tests/test_processes.py
index 7140e8a3ef394c64800be592dcb5d675b18b3275..d0466ec05af91a109900aff3e1906427d8ae682e 100644
--- a/arkindex/process/tests/test_processes.py
+++ b/arkindex/process/tests/test_processes.py
@@ -3049,18 +3049,21 @@ class TestProcesses(FixtureAPITestCase):
         self.elts_process.activity_state = ActivityState.Ready
         self.elts_process.save()
         self.client.force_login(self.user)
+
         for state in unfinished_states:
             with self.subTest(state=state):
                 self.elts_process.tasks.update(state=state)
                 self.assertEqual(self.elts_process.state, state)
+
                 response = self.client.post(
                     reverse("api:process-select-failures", kwargs={"pk": str(self.elts_process.id)})
                 )
                 self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
-            self.assertDictEqual(
-                response.json(),
-                {"__all__": ["The process must be finished to select elements with failures."]},
-            )
+
+                self.assertDictEqual(
+                    response.json(),
+                    {"__all__": ["The process must be finished to select elements with failures."]},
+                )
 
     def test_select_failed_elts_no_failure(self):
         self.elts_process.run()
diff --git a/arkindex/project/openapi/schema.py b/arkindex/project/openapi/schema.py
index b7737991377f34603fd112c528cf8fe000943fc7..4cac2e090c185c1700db48691a3910b858d83d26 100644
--- a/arkindex/project/openapi/schema.py
+++ b/arkindex/project/openapi/schema.py
@@ -32,5 +32,6 @@ class AutoSchema(BaseAutoSchema):
         Spectacular does not include any pagination parameters.
         """
         operation = super().get_operation(*args, **kwargs)
-        operation["x-paginated"] = self._is_list_view() and self._get_paginator() is not None
+        if operation is not None:
+            operation["x-paginated"] = self._is_list_view() and self._get_paginator() is not None
         return operation
diff --git a/arkindex/project/settings.py b/arkindex/project/settings.py
index 30feb9bf9877e2e140b40d43ed051572dc5c7826..fe463ed9634eb16e1013d40337585d13e485423c 100644
--- a/arkindex/project/settings.py
+++ b/arkindex/project/settings.py
@@ -219,6 +219,9 @@ SIMPLE_JWT = {
 
 SPECTACULAR_SETTINGS = {
     "CAMELIZE_NAMES": True,
+    # Remove the automatically generated `description` that lists the members of all enums,
+    # which duplicates the enum choices already displayed by ReDoc and does not add any documentation
+    "ENUM_GENERATE_CHOICE_DESCRIPTION": False,
     "SCHEMA_PATH_PREFIX": "/api/v1/",
     "TITLE": "Arkindex API",
     "CONTACT": {
diff --git a/arkindex/training/api.py b/arkindex/training/api.py
index 77289fab01822ca7819042ef6895be4696d7c4aa..20f10a203fb5418b4f8c2f94a6e609d555e0330b 100644
--- a/arkindex/training/api.py
+++ b/arkindex/training/api.py
@@ -522,9 +522,7 @@ class ModelCompatibleWorkerManage(CreateAPIView, DestroyAPIView):
             "Retrieve a download url for a model_version. A secret token is required to have access to the model version."
             "This endpoint does **not** require to be logged in."
         ),
-        responses={
-            302: serializers.Serializer,
-        },
+        responses={302: None},
         parameters=[
             OpenApiParameter(
                 "token",
diff --git a/arkindex/training/serializers.py b/arkindex/training/serializers.py
index 2f658f8b730a1bc6b6bd25c05350c0625ad0595e..48ec505718ece1ef8eede564a89da1ec14680311 100644
--- a/arkindex/training/serializers.py
+++ b/arkindex/training/serializers.py
@@ -171,7 +171,7 @@ class ModelVersionLightSerializer(serializers.ModelSerializer):
 
     model = ModelLightSerializer(default=_model_from_context)
     state = EnumField(ModelVersionState)
-    configuration = serializers.JSONField(style={"base_template": "textarea.html"})
+    configuration = serializers.DictField(style={"base_template": "textarea.html"})
 
     class Meta:
         model = ModelVersion
@@ -190,7 +190,7 @@ class ModelVersionSerializer(serializers.ModelSerializer):
         allow_null=True,
     )
     description = serializers.CharField(required=False, style={"base_template": "textarea.html"})
-    configuration = serializers.JSONField(required=False, decoder=None, encoder=None, style={"base_template": "textarea.html"})
+    configuration = serializers.DictField(required=False, style={"base_template": "textarea.html"})
     tag = serializers.CharField(allow_null=True, max_length=50, required=False, default=None)
     state = EnumField(ModelVersionState, read_only=True)
     s3_url = serializers.SerializerMethodField(read_only=True, help_text="Only returned if the user has **contributor** access to the model, and the model is available.")
diff --git a/arkindex/users/api.py b/arkindex/users/api.py
index 5bc55e012c13abb97df750e9469ab14de34420a2..e4bfc44db7d8209ce6f8cd0b6e06770f3f4d0ef8 100644
--- a/arkindex/users/api.py
+++ b/arkindex/users/api.py
@@ -13,7 +13,7 @@ from django_rq.queues import get_queue
 from django_rq.settings import QUEUES
 from drf_spectacular.types import OpenApiTypes
 from drf_spectacular.utils import OpenApiParameter, extend_schema, extend_schema_view
-from rest_framework import serializers, status
+from rest_framework import status
 from rest_framework.exceptions import AuthenticationFailed, NotFound, PermissionDenied, ValidationError
 from rest_framework.generics import CreateAPIView, ListAPIView, RetrieveDestroyAPIView, RetrieveUpdateDestroyAPIView
 from rest_framework.response import Response
@@ -180,7 +180,7 @@ class UserEmailVerification(APIView):
                 required=True,
             )
         ],
-        responses={302: serializers.Serializer}
+        responses={302: None},
     )
     def get(self, *args, **kwargs):
         if not all(arg in self.request.GET for arg in ("email", "token")):
diff --git a/arkindex/users/serializers.py b/arkindex/users/serializers.py
index 8bf49eeea60a226a5c1c9bae0c2c449e7962c148..d30ba1c3691a73bc3148e7f68c55bd21d9c25859 100644
--- a/arkindex/users/serializers.py
+++ b/arkindex/users/serializers.py
@@ -161,9 +161,7 @@ class JobSerializer(serializers.Serializer):
     ended_at = serializers.DateTimeField(read_only=True, allow_null=True)
 
     def get_status(self, instance) -> str:
-        """
-        Avoid causing more Redis queries to fetch a job's current status
-        Note that a job status is part of a JobStatus enum,
-        but the enum is just a plain object and not an Enum for Py2 compatibility.
-        """
+        # Avoid causing more Redis queries to fetch a job's current status
+        # Note that a job status is part of a JobStatus enum,
+        # but the enum is just a plain object and not an Enum for Py2 compatibility.
         return instance.get_status(refresh=False)
diff --git a/requirements.txt b/requirements.txt
index dc936a8639ede1e2d7cb9c1d276b8c11fe45bb83..0c6424ce3eb36d753d34f21bf83554fa0ff068f2 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -10,7 +10,7 @@ django-rq==2.10.1
 djangorestframework==3.13.1
 djangorestframework-simplejwt==5.2.2
 docker==7.0.0
-drf-spectacular==0.21.2
+drf-spectacular==0.27.2
 python-magic==0.4.27
 python-memcached==1.59
 pytz==2023.3