diff --git a/arkindex/documents/tests/test_destroy_elements.py b/arkindex/documents/tests/test_destroy_elements.py
index 7e147d67b0e1d3aa80ec6c105cb658bd6911434d..18e0b5b207e20d37a01fd7ba60f6d7162ee70a36 100644
--- a/arkindex/documents/tests/test_destroy_elements.py
+++ b/arkindex/documents/tests/test_destroy_elements.py
@@ -42,7 +42,13 @@ class TestDestroyElements(FixtureAPITestCase):
             name='Castle story'
         )
         self.assertEqual(self.corpus.elements.filter(id=castle_story.id).exists(), True)
-        with self.assertNumQueries(13):
+        with self.assertExactQueries(
+                'element_deletion.sql',
+                params={
+                    'id': str(castle_story.id),
+                    'corpus_id': str(self.corpus.id),
+                    'user_id': self.user.id
+                }, skip=1):
             response = self.client.delete(reverse('api:element-retrieve', kwargs={'pk': str(castle_story.id)}))
         self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
         self.assertEqual(self.corpus.elements.filter(id=castle_story.id).exists(), False)
diff --git a/arkindex/project/tests/__init__.py b/arkindex/project/tests/__init__.py
index 237513bb36dce8e334945563bd228560d4a83826..b42d515b72825d1aa42d945f5e7824e172c809de 100644
--- a/arkindex/project/tests/__init__.py
+++ b/arkindex/project/tests/__init__.py
@@ -20,10 +20,11 @@ class _AssertExactQueriesContext(CaptureQueriesContext):
     The implementation is inspired by assertNumQueries's own implementation.
     """
 
-    def __init__(self, test_case, path, params, connection):
+    def __init__(self, test_case, path, params, connection, skip=0):
         self.test_case = test_case
         self.path = settings.SQL_VALIDATION_DIR / Path(path)
         self.params = params
+        self.skip = skip
         super().__init__(connection)
 
     def __exit__(self, exc_type, exc_value, traceback):
@@ -34,7 +35,7 @@ class _AssertExactQueriesContext(CaptureQueriesContext):
         # Django's logged queries are each on a single line without semicolons
         # so we lint them and assemble them to build a more readable diff for humans.
         actual_sql = sqlparse.format(
-            ';'.join(query['sql'] for query in self),
+            ';'.join(query['sql'] for query in self[self.skip:]),
             reindent=True,
             use_space_around_operators=True,
             indent_width=4,
@@ -93,6 +94,7 @@ class FixtureMixin(object):
             *args,
             using: str = DEFAULT_DB_ALIAS,
             params: Union[Iterable, Mapping[str, Any]] = [],
+            skip: int = 0,
             **kwargs) -> Optional[_AssertExactQueriesContext]:
         """
         Assert that a function call causes exactly the SQL queries specified in a given file.
@@ -118,8 +120,16 @@ class FixtureMixin(object):
         To make first runs easier, when the expect SQL file does not exist, assertExactQueries will try to write
         the current SQL queries to it and warn you using an `AssertionError`. Make sure to check this new SQL file
         as subsequent runs of unit tests will no longer fail.
