diff --git a/requirements.txt b/requirements.txt
index 2a1d5627e19ca66948b5189656b2612f17d61ab4..122fe03c84cb9f04b0000669c1f10855b9567cd3 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,10 +2,10 @@ fastapi==0.108.0
 hypercorn==0.16.0
 toml==0.10.2
 email-validator==2.1.0.post1
-python-dotenv==1.0.0
+python-dotenv==1.0.1
 passlib[bcrypt]==1.7.4
 pymitter==0.5.0
-uvicorn==0.25.0
-python-multipart==0.0.6
+uvicorn==0.27.0.post1
+python-multipart==0.0.7
 aiosmtplib==3.0.1
-nacsos_data[scripts,server,utils] @ git+ssh://git@gitlab.pik-potsdam.de/mcc-apsis/nacsos/nacsos-data.git@v0.12.15
+nacsos_data[scripts,server,utils] @ git+ssh://git@gitlab.pik-potsdam.de/mcc-apsis/nacsos/nacsos-data.git@v0.13.0
diff --git a/requirements_dev.txt b/requirements_dev.txt
index a0b8d3edfe136758f8dcb6e46245cfa45c80c846..c40c69763d949be7146f27b91de5b55ec643ad25 100644
--- a/requirements_dev.txt
+++ b/requirements_dev.txt
@@ -1,7 +1,7 @@
-flake8==6.1.0
-tox==4.11.4
-pytest==7.4.3
+flake8==7.0.0
+tox==4.12.1
+pytest==8.0.0
 pytest-cov==4.1.0
-mypy==1.7.1
+mypy==1.8.0
 types-toml==0.10.8.7
 types-PyYAML==6.0.12.12
\ No newline at end of file
diff --git a/server/api/routes/annotations.py b/server/api/routes/annotations.py
index f4385931f784cfa490c1ed1d774b6c52cf760c3c..40a721617968aafba011f81ec33b5bb3c59f3d5c 100644
--- a/server/api/routes/annotations.py
+++ b/server/api/routes/annotations.py
@@ -61,7 +61,6 @@ from nacsos_data.db.crud.annotations import (
     read_resolved_bot_annotation_meta,
     read_resolved_bot_annotations_for_meta
 )