+
+        In API endpoint tests, some queries do not really need to be tested and are annoying to mock, such as
+        session queries that use the `django_session` table and are the first query to be made when you call an endpoint
+        using the test client. You can skip those queries and go straight to the point using `skip`.
+        This will skip the first query it finds:
+
+        >>> with self.assertExactQueries('my_endpoint.sql', skip=1):
+        ...     self.client.get(reverse('api:my-endpoint'))
         """
-        context = _AssertExactQueriesContext(self, path, params, connections[using])
+        context = _AssertExactQueriesContext(self, path, params, connections[using], skip=skip)
 
         if func is None:
             return context
diff --git a/arkindex/sql_validation/element_deletion.sql b/arkindex/sql_validation/element_deletion.sql
new file mode 100644
index 0000000000000000000000000000000000000000..61c9f93396b684bc814b43f928ca040ec4f1bbaf
--- /dev/null
+++ b/arkindex/sql_validation/element_deletion.sql
@@ -0,0 +1,119 @@
+SELECT "users_user"."id",
+       "users_user"."password",
+       "users_user"."last_login",
+       "users_user"."email",
+       "users_user"."transkribus_email",
+       "users_user"."is_active",
+       "users_user"."is_internal",
+       "users_user"."is_admin",
+       "users_user"."verified_email"
+FROM "users_user"
+WHERE "users_user"."id" = {user_id}
+LIMIT 21;
+
+SELECT "documents_element"."id",
+       "documents_element"."corpus_id",
+       "documents_corpus"."created",
+       "documents_corpus"."updated",
+       "documents_corpus"."id",
+       "documents_corpus"."name",
+       "documents_corpus"."description",
+       "documents_corpus"."repository_id",
+       "documents_corpus"."public"
+FROM "documents_element"
+INNER JOIN "documents_corpus" ON ("documents_element"."corpus_id" = "documents_corpus"."id")
+WHERE ("documents_element"."corpus_id" IN
+           (SELECT DISTINCT U0."id"
+            FROM "documents_corpus" U0
+            LEFT OUTER JOIN "users_corpusright" U1 ON (U0."id" = U1."corpus_id")
+            WHERE (U0."public"
+                   OR U1."user_id" = {user_id}))
+       AND "documents_element"."id" = '{id}'::uuid)
+LIMIT 21;
+
+SELECT "users_corpusright"."id",
+       "users_corpusright"."user_id",
+       "users_corpusright"."corpus_id",
+       "users_corpusright"."can_write",
+       "users_corpusright"."can_admin"
+FROM "users_corpusright"
+WHERE ("users_corpusright"."corpus_id" = '{corpus_id}'::uuid
+       AND "users_corpusright"."user_id" = {user_id})
+LIMIT 21;
+
+
+DELETE
+FROM documents_transcriptionentity te
+WHERE transcription_id IN
+        (SELECT t.id
+         FROM documents_transcription t
+         LEFT JOIN documents_elementpath elementpath USING (element_id)
+         WHERE t.element_id = '{id}'::uuid
+             OR elementpath.path && ARRAY['{id}'::uuid] ) ;
+
+
+DELETE
+FROM documents_transcription
+WHERE element_id = '{id}'::uuid
+    OR element_id IN
+        (SELECT element_id
+         FROM documents_elementpath
+         WHERE path && ARRAY['{id}'::uuid] ) ;
+
+
+DELETE
+FROM documents_classification
+WHERE element_id = '{id}'::uuid
+    OR element_id IN
+        (SELECT element_id
+         FROM documents_elementpath
+         WHERE path && ARRAY['{id}'::uuid] ) ;
+
+
+DELETE
+FROM documents_metadata
+WHERE element_id = '{id}'::uuid
+    OR element_id IN
+        (SELECT element_id
+         FROM documents_elementpath
+         WHERE path && ARRAY['{id}'::uuid] ) ;
+
+
+DELETE
+FROM dataimport_dataimportelement
+WHERE element_id = '{id}'::uuid
+    OR element_id IN
+        (SELECT element_id
+         FROM documents_elementpath
+         WHERE path && ARRAY['{id}'::uuid] ) ;
+
+
+UPDATE dataimport_dataimport
+SET element_id = NULL
+WHERE element_id = '{id}'::uuid
+    OR element_id IN
+        (SELECT element_id
+         FROM documents_elementpath
+         WHERE path && ARRAY['{id}'::uuid] ) ;
+
+
+DELETE
+FROM documents_selection selection
+WHERE element_id = '{id}'::uuid
+    OR element_id IN
+        (SELECT element_id
+         FROM documents_elementpath
+         WHERE path && ARRAY['{id}'::uuid] ) ;
+
+WITH children_ids (id) AS
+    (DELETE
+     FROM documents_elementpath
+     WHERE element_id = '{id}'::uuid
+         OR path && ARRAY['{id}'::uuid] RETURNING element_id)
+DELETE
+FROM documents_element element USING children_ids
+WHERE element.id = children_ids.id ;
+
+DELETE
+FROM "documents_element"
+WHERE "documents_element"."id" = '{id}'::uuid