-from nacsos_data.util.annotations import AnnotationFilterObject
 from nacsos_data.util.annotations.resolve import (
     get_resolved_item_annotations,
     read_annotation_scheme
@@ -73,7 +72,6 @@ from nacsos_data.util.annotations.validation import (
 )
 from nacsos_data.util.annotations.assignments.random import random_assignments
 from nacsos_data.util.annotations.assignments.random_exclusion import random_assignments_with_exclusion
-from nacsos_data.util.annotations.assignments.random_nql import random_assignments_with_nql
 
 from server.api.errors import (
     SaveFailedError,
@@ -390,17 +388,6 @@ async def make_assignments(payload: MakeAssignmentsRequestModel,
         except ValueError as e:
             raise HTTPException(status_code=http_status.HTTP_400_BAD_REQUEST,
                                 detail=str(e))
-    elif payload.config.config_type == 'random_nql':
-        try:
-            assignments = await random_assignments_with_nql(
-                assignment_scope_id=payload.scope_id,
-                annotation_scheme_id=payload.annotation_scheme_id,
-                project_id=permissions.permissions.project_id,
-                config=payload.config,  # type: ignore[arg-type] # FIXME
-                engine=db_engine)
-        except ValueError as e:
-            raise HTTPException(status_code=http_status.HTTP_400_BAD_REQUEST,
-                                detail=str(e))
     else:
         raise HTTPException(status_code=http_status.HTTP_501_NOT_IMPLEMENTED,
                             detail=f'Method "{payload.config.config_type}" is unknown.')
@@ -435,12 +422,13 @@ async def get_annotators_for_scheme(scheme_id: str,
                                           .where(Annotation.annotation_scheme_id == scheme_id))).scalars().all()]
 
 
-@router.post('/config/resolve/', response_model=ResolutionProposal)
+@router.post('/config/resolve', response_model=ResolutionProposal)
 async def get_resolved_annotations(settings: BotMetaResolveBase,
-                                   include_empty: bool | None = Query(default=False),
-                                   existing_resolution: str | None = Query(default=None),
-                                   include_new: bool | None = Query(default=False),
-                                   update_existing: bool | None = Query(default=False),
+                                   assignment_scope_id: str | None = None,
+                                   bot_annotation_metadat_id: str | None = None,
+                                   include_empty: bool = False,
+                                   include_new: bool = False,
+                                   update_existing: bool = False,
                                    permissions=Depends(UserPermissionChecker('annotations_edit'))) \
         -> ResolutionProposal:
     """
@@ -448,28 +436,30 @@ async def get_resolved_annotations(settings: BotMetaResolveBase,
 
     :param include_new:
     :param update_existing:
-    :param existing_resolution:
+    :param assignment_scope_id:
+    :param bot_annotation_metadat_id:
     :param include_empty:
     :param settings
     :param permissions:
     :return:
     """
     if include_empty is None:
-        include_empty = True
+        include_empty = True  # type: ignore[unreachable]
     if include_new is None:
-        include_new = False
+        include_new = False  # type: ignore[unreachable]
     if update_existing is None:
-        update_existing = False
+        update_existing = False  # type: ignore[unreachable]
 
-    if existing_resolution is not None:
+    if bot_annotation_metadat_id is not None:
         return await read_resolved_bot_annotations(db_engine=db_engine,
-                                                   existing_resolution=existing_resolution,
+                                                   existing_resolution=bot_annotation_metadat_id,
                                                    include_new=include_new,
                                                    include_empty=include_empty,
                                                    update_existing=update_existing)
-    filters = AnnotationFilterObject.model_validate(settings.filters.model_dump())
+    if assignment_scope_id is None:
+        raise ValueError('Missing assignment scope')
     return await get_resolved_item_annotations(strategy=settings.algorithm,
-                                               filters=filters,
+                                               assignment_scope_id=assignment_scope_id,
                                                ignore_repeat=settings.ignore_repeat,
                                                ignore_hierarchy=settings.ignore_hierarchy,
                                                include_new=include_new,
@@ -502,12 +492,15 @@ async def get_saved_resolved_annotations(bot_annotation_metadata_id: str,
 async def save_resolved_annotations(settings: BotMetaResolveBase,
                                     matrix: ResolutionMatrix,
                                     name: str,
+                                    assignment_scope_id: str,
+                                    annotation_scheme_id: str,
                                     permissions=Depends(UserPermissionChecker('annotations_edit'))):
     meta_id = await store_resolved_bot_annotations(db_engine=db_engine,
                                                    project_id=permissions.permissions.project_id,
+                                                   assignment_scope_id=assignment_scope_id,
+                                                   annotation_scheme_id=annotation_scheme_id,
                                                    name=name,
                                                    algorithm=settings.algorithm,
-                                                   filters=settings.filters,
                                                    ignore_hierarchy=settings.ignore_hierarchy,
                                                    ignore_repeat=settings.ignore_repeat,
                                                    matrix=matrix)
@@ -525,9 +518,10 @@ async def update_resolved_annotations(bot_annotation_metadata_id: str,
 
 
 @router.get('/config/resolved-list/', response_model=list[BotAnnotationMetaDataBaseModel])
-async def list_saved_resolved_annotations(permissions=Depends(UserPermissionChecker('annotations_read'))):
+async def list_saved_resolved_annotations(annotation_scheme_id: str | None = None,
+                                          permissions=Depends(UserPermissionChecker('annotations_read'))):
     async with db_engine.session() as session:  # type: AsyncSession
-        exports = (await session.execute(
+        stmt = (
             select(BotAnnotationMetaData)
             .where(BotAnnotationMetaData.project_id == permissions.permissions.project_id,
                    BotAnnotationMetaData.kind == BotKind.RESOLVE)
@@ -539,9 +533,11 @@ async def list_saved_resolved_annotations(permissions=Depends(UserPermissionChec
                                BotAnnotationMetaData.name,
                                BotAnnotationMetaData.kind,
                                BotAnnotationMetaData.time_updated,
-                               BotAnnotationMetaData.time_created)))) \
-            .scalars().all()
-
+                               BotAnnotationMetaData.time_created))
+        )
+        if annotation_scheme_id is not None:
+            stmt = stmt.where(BotAnnotationMetaData.annotation_scheme_id == annotation_scheme_id)
+        exports = (await session.execute(stmt)).scalars().all()
         return [BotAnnotationMetaDataBaseModel.model_validate(e.__dict__) for e in exports]
 
 
diff --git a/server/api/routes/evaluation.py b/server/api/routes/evaluation.py
index b34be08444ef87476c08559e61ff6610a244c08f..d7ab7ada2a1f0adeb527395f17b227e6487da1df 100644
--- a/server/api/routes/evaluation.py
+++ b/server/api/routes/evaluation.py
@@ -161,7 +161,7 @@ async def bg_populate_tracker(tracker_id: str, batch_size: int | None = None, la
 
             if batch_size is None:
                 # Use scopes as batches
-                it = calculate_h0s_for_batches(labels=labels,
+                it = calculate_h0s_for_batches(labels=tracker.labels,
                                                recall_target=tracker.recall_target,
                                                n_docs=tracker.n_items_total)
             else:
@@ -190,8 +190,10 @@ async def get_irr(assignment_scope_id: str,
         return [AnnotationQualityModel(**r.__dict__) for r in results]
 
 
-@router.get('/quality/compute/{assignment_scope_id}', response_model=list[AnnotationQualityModel])
+@router.get('/quality/compute', response_model=list[AnnotationQualityModel])
 async def recompute_irr(assignment_scope_id: str,
+                        bot_annotation_metadata_id: str | None = None,
+                        relevance_rule: str | None = None,
                         permissions: UserPermissions = Depends(UserPermissionChecker('annotations_read'))) \
         -> list[AnnotationQualityModel]:
     async with db_engine.session() as session:  # type: AsyncSession
diff --git a/server/api/routes/users.py b/server/api/routes/users.py
index 9491235f90208fe6731c6a2743b2718914476ed2..ea0c59023a75dc0650c7d964240b50ae10279dfa 100644
--- a/server/api/routes/users.py
+++ b/server/api/routes/users.py
@@ -4,7 +4,7 @@ from fastapi import APIRouter, Depends, Query
 from sqlalchemy import select, asc
 
 from nacsos_data.util.auth import UserPermissions
-from nacsos_data.models.users import UserModel, UserInDBModel, UserBaseModel
+from nacsos_data.models.users import UserModel, UserInDBModel, UserBaseModel, DehydratedUser
 from nacsos_data.db.schemas import User, AssignmentScope, AnnotationScheme, Assignment
 from nacsos_data.db.crud.users import (
     read_users,
@@ -35,6 +35,15 @@ async def get_all_users(current_user: UserModel = Depends(get_current_active_use
     return result
 
 
+@router.get('/list/all/dehydrated', response_model=list[DehydratedUser])
+async def get_all_users_dehydrated(current_user: UserModel = Depends(get_current_active_user)) \
+        -> list[UserInDBModel]:
+    result = await read_users(project_id=None, order_by_username=True, engine=db_engine)
+    if result is None:
+        return []
+    return result
+
+
 @router.get('/list/project/annotators/{project_id}', response_model=dict[str, UserBaseModel])
 async def get_project_annotator_users(project_id: str,
                                       permissions: UserPermissions = Depends(UserPermissionChecker())) \