diff --git a/backend/capellacollab/alembic/versions/014438261702_add_provisioning_feature.py b/backend/capellacollab/alembic/versions/014438261702_add_provisioning_feature.py
new file mode 100644
index 0000000000..df65ff933b
--- /dev/null
+++ b/backend/capellacollab/alembic/versions/014438261702_add_provisioning_feature.py
@@ -0,0 +1,51 @@
+# SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
+# SPDX-License-Identifier: Apache-2.0
+
+"""Add provisioning feature
+
+Revision ID: 014438261702
+Revises: 3818a5009130
+Create Date: 2024-10-11 17:34:05.210906
+
+"""
+import sqlalchemy as sa
+from alembic import op
+
+# revision identifiers, used by Alembic.
+revision = "014438261702"
+down_revision = "3818a5009130"
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    op.create_table(
+        "model_provisioning",
+        sa.Column("id", sa.Integer(), nullable=False),
+        sa.Column("user_id", sa.Integer(), nullable=False),
+        sa.Column("tool_model_id", sa.Integer(), nullable=False),
+        sa.Column("revision", sa.String(), nullable=False),
+        sa.Column("commit_hash", sa.String(), nullable=False),
+        sa.Column("provisioned_at", sa.DateTime(), nullable=False),
+        sa.ForeignKeyConstraint(
+            ["tool_model_id"],
+            ["models.id"],
+        ),
+        sa.ForeignKeyConstraint(
+            ["user_id"],
+            ["users.id"],
+        ),
+        sa.PrimaryKeyConstraint("id"),
+    )
+    op.create_index(
+        op.f("ix_model_provisioning_id"),
+        "model_provisioning",
+        ["id"],
+        unique=False,
+    )
+    op.add_column(
+        "sessions", sa.Column("provisioning_id", sa.Integer(), nullable=True)
+    )
+    op.create_foreign_key(
+        None, "sessions", "model_provisioning", ["provisioning_id"], ["id"]
+    )
diff --git a/backend/capellacollab/core/database/models.py b/backend/capellacollab/core/database/models.py
index c82af1b56d..16f56acd2b 100644
--- a/backend/capellacollab/core/database/models.py
+++ b/backend/capellacollab/core/database/models.py
@@ -13,6 +13,7 @@
 import capellacollab.projects.toolmodels.models
 import capellacollab.projects.toolmodels.modelsources.git.models
 import capellacollab.projects.toolmodels.modelsources.t4c.models
+import capellacollab.projects.toolmodels.provisioning.models
 import capellacollab.projects.toolmodels.restrictions.models
 import capellacollab.projects.users.models
 import capellacollab.sessions.models
diff --git a/backend/capellacollab/projects/toolmodels/modelsources/git/routes.py b/backend/capellacollab/projects/toolmodels/modelsources/git/routes.py
index 42339c2153..d5d7a0d190 100644
--- a/backend/capellacollab/projects/toolmodels/modelsources/git/routes.py
+++ b/backend/capellacollab/projects/toolmodels/modelsources/git/routes.py
@@ -14,7 +14,7 @@
 from capellacollab.projects.toolmodels import models as toolmodels_models
 from capellacollab.projects.toolmodels.backups import crud as backups_crud
 from capellacollab.projects.users import models as projects_users_models
-from capellacollab.settings.modelsources.git import core as git_core
+from capellacollab.settings.modelsources.git import core as instances_git_core
 from capellacollab.settings.modelsources.git import models as git_models
 from capellacollab.settings.modelsources.git import util as git_util
 
@@ -69,7 +69,7 @@ async def get_revisions_of_primary_git_model(
         injectables.get_existing_primary_git_model
     ),
 ) -> git_models.GetRevisionsResponseModel:
-    return await git_core.get_remote_refs(
+    return await instances_git_core.get_remote_refs(
         primary_git_model.path,
         primary_git_model.username,
         primary_git_model.password,
@@ -94,7 +94,7 @@ async def get_revisions_with_model_credentials(
         injectables.get_existing_git_model
     ),
 ):
-    return await git_core.get_remote_refs(
+    return await instances_git_core.get_remote_refs(
         url, git_model.username, git_model.password
     )
 
diff --git a/backend/capellacollab/projects/toolmodels/provisioning/__init__.py b/backend/capellacollab/projects/toolmodels/provisioning/__init__.py
new file mode 100644
index 0000000000..04412280d8
--- /dev/null
+++ b/backend/capellacollab/projects/toolmodels/provisioning/__init__.py
@@ -0,0 +1,2 @@
+# SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
+# SPDX-License-Identifier: Apache-2.0
diff --git a/backend/capellacollab/projects/toolmodels/provisioning/crud.py b/backend/capellacollab/projects/toolmodels/provisioning/crud.py
new file mode 100644
index 0000000000..ccdfb57834
--- /dev/null
+++ b/backend/capellacollab/projects/toolmodels/provisioning/crud.py
@@ -0,0 +1,36 @@
+# SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
+# SPDX-License-Identifier: Apache-2.0
+
+import sqlalchemy as sa
+from sqlalchemy import orm
+
+from capellacollab.projects.toolmodels import models as toolmodels_models
+from capellacollab.users import models as users_models
+
+from . import models
+
+
+def create_project_provisioning(
+    db: orm.Session, model: models.DatabaseModelProvisioning
+):
+    db.add(model)
+    db.commit()
+
+
+def get_project_provisioning(
+    db: orm.Session,
+    tool_model: toolmodels_models.DatabaseToolModel,
+    user: users_models.DatabaseUser,
+) -> models.DatabaseModelProvisioning | None:
+    return db.execute(
+        sa.select(models.DatabaseModelProvisioning)
+        .where(models.DatabaseModelProvisioning.tool_model == tool_model)
+        .where(models.DatabaseModelProvisioning.user == user)
+    ).scalar_one_or_none()
+
+
+def delete_project_provisioning(
+    db: orm.Session, provisioning: models.DatabaseModelProvisioning
+):
+    db.delete(provisioning)
+    db.commit()
diff --git a/backend/capellacollab/projects/toolmodels/provisioning/injectables.py b/backend/capellacollab/projects/toolmodels/provisioning/injectables.py
new file mode 100644
index 0000000000..081c46044d
--- /dev/null
+++ b/backend/capellacollab/projects/toolmodels/provisioning/injectables.py
@@ -0,0 +1,28 @@
+# SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
+# SPDX-License-Identifier: Apache-2.0
+
+
+import fastapi
+from sqlalchemy import orm
+
+from capellacollab.core import database
+from capellacollab.users import injectables as users_injectables
+from capellacollab.users import models as users_models
+
+from .. import injectables as toolmodels_injectables
+from .. import models as toolmodels_models
+from . import crud, models
+
+
+def get_model_provisioning(
+    model: toolmodels_models.DatabaseToolModel = fastapi.Depends(
+        toolmodels_injectables.get_existing_capella_model
+    ),
+    current_user: users_models.DatabaseUser = fastapi.Depends(
+        users_injectables.get_own_user
+    ),
+    db: orm.Session = fastapi.Depends(database.get_db),
+) -> models.DatabaseModelProvisioning | None:
+    return crud.get_project_provisioning(
+        db, tool_model=model, user=current_user
+    )
diff --git a/backend/capellacollab/projects/toolmodels/provisioning/models.py b/backend/capellacollab/projects/toolmodels/provisioning/models.py
new file mode 100644
index 0000000000..f1ec9aed53
--- /dev/null
+++ b/backend/capellacollab/projects/toolmodels/provisioning/models.py
@@ -0,0 +1,60 @@
+# SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
+# SPDX-License-Identifier: Apache-2.0
+
+import datetime
+
+import sqlalchemy as sa
+from sqlalchemy import orm
+
+from capellacollab.core import database
+from capellacollab.core import pydantic as core_pydantic
+from capellacollab.projects.toolmodels import (
+    models as projects_toolmodels_models,
+)
+from capellacollab.sessions import models as sessions_models
+from capellacollab.users import models as users_models
+
+
+class ModelProvisioning(core_pydantic.BaseModel):
+    session: sessions_models.Session | None
+    provisioned_at: datetime.datetime
+    revision: datetime.datetime
+
+
+class DatabaseModelProvisioning(database.Base):
+    __tablename__ = "model_provisioning"
+
+    id: orm.Mapped[int] = orm.mapped_column(
+        init=False, primary_key=True, index=True
+    )
+
+    user_id: orm.Mapped[int] = orm.mapped_column(
+        sa.ForeignKey("users.id"),
+        init=False,
+    )
+    user: orm.Mapped[users_models.DatabaseUser] = orm.relationship(
+        foreign_keys=[user_id]
+    )
+
+    tool_model_id: orm.Mapped[int] = orm.mapped_column(
+        sa.ForeignKey("models.id"),
+        init=False,
+    )
+    tool_model: orm.Mapped[projects_toolmodels_models.DatabaseToolModel] = (
+        orm.relationship(
+            foreign_keys=[tool_model_id],
+        )
+    )
+
+    revision: orm.Mapped[str]
+    commit_hash: orm.Mapped[str]
+
+    provisioned_at: orm.Mapped[datetime.datetime] = orm.mapped_column(
+        default=datetime.datetime.now(datetime.UTC)
+    )
+
+    session: orm.Mapped[sessions_models.DatabaseSession | None] = (
+        orm.relationship(
+            uselist=False, back_populates="provisioning", default=None
+        )
+    )
diff --git a/backend/capellacollab/projects/toolmodels/provisioning/routes.py b/backend/capellacollab/projects/toolmodels/provisioning/routes.py
new file mode 100644
index 0000000000..4cab896373
--- /dev/null
+++ b/backend/capellacollab/projects/toolmodels/provisioning/routes.py
@@ -0,0 +1,44 @@
+# SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
+# SPDX-License-Identifier: Apache-2.0
+
+import fastapi
+from sqlalchemy import orm
+
+from capellacollab.core import database
+from capellacollab.core.authentication import injectables as auth_injectables
+from capellacollab.projects.users import models as projects_users_models
+
+from . import crud, injectables, models
+
+router = fastapi.APIRouter(
+    dependencies=[
+        fastapi.Depends(
+            auth_injectables.ProjectRoleVerification(
+                required_role=projects_users_models.ProjectUserRole.USER
+            )
+        )
+    ],
+)
+
+
+@router.get("", response_model=models.ModelProvisioning)
+def get_provisioning(
+    provisioning: models.DatabaseModelProvisioning = fastapi.Depends(
+        injectables.get_model_provisioning
+    ),
+) -> models.DatabaseModelProvisioning:
+    return provisioning
+
+
+@router.delete("", status_code=204)
+def reset_provisioning(
+    provisioning: models.DatabaseModelProvisioning = fastapi.Depends(
+        injectables.get_model_provisioning
+    ),
+    db: orm.Session = fastapi.Depends(database.get_db),
+):
+    """This will delete the provisioning data from the workspace.
+    During the next session request, the existing provisioning will be overwritten in the workspace.
+    """
+
+    crud.delete_project_provisioning(db, provisioning)
diff --git a/backend/capellacollab/projects/toolmodels/routes.py b/backend/capellacollab/projects/toolmodels/routes.py
index 43ea85ccf6..8b2f5fcf4d 100644
--- a/backend/capellacollab/projects/toolmodels/routes.py
+++ b/backend/capellacollab/projects/toolmodels/routes.py
@@ -23,6 +23,7 @@
 from .diagrams import routes as diagrams_routes
 from .modelbadge import routes as complexity_badge_routes
 from .modelsources import routes as modelsources_routes
+from .provisioning import routes as provisioning_routes
 from .restrictions import routes as restrictions_routes
 
 router = fastapi.APIRouter(
@@ -267,3 +268,8 @@ def raise_if_model_exists_in_project(
     prefix="/{model_slug}/badges/complexity",
     tags=["Projects - Models - Model complexity badge"],
 )
+router.include_router(
+    provisioning_routes.router,
+    prefix="/{model_slug}/provisioning",
+    tags=["Projects - Models - Provisioning"],
+)
diff --git a/backend/capellacollab/sessions/hooks/guacamole.py b/backend/capellacollab/sessions/hooks/guacamole.py
index dce8f001c6..4ea12c157c 100644
--- a/backend/capellacollab/sessions/hooks/guacamole.py
+++ b/backend/capellacollab/sessions/hooks/guacamole.py
@@ -12,9 +12,6 @@
 
 from capellacollab.config import config
 from capellacollab.core import credentials
-from capellacollab.sessions import models as sessions_models
-from capellacollab.sessions.operators import k8s
-from capellacollab.tools import models as tools_models
 
 from . import interface
 
@@ -40,14 +37,11 @@ class GuacamoleIntegration(interface.HookRegistration):
         "https": None,
     }
 
-    def post_session_creation_hook(  # type: ignore[override]
+    def post_session_creation_hook(
         self,
-        session: k8s.Session,
-        db_session: sessions_models.DatabaseSession,
-        connection_method: tools_models.ToolSessionConnectionMethod,
-        **kwargs,
+        request: interface.PostSessionCreationHookRequest,
     ) -> interface.PostSessionCreationHookResult:
-        if connection_method.type != "guacamole":
+        if request.connection_method.type != "guacamole":
             return interface.PostSessionCreationHookResult()
 
         guacamole_username = credentials.generate_password()
@@ -60,9 +54,9 @@ def post_session_creation_hook(  # type: ignore[override]
 
         guacamole_identifier = self._create_connection(
             guacamole_token,
-            db_session.environment["CAPELLACOLLAB_SESSION_TOKEN"],
-            session["host"],
-            session["port"],
+            request.db_session.environment["CAPELLACOLLAB_SESSION_TOKEN"],
+            request.session["host"],
+            request.session["port"],
         )["identifier"]
 
         self._assign_user_to_connection(
@@ -79,16 +73,14 @@ def post_session_creation_hook(  # type: ignore[override]
             config=guacamole_config,
         )
 
-    def session_connection_hook(  # type: ignore[override]
+    def session_connection_hook(
         self,
-        db_session: sessions_models.DatabaseSession,
-        connection_method: tools_models.ToolSessionConnectionMethod,
-        **kwargs,
+        request: interface.SessionConnectionHookRequest,
     ) -> interface.SessionConnectionHookResult:
-        if connection_method.type != "guacamole":
+        if request.connection_method.type != "guacamole":
             return interface.SessionConnectionHookResult()
 
-        session_config = db_session.config
+        session_config = request.db_session.config
 
         if not session_config or not session_config.get("guacamole_username"):
             return interface.SessionConnectionHookResult()
@@ -102,16 +94,13 @@ def session_connection_hook(  # type: ignore[override]
             redirect_url=config.extensions.guacamole.public_uri + "/#/",
         )
 
-    def pre_session_termination_hook(  # type: ignore[override]
-        self,
-        session: sessions_models.DatabaseSession,
-        connection_method: tools_models.ToolSessionConnectionMethod,
-        **kwargs,
+    def pre_session_termination_hook(
+        self, request: interface.PreSessionTerminationHookRequest
     ) -> interface.PreSessionTerminationHookResult:
-        if connection_method.type != "guacamole":
+        if request.connection_method.type != "guacamole":
             return interface.SessionConnectionHookResult()
 
-        session_config = session.config
+        session_config = request.session.config
 
         if session_config and session_config.get("guacamole_username"):
             guacamole_token = self._get_admin_token()
diff --git a/backend/capellacollab/sessions/hooks/http.py b/backend/capellacollab/sessions/hooks/http.py
index ceb8422aeb..978be376f2 100644
--- a/backend/capellacollab/sessions/hooks/http.py
+++ b/backend/capellacollab/sessions/hooks/http.py
@@ -1,35 +1,28 @@
 # SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
 # SPDX-License-Identifier: Apache-2.0
 
-import logging
-
 from capellacollab.core import models as core_models
 from capellacollab.tools import models as tools_models
 
-from .. import models as sessions_models
 from .. import util as sessions_util
 from . import interface
 
 
 class HTTPIntegration(interface.HookRegistration):
-    def session_connection_hook(  # type: ignore[override]
-        self,
-        db_session: sessions_models.DatabaseSession,
-        connection_method: tools_models.ToolSessionConnectionMethod,
-        logger: logging.LoggerAdapter,
-        **kwargs,
+    def session_connection_hook(
+        self, request: interface.SessionConnectionHookRequest
     ) -> interface.SessionConnectionHookResult:
         if not isinstance(
-            connection_method, tools_models.HTTPConnectionMethod
+            request.connection_method, tools_models.HTTPConnectionMethod
         ):
             return interface.SessionConnectionHookResult()
 
         try:
-            redirect_url = connection_method.redirect_url.format(
-                **db_session.environment
+            redirect_url = request.connection_method.redirect_url.format(
+                **request.db_session.environment
             )
         except Exception:
-            logger.error(
+            request.logger.error(
                 "Error while formatting the redirect URL", exc_info=True
             )
             return interface.SessionConnectionHookResult(
@@ -43,12 +36,14 @@ def session_connection_hook(  # type: ignore[override]
             )
 
         cookies, warnings = sessions_util.resolve_environment_variables(
-            logger, db_session.environment, connection_method.cookies
+            request.logger,
+            request.db_session.environment,
+            request.connection_method.cookies,
         )
 
         # Set token for pre-authentication
         cookies |= {
-            "ccm_session_token": db_session.environment[
+            "ccm_session_token": request.db_session.environment[
                 "CAPELLACOLLAB_SESSION_TOKEN"
             ]
         }
diff --git a/backend/capellacollab/sessions/hooks/interface.py b/backend/capellacollab/sessions/hooks/interface.py
index fefbd60f4a..80dd6582ed 100644
--- a/backend/capellacollab/sessions/hooks/interface.py
+++ b/backend/capellacollab/sessions/hooks/interface.py
@@ -2,6 +2,7 @@
 # SPDX-License-Identifier: Apache-2.0
 
 import abc
+import dataclasses
 import logging
 import typing as t
 
@@ -17,6 +18,43 @@
 from .. import models as sessions_models
 
 
+@dataclasses.dataclass()
+class ConfigurationHookRequest:
+    """Request type of the configuration hook
+
+    Attributes
+    ----------
+    db : sqlalchemy.orm.Session
+        Database session. Can be used to access the database
+    operator : operators.KubernetesOperator
+        Operator, which is used to spawn the session
+    user : users_models.DatabaseUser
+        User who has requested the session
+    tool : tools_models.DatabaseTool
+        Tool of the requested session
+    tool_version : tools_models.DatabaseVersion
+        Tool version of the requested session
+    session_type : sessions_models.SessionType
+        Type of the session (persistent, read-only, etc.)
+    connection_method : tools_models.ToolSessionConnectionMethod
+        Requested connection method for the session
+    provisioning : list[sessions_models.SessionProvisioningRequest]
+        List of workspace provisioning requests
+    session_id: str
+        ID of the session to be created
+    """
+
+    db: orm.Session
+    operator: operators.KubernetesOperator
+    user: users_models.DatabaseUser
+    tool: tools_models.DatabaseTool
+    tool_version: tools_models.DatabaseVersion
+    session_type: sessions_models.SessionType
+    connection_method: tools_models.ToolSessionConnectionMethod
+    provisioning: list[sessions_models.SessionProvisioningRequest]
+    session_id: str
+
+
 class ConfigurationHookResult(t.TypedDict):
     """Return type of the configuration hook
 
@@ -42,6 +80,34 @@ class ConfigurationHookResult(t.TypedDict):
     init_environment: t.NotRequired[t.Mapping]
 
 
+@dataclasses.dataclass()
+class PostSessionCreationHookRequest:
+    """Request type of the post session creation hook
+
+    Attributes
+    ----------
+    session_id : str
+        ID of the session
+    session : k8s.Session
+        Session object (contains connection information)
+    db_session : sessions_models.DatabaseSession
+        Collaboration Manager session in the database
+    operator : operators.KubernetesOperator
+        Operator, which is used to spawn the session
+    user : users_models.DatabaseUser
+        User who has requested the session
+    connection_method : tools_models.ToolSessionConnectionMethod
+        Requested connection method for the session
+    """
+
+    session_id: str
+    session: k8s.Session
+    db_session: sessions_models.DatabaseSession
+    operator: operators.KubernetesOperator
+    user: users_models.DatabaseUser
+    connection_method: tools_models.ToolSessionConnectionMethod
+
+
 class PostSessionCreationHookResult(t.TypedDict):
     """Return type of the post session creation hook
 
@@ -55,6 +121,31 @@ class PostSessionCreationHookResult(t.TypedDict):
     config: t.NotRequired[t.Mapping]
 
 
+@dataclasses.dataclass()
+class SessionConnectionHookRequest:
+    """Request type of the session connection hook
+
+    Attributes
+    ----------
+    db : sqlalchemy.orm.Session
+        Database session. Can be used to access the database
+    db_session : sessions_models.DatabaseSession
+        Collaboration Manager session in the database
+    connection_method : tools_models.ToolSessionConnectionMethod
+        Connection method of the session
+    logger : logging.LoggerAdapter
+        Logger for the specific request
+    user : users_models.DatabaseUser
+        User who is connecting to the session
+    """
+
+    db: orm.Session
+    db_session: sessions_models.DatabaseSession
+    connection_method: tools_models.ToolSessionConnectionMethod
+    logger: logging.LoggerAdapter
+    user: users_models.DatabaseUser
+
+
 class SessionConnectionHookResult(t.TypedDict):
     """Return type of the session connection hook
 
@@ -80,6 +171,28 @@ class SessionConnectionHookResult(t.TypedDict):
     warnings: t.NotRequired[list[core_models.Message]]
 
 
+@dataclasses.dataclass()
+class PreSessionTerminationHookRequest:
+    """Request type of the pre session termination hook
+
+    Attributes
+    ----------
+    db : sqlalchemy.orm.Session
+        Database session. Can be used to access the database
+    operator : operators.KubernetesOperator
+        Operator, which is used to spawn the session
+    session : sessions_models.DatabaseSession
+        Session which is to be terminated
+    connection_method : tools_models.ToolSessionConnectionMethod
+        Connection method of the session
+    """
+
+    db: orm.Session
+    operator: operators.KubernetesOperator
+    session: sessions_models.DatabaseSession
+    connection_method: tools_models.ToolSessionConnectionMethod
+
+
 class PreSessionTerminationHookResult(t.TypedDict):
     """Return type of the pre session termination hook"""
 
@@ -101,143 +214,47 @@ class HookRegistration(metaclass=abc.ABCMeta):
 
     # pylint: disable=unused-argument
     def configuration_hook(
-        self,
-        db: orm.Session,
-        operator: operators.KubernetesOperator,
-        user: users_models.DatabaseUser,
-        tool: tools_models.DatabaseTool,
-        tool_version: tools_models.DatabaseVersion,
-        session_type: sessions_models.SessionType,
-        connection_method: tools_models.ToolSessionConnectionMethod,
-        provisioning: list[sessions_models.SessionProvisioningRequest],
-        session_id: str,
-        **kwargs,
+        self, request: ConfigurationHookRequest
     ) -> ConfigurationHookResult:
         """Hook to determine session configuration
 
         This hook is executed before the creation of persistent sessions.
-
-        Parameters
-        ----------
-        db : sqlalchemy.orm.Session
-            Database session. Can be used to access the database
-        operator : operators.KubernetesOperator
-            Operator, which is used to spawn the session
-        user : users_models.DatabaseUser
-            User who has requested the session
-        tool : tools_models.DatabaseTool
-            Tool of the requested session
-        tool_version : tools_models.DatabaseVersion
-            Tool version of the requested session
-        session_type : sessions_models.SessionType
-            Type of the session (persistent, read-only, etc.)
-        connection_method : tools_models.ToolSessionConnectionMethod
-            Requested connection method for the session
-        provisioning : list[sessions_models.SessionProvisioningRequest]
-            List of workspace provisioning requests
-        session_id: str
-            ID of the session to be created
-        Returns
-        -------
-        result : ConfigurationHookResult
         """
 
         return ConfigurationHookResult()
 
+    # pylint: disable=unused-argument
     def post_session_creation_hook(
         self,
-        session_id: str,
-        session: k8s.Session,
-        db_session: sessions_models.DatabaseSession,
-        operator: operators.KubernetesOperator,
-        user: users_models.DatabaseUser,
-        connection_method: tools_models.ToolSessionConnectionMethod,
-        **kwargs,
+        request: PostSessionCreationHookRequest,
     ) -> PostSessionCreationHookResult:
         """Hook executed after session creation
 
         This hook is executed after a persistent session was created
         by the operator.
-
-        Parameters
-        ----------
-        session_id : str
-            ID of the session
-        session : k8s.Session
-            Session object (contains connection information)
-        db_session : sessions_models.DatabaseSession
-            Collaboration Manager session in the database
-        operator : operators.KubernetesOperator
-            Operator, which is used to spawn the session
-        user : users_models.DatabaseUser
-            User who has requested the session
-        connection_method : tools_models.ToolSessionConnectionMethod
-            Requested connection method for the session
-
-        Returns
-        -------
-        result : PostSessionCreationHookResult
         """
 
         return PostSessionCreationHookResult()
 
     # pylint: disable=unused-argument
     def session_connection_hook(
-        self,
-        db: orm.Session,
-        db_session: sessions_models.DatabaseSession,
-        connection_method: tools_models.ToolSessionConnectionMethod,
-        logger: logging.LoggerAdapter,
-        **kwargs,
+        self, request: SessionConnectionHookRequest
     ) -> SessionConnectionHookResult:
         """Hook executed while connecting to a session
 
         The hook is executed each time the
         GET `/sessions/{session_id}/connection` endpoint is called.
-
-        Parameters
-        ----------
-        db : sqlalchemy.orm.Session
-            Database session. Can be used to access the database
-        db_session : sessions_models.DatabaseSession
-            Collaboration Manager session in the database
-        connection_method : tools_models.ToolSessionConnectionMethod
-            Connection method of the session
-        logger : logging.LoggerAdapter
-            Logger for the specific request
-        Returns
-        -------
-        result : SessionConnectionHookResult
         """
 
         return SessionConnectionHookResult()
 
+    # pylint: disable=unused-argument
     def pre_session_termination_hook(
-        self,
-        db: orm.Session,
-        operator: operators.KubernetesOperator,
-        session: sessions_models.DatabaseSession,
-        connection_method: tools_models.ToolSessionConnectionMethod,
-        **kwargs,
+        self, request: PreSessionTerminationHookRequest
     ) -> PreSessionTerminationHookResult:
         """Hook executed directly before session termination
 
         This hook is executed before a read-only or persistent session
         is terminated by the operator.
-
-        Parameters
-        ----------
-        db : sqlalchemy.orm.Session
-            Database session. Can be used to access the database
-        operator : operators.KubernetesOperator
-            Operator, which is used to spawn the session
-        session : sessions_models.DatabaseSession
-            Session which is to be terminated
-        connection_method : tools_models.ToolSessionConnectionMethod
-            Connection method of the session
-
-        Returns
-        -------
-        result : PreSessionTerminationHookResult
         """
         return PreSessionTerminationHookResult()
diff --git a/backend/capellacollab/sessions/hooks/jupyter.py b/backend/capellacollab/sessions/hooks/jupyter.py
index 4e0fa7050a..5e208291da 100644
--- a/backend/capellacollab/sessions/hooks/jupyter.py
+++ b/backend/capellacollab/sessions/hooks/jupyter.py
@@ -14,7 +14,6 @@
 from capellacollab.sessions import operators
 from capellacollab.sessions.operators import models as operators_models
 from capellacollab.tools import models as tools_models
-from capellacollab.users import models as users_models
 
 from . import interface
 
@@ -22,16 +21,12 @@
 
 
 class JupyterIntegration(interface.HookRegistration):
-    def configuration_hook(  # type: ignore[override]
+    def configuration_hook(
         self,
-        db: orm.Session,
-        user: users_models.DatabaseUser,
-        tool: tools_models.DatabaseTool,
-        operator: operators.KubernetesOperator,
-        **kwargs,
+        request: interface.ConfigurationHookRequest,
     ) -> interface.ConfigurationHookResult:
         volumes, warnings = self._get_project_share_volume_mounts(
-            db, user.name, tool, operator
+            request.db, request.user.name, request.tool, request.operator
         )
         return interface.ConfigurationHookResult(
             volumes=volumes, warnings=warnings
diff --git a/backend/capellacollab/sessions/hooks/networking.py b/backend/capellacollab/sessions/hooks/networking.py
index fb0a36ca6e..2561d68564 100644
--- a/backend/capellacollab/sessions/hooks/networking.py
+++ b/backend/capellacollab/sessions/hooks/networking.py
@@ -2,37 +2,26 @@
 # SPDX-License-Identifier: Apache-2.0
 
 
-from capellacollab.sessions import operators
-from capellacollab.users import models as users_models
-
-from .. import models as sessions_models
 from . import interface
 
 
 class NetworkingIntegration(interface.HookRegistration):
     """Allow sessions of the same user to talk to each other."""
 
-    def post_session_creation_hook(  # type: ignore
-        self,
-        session_id: str,
-        operator: operators.KubernetesOperator,
-        user: users_models.DatabaseUser,
-        **kwargs,
+    def post_session_creation_hook(
+        self, request: interface.PostSessionCreationHookRequest
     ) -> interface.PostSessionCreationHookResult:
         """Allow sessions of the user to talk to each other."""
 
-        operator.create_network_policy_from_pod_to_label(
-            session_id,
-            {"capellacollab/session-id": session_id},
-            {"capellacollab/owner-id": str(user.id)},
+        request.operator.create_network_policy_from_pod_to_label(
+            request.session_id,
+            {"capellacollab/session-id": request.session_id},
+            {"capellacollab/owner-id": str(request.user.id)},
         )
 
         return interface.PostSessionCreationHookResult()
 
-    def pre_session_termination_hook(  # type: ignore
-        self,
-        operator: operators.KubernetesOperator,
-        session: sessions_models.DatabaseSession,
-        **kwargs,
+    def pre_session_termination_hook(
+        self, request: interface.PreSessionTerminationHookRequest
     ):
-        operator.delete_network_policy(session.id)
+        request.operator.delete_network_policy(request.session.id)
diff --git a/backend/capellacollab/sessions/hooks/persistent_workspace.py b/backend/capellacollab/sessions/hooks/persistent_workspace.py
index 5086f57a79..98e7ab37ed 100644
--- a/backend/capellacollab/sessions/hooks/persistent_workspace.py
+++ b/backend/capellacollab/sessions/hooks/persistent_workspace.py
@@ -2,7 +2,6 @@
 # SPDX-License-Identifier: Apache-2.0
 
 import pathlib
-import typing as t
 import uuid
 
 from sqlalchemy import orm
@@ -19,32 +18,25 @@
 from . import interface
 
 
-class PersistentWorkspacEnvironment(t.TypedDict):
-    pass
-
-
 class PersistentWorkspaceHook(interface.HookRegistration):
     """Takes care of the persistent workspace of a user.
 
     Is responsible for mounting the persistent workspace into persistent sessions.
     """
 
-    def configuration_hook(  # type: ignore
+    def configuration_hook(
         self,
-        db: orm.Session,
-        operator: operators.KubernetesOperator,
-        user: users_models.DatabaseUser,
-        session_type: sessions_models.SessionType,
-        tool: tools_models.DatabaseTool,
-        **kwargs,
+        request: interface.ConfigurationHookRequest,
     ) -> interface.ConfigurationHookResult:
-        if session_type == sessions_models.SessionType.READONLY:
+        if request.session_type == sessions_models.SessionType.READONLY:
             # Skip read-only sessions, no persistent workspace needed.
             return interface.ConfigurationHookResult()
 
-        self._check_that_persistent_workspace_is_allowed(tool)
+        self._check_that_persistent_workspace_is_allowed(request.tool)
 
-        volume_name = self._create_persistent_workspace(db, operator, user)
+        volume_name = self._create_persistent_workspace(
+            request.db, request.operator, request.user
+        )
         volume = operators_models.PersistentVolume(
             name="workspace",
             read_only=False,
@@ -53,7 +45,7 @@ def configuration_hook(  # type: ignore
         )
 
         return interface.ConfigurationHookResult(
-            volumes=[volume],
+            volumes=[volume], init_volumes=[volume]
         )
 
     def _check_that_persistent_workspace_is_allowed(
diff --git a/backend/capellacollab/sessions/hooks/provisioning.py b/backend/capellacollab/sessions/hooks/provisioning.py
index 4d1b92c902..4b2a51def5 100644
--- a/backend/capellacollab/sessions/hooks/provisioning.py
+++ b/backend/capellacollab/sessions/hooks/provisioning.py
@@ -19,9 +19,19 @@
 from capellacollab.projects.toolmodels.modelsources.git import (
     models as git_models,
 )
+from capellacollab.projects.toolmodels.provisioning import (
+    crud as provisioning_crud,
+)
+from capellacollab.projects.toolmodels.provisioning import (
+    models as provisioning_models,
+)
 from capellacollab.projects.users import models as projects_users_models
 from capellacollab.sessions import exceptions as sessions_exceptions
 from capellacollab.sessions import models as sessions_models
+from capellacollab.settings.modelsources.git import core as instances_git_core
+from capellacollab.settings.modelsources.git import (
+    exceptions as instances_git_exceptions,
+)
 from capellacollab.tools import crud as tools_crud
 from capellacollab.tools import models as tools_models
 from capellacollab.users import models as users_models
@@ -40,37 +50,75 @@ class ProvisionWorkspaceHook(interface.HookRegistration):
     """Takes care of the provisioning of user workspaces."""
 
     @classmethod
-    def configuration_hook(  # type: ignore
+    async def async_configuration_hook(
         cls,
-        db: orm.Session,
-        tool: tools_models.DatabaseTool,
-        tool_version: tools_models.DatabaseVersion,
-        user: users_models.DatabaseUser,
-        provisioning: list[sessions_models.SessionProvisioningRequest],
-        **kwargs,
+        request: interface.ConfigurationHookRequest,
     ) -> interface.ConfigurationHookResult:
-        max_number_of_models = tool.config.provisioning.max_number_of_models
-        if max_number_of_models and len(provisioning) > max_number_of_models:
+        max_number_of_models = (
+            request.tool.config.provisioning.max_number_of_models
+        )
+        if (
+            max_number_of_models
+            and len(request.provisioning) > max_number_of_models
+        ):
             raise sessions_exceptions.TooManyModelsRequestedToProvisionError(
                 max_number_of_models
             )
 
-        resolved_entries = cls._resolve_provisioning_request(db, provisioning)
+        resolved_entries = cls._resolve_provisioning_request(
+            request.db, request.provisioning
+        )
         cls._verify_matching_tool_version_and_model(
-            db, tool_version, resolved_entries
+            request.db, request.tool_version, resolved_entries
+        )
+        cls._verify_model_permissions(
+            request.db, request.user, resolved_entries
         )
-        cls._verify_model_permissions(db, user, resolved_entries)
 
-        init_environment = {
-            "CAPELLACOLLAB_PROVISIONING": cls._get_git_repos_json(
-                resolved_entries, include_credentials=True
+        if request.session_type == sessions_models.SessionType.PERSISTENT:
+            if len(request.provisioning) > 1:
+                raise sessions_exceptions.TooManyModelsRequestedToProvisionError(
+                    1
+                )
+
+            if len(request.provisioning) == 0:
+                return interface.ConfigurationHookResult()
+
+            resolved_entry = resolved_entries[0]
+
+            if not provisioning_crud.get_project_provisioning(
+                request.db, resolved_entry["model"], request.user
+            ):
+                await cls._create_provisioning_record(
+                    request.db,
+                    resolved_entry,
+                    request.user,
+                )
+
+            environment["WORKSPACE_DIR"] = (
+                pathlib.PurePosixPath("/workspace")
+                / resolved_entry["project"].slug
+                / resolved_entry["model"].slug
             )
+
+        git_repos_with_credentials = cls._get_git_repos_json(
+            resolved_entries,
+            request.session_type,
+            include_credentials=True,
+        )
+
+        git_repos_without_credentials = cls._get_git_repos_json(
+            resolved_entries,
+            request.session_type,
+            include_credentials=False,
+        )
+
+        init_environment = {
+            "CAPELLACOLLAB_PROVISIONING": git_repos_with_credentials
         }
 
         environment = {
-            "CAPELLACOLLAB_SESSION_PROVISIONING": cls._get_git_repos_json(
-                resolved_entries, include_credentials=False
-            )
+            "CAPELLACOLLAB_SESSION_PROVISIONING": git_repos_without_credentials
         }
 
         return interface.ConfigurationHookResult(
@@ -143,14 +191,16 @@ def _verify_model_permissions(
     def _get_git_repos_json(
         cls,
         resolved_entries: list[ResolvedSessionProvisioning],
+        session_type: sessions_models.SessionType,
         include_credentials: bool = False,
     ):
         """Get the git repos as a JSON-serializable list"""
         return [
             cls._git_model_as_json(
                 entry["git_model"],
-                entry["entry"].revision,
+                entry["entry"].revision or entry["git_model"].revision,
                 entry["entry"].deep_clone,
+                session_type,
                 include_credentials,
             )
             for entry in resolved_entries
@@ -162,6 +212,7 @@ def _git_model_as_json(
         git_model: git_models.DatabaseGitModel,
         revision: str,
         deep_clone: bool,
+        session_type: sessions_models.SessionType,
         include_credentials: bool,
     ) -> dict[str, str | int]:
         """Convert a DatabaseGitModel to a JSON-serializable dictionary."""
@@ -178,6 +229,8 @@ def _git_model_as_json(
             "path": str(
                 pathlib.PurePosixPath(
                     toolmodel.tool.config.provisioning.directory
+                    if session_type == sessions_models.SessionType.READONLY
+                    else "/workspace"
                 )
                 / toolmodel.project.slug
                 / toolmodel.slug
@@ -187,3 +240,39 @@ def _git_model_as_json(
             git_dict["username"] = git_model.username
             git_dict["password"] = git_model.password
         return git_dict
+
+    @classmethod
+    async def _determine_commit_hash(
+        cls, revision: str | None, git_model: git_models.DatabaseGitModel
+    ) -> tuple[str, str]:
+        revision = revision or git_model.revision
+        for hash, rev in await instances_git_core.ls_remote(
+            url=git_model.path,
+            username=git_model.username,
+            password=git_model.password,
+        ):
+            rev = rev.removeprefix("refs/heads/").removeprefix("refs/tags/")
+            if rev == revision:
+                return revision, hash
+
+        raise instances_git_exceptions.RevisionNotFoundError(revision)
+
+    @classmethod
+    async def _create_provisioning_record(
+        cls,
+        db: orm.Session,
+        resolved_entry: ResolvedSessionProvisioning,
+        user: users_models.DatabaseUser,
+    ) -> None:
+        rev, hash = await cls._determine_commit_hash(
+            resolved_entry["entry"].revision, resolved_entry["git_model"]
+        )
+        provisioning_crud.create_project_provisioning(
+            db,
+            provisioning_models.DatabaseModelProvisioning(
+                user=user,
+                tool_model=resolved_entry["model"],
+                revision=rev,
+                commit_hash=hash,
+            ),
+        )
diff --git a/backend/capellacollab/sessions/hooks/pure_variants.py b/backend/capellacollab/sessions/hooks/pure_variants.py
index 75d8b8c19e..f0016ee620 100644
--- a/backend/capellacollab/sessions/hooks/pure_variants.py
+++ b/backend/capellacollab/sessions/hooks/pure_variants.py
@@ -5,8 +5,6 @@
 import pathlib
 import typing as t
 
-from sqlalchemy import orm
-
 from capellacollab.core import models as core_models
 from capellacollab.projects.toolmodels import models as toolmodels_models
 from capellacollab.sessions import models as sessions_models
@@ -26,20 +24,17 @@ class PureVariantsConfigEnvironment(t.TypedDict):
 
 
 class PureVariantsIntegration(interface.HookRegistration):
-    def configuration_hook(  # type: ignore
+    def configuration_hook(
         self,
-        db: orm.Session,
-        user: users_models.DatabaseUser,
-        session_type: sessions_models.SessionType,
-        **kwargs,
+        request: interface.ConfigurationHookRequest,
     ) -> interface.ConfigurationHookResult:
-        if session_type == sessions_models.SessionType.READONLY:
+        if request.session_type == sessions_models.SessionType.READONLY:
             # Skip read-only sessions, no pure::variants integration supported.
             return interface.ConfigurationHookResult()
 
         if (
-            not self._user_has_project_with_pure_variants_model(user)
-            and user.role == users_models.Role.USER
+            not self._user_has_project_with_pure_variants_model(request.user)
+            and request.user.role == users_models.Role.USER
         ):
             warnings = [
                 core_models.Message(
@@ -57,7 +52,9 @@ def configuration_hook(  # type: ignore
                 warnings=warnings,
             )
 
-        pv_license = purevariants_crud.get_pure_variants_configuration(db)
+        pv_license = purevariants_crud.get_pure_variants_configuration(
+            request.db
+        )
         if not pv_license or pv_license.license_server_url is None:
             warnings = [
                 core_models.Message(
diff --git a/backend/capellacollab/sessions/hooks/read_only_workspace.py b/backend/capellacollab/sessions/hooks/read_only_workspace.py
index 10109a4812..cbe1b45119 100644
--- a/backend/capellacollab/sessions/hooks/read_only_workspace.py
+++ b/backend/capellacollab/sessions/hooks/read_only_workspace.py
@@ -12,12 +12,10 @@
 class ReadOnlyWorkspaceHook(interface.HookRegistration):
     """Mounts an empty workspace to the container for read-only sessions."""
 
-    def configuration_hook(  # type: ignore
-        self,
-        session_type: sessions_models.SessionType,
-        **kwargs,
+    def configuration_hook(
+        self, request: interface.ConfigurationHookRequest
     ) -> interface.ConfigurationHookResult:
-        if session_type != sessions_models.SessionType.READONLY:
+        if request.session_type != sessions_models.SessionType.READONLY:
             # Configuration for persistent workspace sessions happens in the PersistentWorkspaceHook.
             return interface.ConfigurationHookResult()
 
diff --git a/backend/capellacollab/sessions/hooks/session_preparation.py b/backend/capellacollab/sessions/hooks/session_preparation.py
index 4e27ec7f4f..e81c7f53a7 100644
--- a/backend/capellacollab/sessions/hooks/session_preparation.py
+++ b/backend/capellacollab/sessions/hooks/session_preparation.py
@@ -2,39 +2,30 @@
 # SPDX-License-Identifier: Apache-2.0
 
 import pathlib
-import typing as t
 
 from capellacollab.sessions import models as sessions_models
 from capellacollab.sessions.operators import models as operators_models
-from capellacollab.tools import models as tools_models
 
 from . import interface
 
 
-class PersistentWorkspacEnvironment(t.TypedDict):
-    pass
-
-
 class GitRepositoryCloningHook(interface.HookRegistration):
     """Creates a volume that is shared between the actual container and the session preparation.
 
     The volume is used to clone Git repositories as preparation for the session.
     """
 
-    def configuration_hook(  # type: ignore
+    def configuration_hook(
         self,
-        session_type: sessions_models.SessionType,
-        session_id: str,
-        tool: tools_models.DatabaseTool,
-        **kwargs,
+        request: interface.ConfigurationHookRequest,
     ) -> interface.ConfigurationHookResult:
-        if session_type != sessions_models.SessionType.READONLY:
+        if request.session_type != sessions_models.SessionType.READONLY:
             return interface.ConfigurationHookResult()
 
         shared_model_volume = operators_models.EmptyVolume(
-            name=f"{session_id}-models",
+            name=f"{request.session_id}-models",
             container_path=pathlib.PurePosixPath(
-                tool.config.provisioning.directory
+                request.tool.config.provisioning.directory
             ),
             read_only=False,
         )
diff --git a/backend/capellacollab/sessions/hooks/t4c.py b/backend/capellacollab/sessions/hooks/t4c.py
index e6ffd4a884..92f03ba81e 100644
--- a/backend/capellacollab/sessions/hooks/t4c.py
+++ b/backend/capellacollab/sessions/hooks/t4c.py
@@ -17,7 +17,6 @@
 from capellacollab.settings.modelsources.t4c.instance.repositories import (
     interface as repo_interface,
 )
-from capellacollab.tools import models as tools_models
 from capellacollab.users import models as users_models
 
 from .. import models as sessions_models
@@ -34,22 +33,19 @@ class T4CConfigEnvironment(t.TypedDict):
 
 
 class T4CIntegration(interface.HookRegistration):
-    def configuration_hook(  # type: ignore
-        self,
-        db: orm.Session,
-        user: users_models.DatabaseUser,
-        tool_version: tools_models.DatabaseVersion,
-        session_type: sessions_models.SessionType,
-        **kwargs,
+    def configuration_hook(
+        self, request: interface.ConfigurationHookRequest
     ) -> interface.ConfigurationHookResult:
-        if session_type != sessions_models.SessionType.PERSISTENT:
+        user = request.user
+
+        if request.session_type != sessions_models.SessionType.PERSISTENT:
             # Skip non-persistent sessions, no T4C integration needed.
             return interface.ConfigurationHookResult()
 
         warnings: list[core_models.Message] = []
 
         t4c_repositories = repo_crud.get_user_t4c_repositories(
-            db, tool_version, user
+            request.db, request.tool_version, user
         )
 
         t4c_json = json.dumps(
@@ -91,7 +87,7 @@ def configuration_hook(  # type: ignore
                     password=environment["T4C_PASSWORD"],
                     is_admin=auth_injectables.RoleVerification(
                         required_role=users_models.Role.ADMIN, verify=False
-                    )(user.name, db),
+                    )(user.name, request.db),
                 )
             except requests.RequestException:
                 warnings.append(
@@ -116,30 +112,25 @@ def configuration_hook(  # type: ignore
             environment=environment, warnings=warnings
         )
 
-    def pre_session_termination_hook(  # type: ignore
-        self,
-        db: orm.Session,
-        session: sessions_models.DatabaseSession,
-        **kwargs,
+    def pre_session_termination_hook(
+        self, request: interface.PreSessionTerminationHookRequest
     ):
-        if session.type == sessions_models.SessionType.PERSISTENT:
-            self._revoke_session_tokens(db, session)
+        if request.session.type == sessions_models.SessionType.PERSISTENT:
+            self._revoke_session_tokens(request.db, request.session)
 
-    def session_connection_hook(  # type: ignore[override]
+    def session_connection_hook(
         self,
-        db_session: sessions_models.DatabaseSession,
-        user: users_models.DatabaseUser,
-        **kwargs,
+        request: interface.SessionConnectionHookRequest,
     ) -> interface.SessionConnectionHookResult:
-        if db_session.type != sessions_models.SessionType.PERSISTENT:
+        if request.db_session.type != sessions_models.SessionType.PERSISTENT:
             return interface.SessionConnectionHookResult()
 
-        if db_session.owner != user:
+        if request.db_session.owner != request.user:
             # The session is shared, don't provide the T4C token.
             return interface.SessionConnectionHookResult()
 
         return interface.SessionConnectionHookResult(
-            t4c_token=db_session.environment.get("T4C_PASSWORD")
+            t4c_token=request.db_session.environment.get("T4C_PASSWORD")
         )
 
     def _revoke_session_tokens(
diff --git a/backend/capellacollab/sessions/models.py b/backend/capellacollab/sessions/models.py
index 566811145e..13cf665f08 100644
--- a/backend/capellacollab/sessions/models.py
+++ b/backend/capellacollab/sessions/models.py
@@ -21,6 +21,9 @@
 from . import injection
 
 if t.TYPE_CHECKING:
+    from capellacollab.projects.toolmodels.provisioning.models import (
+        DatabaseModelProvisioning,
+    )
     from capellacollab.tools.models import DatabaseTool, DatabaseVersion
     from capellacollab.users.models import DatabaseUser
 
@@ -49,7 +52,7 @@ class SessionProvisioningRequest(core_pydantic.BaseModel):
     project_slug: str
     toolmodel_slug: str = pydantic.Field(alias="model_slug")
     git_model_id: int
-    revision: str
+    revision: str | None = None
     deep_clone: bool
 
 
@@ -158,6 +161,18 @@ class DatabaseSession(database.Base):
 
     connection_method_id: orm.Mapped[str]
 
+    provisioning_id: orm.Mapped[int | None] = orm.mapped_column(
+        sa.ForeignKey("model_provisioning.id"),
+        init=False,
+    )
+    provisioning: orm.Mapped[DatabaseModelProvisioning | None] = (
+        orm.relationship(
+            back_populates="session",
+            foreign_keys=[provisioning_id],
+            default=None,
+        )
+    )
+
     environment: orm.Mapped[dict[str, str]] = orm.mapped_column(
         nullable=False, default_factory=dict
     )
diff --git a/backend/capellacollab/sessions/operators/k8s.py b/backend/capellacollab/sessions/operators/k8s.py
index 0732a48e9c..4d39f5ef67 100644
--- a/backend/capellacollab/sessions/operators/k8s.py
+++ b/backend/capellacollab/sessions/operators/k8s.py
@@ -19,6 +19,7 @@
 import kubernetes.config
 import kubernetes.stream.stream
 import prometheus_client
+import typing_extensions as te  # codespell:ignore te
 import yaml
 from kubernetes import client
 from kubernetes.client import exceptions
@@ -53,7 +54,7 @@
     )
 
 
-class Session(t.TypedDict):
+class Session(te.TypedDict):  # codespell:ignore te
     id: str
     port: int
     created_at: datetime.datetime
diff --git a/backend/capellacollab/sessions/routes.py b/backend/capellacollab/sessions/routes.py
index ee8dcf56b0..622ee7b037 100644
--- a/backend/capellacollab/sessions/routes.py
+++ b/backend/capellacollab/sessions/routes.py
@@ -18,6 +18,7 @@
 from capellacollab.core.authentication import injectables as auth_injectables
 from capellacollab.sessions import hooks
 from capellacollab.sessions.files import routes as files_routes
+from capellacollab.sessions.hooks import interface as hooks_interface
 from capellacollab.tools import exceptions as tools_exceptions
 from capellacollab.tools import injectables as tools_injectables
 from capellacollab.tools import models as tools_models
@@ -124,19 +125,26 @@ def request_session(
     init_volumes: list[operators_models.Volume] = []
     init_environment: dict[str, str] = {}
 
+    hook_results = []
+
     for hook in hooks.get_activated_integration_hooks(tool):
-        hook_result = hook.configuration_hook(
-            db=db,
-            user=user,
-            tool_version=version,
-            tool=tool,
-            username=user.name,
-            operator=operator,
-            session_type=body.session_type,
-            connection_method=connection_method,
-            provisioning=body.provisioning,
-            session_id=session_id,
+        hook_results.append(
+            hook.configuration_hook(
+                hooks_interface.ConfigurationHookRequest(
+                    db=db,
+                    user=user,
+                    tool_version=version,
+                    tool=tool,
+                    operator=operator,
+                    session_type=body.session_type,
+                    connection_method=connection_method,
+                    provisioning=body.provisioning,
+                    session_id=session_id,
+                )
+            )
         )
+
+    for hook_result in hook_results:
         environment |= hook_result.get("environment", {})
         init_environment |= hook_result.get("init_environment", {})
         volumes += hook_result.get("volumes", [])
@@ -223,12 +231,14 @@ def request_session(
     hook_config: dict[str, str] = {}
     for hook in hooks.get_activated_integration_hooks(tool):
         result = hook.post_session_creation_hook(
-            session_id=session_id,
-            operator=operator,
-            user=user,
-            session=session,
-            db_session=db_session,
-            connection_method=connection_method,
+            hooks_interface.PostSessionCreationHookRequest(
+                session_id=session_id,
+                operator=operator,
+                user=user,
+                session=session,
+                db_session=db_session,
+                connection_method=connection_method,
+            )
         )
 
         hook_config |= result.get("config", {})
@@ -366,11 +376,13 @@ def get_session_connection_information(
 
     for hook in hooks.get_activated_integration_hooks(session.tool):
         hook_result = hook.session_connection_hook(
-            db=db,
-            user=user,
-            db_session=session,
-            connection_method=connection_method,
-            logger=logger,
+            hooks_interface.SessionConnectionHookRequest(
+                db=db,
+                db_session=session,
+                connection_method=connection_method,
+                logger=logger,
+                user=user,
+            )
         )
 
         local_storage |= hook_result.get("local_storage", {})
diff --git a/backend/capellacollab/sessions/util.py b/backend/capellacollab/sessions/util.py
index 8abe6876f0..530101ea88 100644
--- a/backend/capellacollab/sessions/util.py
+++ b/backend/capellacollab/sessions/util.py
@@ -13,6 +13,7 @@
 from capellacollab.core import credentials
 from capellacollab.core import models as core_models
 from capellacollab.sessions import hooks
+from capellacollab.sessions.hooks import interface as hooks_interface
 from capellacollab.sessions.operators import k8s
 from capellacollab.tools import models as tools_models
 from capellacollab.users import models as users_models
@@ -33,11 +34,12 @@ def terminate_session(
     )
     for hook in hooks.get_activated_integration_hooks(session.tool):
         hook.pre_session_termination_hook(
-            db=db,
-            session=session,
-            operator=operator,
-            user=session.owner,
-            connection_method=connection_method,
+            hooks_interface.PreSessionTerminationHookRequest(
+                db=db,
+                session=session,
+                operator=operator,
+                connection_method=connection_method,
+            )
         )
 
     crud.delete_session(db, session)
diff --git a/backend/capellacollab/settings/modelsources/git/core.py b/backend/capellacollab/settings/modelsources/git/core.py
index ea23924ffa..6863059cb8 100644
--- a/backend/capellacollab/settings/modelsources/git/core.py
+++ b/backend/capellacollab/settings/modelsources/git/core.py
@@ -2,18 +2,19 @@
 # SPDX-License-Identifier: Apache-2.0
 
 import asyncio
-import collections.abc as cabc
 import logging
 import os
 import pathlib
 import subprocess
+import typing as t
 
 from . import exceptions, models
 
 log = logging.getLogger(__name__)
 
 
-async def ls_remote(url: str, env: cabc.Mapping[str, str]) -> list[str]:
+async def _ls_remote_command(url: str, env: t.Mapping[str, str]) -> str:
+    """Runs ls-remote on a repository and returns stdout"""
     try:
         proc = await asyncio.create_subprocess_exec(
             "git",
@@ -36,8 +37,30 @@ async def ls_remote(url: str, env: cabc.Mapping[str, str]) -> list[str]:
             raise exceptions.GitRepositoryAccessError()
         else:
             raise e
+
     stdout, _ = await proc.communicate()
-    return stdout.decode().strip().splitlines()
+    return stdout.decode()
+
+
+async def ls_remote(
+    url: str, username: str | None, password: str | None
+) -> list[tuple[str, str]]:
+    env = {
+        "GIT_USERNAME": username or "",
+        "GIT_PASSWORD": password or "",
+        "GIT_ASKPASS": str(
+            (pathlib.Path(__file__).parents[0] / "askpass.py").absolute()
+        ),
+    }
+
+    stdout = await _ls_remote_command(url, env)
+
+    resolved_revisions = []
+    for line in stdout.strip().splitlines():
+        (hash, rev) = line.split("\t")
+        resolved_revisions.append((hash, rev))
+
+    return resolved_revisions
 
 
 async def get_remote_refs(
@@ -47,15 +70,7 @@ async def get_remote_refs(
         models.GetRevisionsResponseModel(branches=[], tags=[])
     )
 
-    git_env = {
-        "GIT_USERNAME": username or "",
-        "GIT_PASSWORD": password or "",
-        "GIT_ASKPASS": str(
-            (pathlib.Path(__file__).parents[0] / "askpass.py").absolute()
-        ),
-    }
-    for ref in await ls_remote(url, git_env):
-        (_, ref) = ref.split("\t")
+    for _, ref in await ls_remote(url, username, password):
         if "^" in ref:
             continue
         if ref.startswith("refs/heads/"):
diff --git a/backend/capellacollab/settings/modelsources/git/exceptions.py b/backend/capellacollab/settings/modelsources/git/exceptions.py
index dcf656a6aa..4d8df820e7 100644
--- a/backend/capellacollab/settings/modelsources/git/exceptions.py
+++ b/backend/capellacollab/settings/modelsources/git/exceptions.py
@@ -41,3 +41,13 @@ def __init__(self):
             ),
             err_code="NO_GIT_INSTANCE_WITH_PREFIX_FOUND",
         )
+
+
+class RevisionNotFoundError(core_exceptions.BaseError):
+    def __init__(self, revision: str):
+        super().__init__(
+            status_code=status.HTTP_404_NOT_FOUND,
+            title="Revision not found in repository",
+            reason=f"The revision '{revision}' is not a valid branch or tag name.",
+            err_code="GIT_REVISION_NOT_FOUND",
+        )
diff --git a/backend/capellacollab/settings/modelsources/git/routes.py b/backend/capellacollab/settings/modelsources/git/routes.py
index 2d6bc53cc6..9c11b9fea0 100644
--- a/backend/capellacollab/settings/modelsources/git/routes.py
+++ b/backend/capellacollab/settings/modelsources/git/routes.py
@@ -8,7 +8,7 @@
 
 from capellacollab.core import database
 from capellacollab.core.authentication import injectables as auth_injectables
-from capellacollab.settings.modelsources.git import core as git_core
+from capellacollab.settings.modelsources.git import core as instances_git_core
 from capellacollab.users import models as users_models
 
 from . import crud, injectables, models, util
@@ -111,7 +111,7 @@ async def get_revisions(
     username = body.credentials.username
     password = body.credentials.password
 
-    return await git_core.get_remote_refs(url, username, password)
+    return await instances_git_core.get_remote_refs(url, username, password)
 
 
 @router.post("/validate/path", response_model=bool)
diff --git a/backend/tests/sessions/hooks/conftest.py b/backend/tests/sessions/hooks/conftest.py
new file mode 100644
index 0000000000..8288a139d7
--- /dev/null
+++ b/backend/tests/sessions/hooks/conftest.py
@@ -0,0 +1,86 @@
+# SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
+# SPDX-License-Identifier: Apache-2.0
+
+import datetime
+import logging
+
+import pytest
+from sqlalchemy import orm
+
+from capellacollab.sessions import models as sessions_models
+from capellacollab.sessions.hooks import interface as hooks_interface
+from capellacollab.sessions.operators import k8s as k8s_operator
+from capellacollab.tools import models as tools_models
+from capellacollab.users import models as users_models
+
+
+@pytest.fixture(name="configuration_hook_request")
+def fixture_configuration_hook_request(
+    db: orm.Session,
+    user: users_models.DatabaseUser,
+    capella_tool: tools_models.DatabaseTool,
+    capella_tool_version: tools_models.DatabaseVersion,
+) -> hooks_interface.ConfigurationHookRequest:
+    return hooks_interface.ConfigurationHookRequest(
+        db=db,
+        operator=k8s_operator.KubernetesOperator(),
+        user=user,
+        tool=capella_tool,
+        tool_version=capella_tool_version,
+        session_type=sessions_models.SessionType.PERSISTENT,
+        connection_method=tools_models.GuacamoleConnectionMethod(),
+        provisioning=[],
+        session_id="nxylxqbmfqwvswlqlcbsirvrt",
+    )
+
+
+@pytest.fixture(name="post_session_creation_hook_request")
+def fixture_post_session_creation_hook_request(
+    session: sessions_models.DatabaseSession,
+    user: users_models.DatabaseUser,
+) -> hooks_interface.PostSessionCreationHookRequest:
+    return hooks_interface.PostSessionCreationHookRequest(
+        session_id="test",
+        db_session=session,
+        session={
+            "id": "test",
+            "port": 8080,
+            "created_at": datetime.datetime.fromisoformat(
+                "2021-01-01T00:00:00"
+            ),
+            "host": "test",
+        },
+        user=user,
+        connection_method=tools_models.GuacamoleConnectionMethod(),
+        operator=k8s_operator.KubernetesOperator(),
+    )
+
+
+@pytest.fixture(name="session_connection_hook_request")
+def fixture_session_connection_hook_request(
+    db: orm.Session,
+    user: users_models.DatabaseUser,
+    session: sessions_models.DatabaseSession,
+    logger: logging.LoggerAdapter,
+) -> hooks_interface.SessionConnectionHookRequest:
+
+    return hooks_interface.SessionConnectionHookRequest(
+        db=db,
+        db_session=session,
+        connection_method=tools_models.GuacamoleConnectionMethod(),
+        logger=logger,
+        user=user,
+    )
+
+
+@pytest.fixture(name="pre_session_termination_hook_request")
+def fixture_pre_session_termination_hook_request(
+    db: orm.Session,
+    session: sessions_models.DatabaseSession,
+) -> hooks_interface.PreSessionTerminationHookRequest:
+    return hooks_interface.PreSessionTerminationHookRequest(
+        db=db,
+        connection_method=tools_models.GuacamoleConnectionMethod(),
+        operator=k8s_operator.KubernetesOperator(),
+        session=session,
+    )
diff --git a/backend/tests/sessions/hooks/test_guacamole_hook.py b/backend/tests/sessions/hooks/test_guacamole_hook.py
index 3a6013cfee..c79dece563 100644
--- a/backend/tests/sessions/hooks/test_guacamole_hook.py
+++ b/backend/tests/sessions/hooks/test_guacamole_hook.py
@@ -127,23 +127,12 @@ def match_user_creation_body(
     "guacamole_create_token", "guacamole_delete_user", "guacamole_apis"
 )
 def test_guacamole_configuration_hook(
-    session: sessions_models.DatabaseSession,
+    post_session_creation_hook_request: session_hooks_interface.PostSessionCreationHookRequest,
 ):
     """Test that the Guacamole hook creates a user and a connection"""
 
     response = guacamole.GuacamoleIntegration().post_session_creation_hook(
-        session_id="test",
-        db_session=session,
-        session={
-            "id": "test",
-            "port": "8080",
-            "created_at": "2021-01-01T00:00:00",
-            "host": "test",
-        },
-        user=users_models.DatabaseUser(
-            name="test", idp_identifier="test", role=users_models.Role.USER
-        ),
-        connection_method=tools_models.GuacamoleConnectionMethod(),
+        post_session_creation_hook_request
     )
 
     assert response["config"]["guacamole_username"]
@@ -157,7 +146,7 @@ def test_guacamole_configuration_hook(
     "guacamole_create_token", "guacamole_delete_user", "guacamole_apis"
 )
 def test_fail_if_guacamole_unreachable(
-    session: sessions_models.DatabaseSession,
+    post_session_creation_hook_request: session_hooks_interface.PostSessionCreationHookRequest,
 ):
     """If Guacamole is unreachable, the session hook will abort the session creation"""
 
@@ -171,18 +160,7 @@ def test_fail_if_guacamole_unreachable(
 
     with pytest.raises(guacamole.GuacamoleError):
         guacamole.GuacamoleIntegration().post_session_creation_hook(
-            session_id="test",
-            db_session=session,
-            session={
-                "id": "test",
-                "port": "8080",
-                "created_at": "2021-01-01T00:00:00",
-                "host": "test",
-            },
-            user=users_models.DatabaseUser(
-                name="test", idp_identifier="test", role=users_models.Role.USER
-            ),
-            connection_method=tools_models.GuacamoleConnectionMethod(),
+            post_session_creation_hook_request
         )
 
 
@@ -191,26 +169,18 @@ def test_fail_if_guacamole_unreachable(
     "guacamole_create_token", "guacamole_delete_user", "guacamole_apis"
 )
 def test_guacamole_hook_not_executed_for_http_method(
-    session: sessions_models.DatabaseSession,
+    post_session_creation_hook_request: session_hooks_interface.PostSessionCreationHookRequest,
 ):
     """Skip if connection method is not Guacamole
 
     If the connection method is not Guacamole, the hook should skip the preparation.
     """
 
+    post_session_creation_hook_request.connection_method = (
+        tools_models.HTTPConnectionMethod()
+    )
     response = guacamole.GuacamoleIntegration().post_session_creation_hook(
-        session_id="test",
-        session={
-            "id": "test",
-            "port": "8080",
-            "created_at": "2021-01-01T00:00:00",
-            "host": "test",
-        },
-        db_session=session,
-        user=users_models.DatabaseUser(
-            name="test", idp_identifier="test", role=users_models.Role.USER
-        ),
-        connection_method=tools_models.HTTPConnectionMethod(),
+        post_session_creation_hook_request
     )
 
     assert session_hooks_interface.PostSessionCreationHookResult() == response
@@ -222,23 +192,12 @@ def test_guacamole_hook_not_executed_for_http_method(
     "guacamole_create_token", "guacamole_delete_user", "guacamole_apis"
 )
 def test_skip_guacamole_user_deletion_on_404(
-    session: sessions_models.DatabaseSession,
+    post_session_creation_hook_request: session_hooks_interface.PostSessionCreationHookRequest,
 ):
     """If the user does not exist, the hook should not fail"""
 
     response = guacamole.GuacamoleIntegration().post_session_creation_hook(
-        session_id="test",
-        db_session=session,
-        session={
-            "id": "test",
-            "port": "8080",
-            "created_at": "2021-01-01T00:00:00",
-            "host": "test",
-        },
-        user=users_models.DatabaseUser(
-            name="test", idp_identifier="test", role=users_models.Role.USER
-        ),
-        connection_method=tools_models.GuacamoleConnectionMethod(),
+        post_session_creation_hook_request
     )
 
     assert response["config"]
diff --git a/backend/tests/sessions/hooks/test_http_hook.py b/backend/tests/sessions/hooks/test_http_hook.py
index 3fe2026910..ef63127ad0 100644
--- a/backend/tests/sessions/hooks/test_http_hook.py
+++ b/backend/tests/sessions/hooks/test_http_hook.py
@@ -9,7 +9,10 @@
 from capellacollab.tools import models as tools_models
 
 
-def test_http_hook(session: sessions_models.DatabaseSession):
+def test_http_hook(
+    session: sessions_models.DatabaseSession,
+    session_connection_hook_request: sessions_hooks_interface.SessionConnectionHookRequest,
+):
     session.environment = {
         "TEST": "test",
         "CAPELLACOLLAB_SESSION_TOKEN": "test",
@@ -18,10 +21,10 @@ def test_http_hook(session: sessions_models.DatabaseSession):
         redirect_url="http://localhost:8000/{TEST}",
         cookies={"test": "{TEST}"},
     )
+    session_connection_hook_request.connection_method = connection_method
+    session_connection_hook_request.db_session = session
     result = http.HTTPIntegration().session_connection_hook(
-        db_session=session,
-        connection_method=connection_method,
-        logger=logging.getLogger(),
+        session_connection_hook_request
     )
 
     assert result["cookies"] == {"test": "test", "ccm_session_token": "test"}
@@ -29,24 +32,27 @@ def test_http_hook(session: sessions_models.DatabaseSession):
     assert not result["warnings"]
 
 
-def test_skip_http_hook_if_guacamole(session: sessions_models.DatabaseSession):
+def test_skip_http_hook_if_guacamole(
+    session_connection_hook_request: sessions_hooks_interface.SessionConnectionHookRequest,
+):
     result = http.HTTPIntegration().session_connection_hook(
-        db_session=session,
-        connection_method=tools_models.GuacamoleConnectionMethod(),
-        logger=logging.getLogger(),
+        session_connection_hook_request
     )
     assert result == sessions_hooks_interface.SessionConnectionHookResult()
 
 
-def test_fail_derive_redirect_url(session: sessions_models.DatabaseSession):
+def test_fail_derive_redirect_url(
+    session: sessions_models.DatabaseSession,
+    session_connection_hook_request: sessions_hooks_interface.SessionConnectionHookRequest,
+):
     session.environment = {"TEST": "test"}
     connection_method = tools_models.HTTPConnectionMethod(
         redirect_url="http://localhost:8000/{TEST2}"
     )
+    session_connection_hook_request.connection_method = connection_method
+    session_connection_hook_request.db_session = session
     result = http.HTTPIntegration().session_connection_hook(
-        db_session=session,
-        connection_method=connection_method,
-        logger=logging.getLogger(),
+        session_connection_hook_request
     )
 
     assert len(result["warnings"]) == 1
diff --git a/backend/tests/sessions/hooks/test_jupyter_hook.py b/backend/tests/sessions/hooks/test_jupyter_hook.py
index b33fdf233d..3769723721 100644
--- a/backend/tests/sessions/hooks/test_jupyter_hook.py
+++ b/backend/tests/sessions/hooks/test_jupyter_hook.py
@@ -3,33 +3,39 @@
 
 
 import pytest
-from sqlalchemy import orm
 
 import capellacollab.projects.toolmodels.models as toolmodels_models
+from capellacollab.sessions.hooks import interface as hooks_interface
 from capellacollab.sessions.hooks import jupyter as jupyter_hook
+from capellacollab.sessions.operators import models as operators_models
 from capellacollab.tools import models as tools_models
-from capellacollab.users import models as users_models
 
 
 @pytest.mark.usefixtures("project_user")
 def test_jupyter_successful_volume_mount(
     jupyter_model: toolmodels_models.DatabaseToolModel,
     jupyter_tool: tools_models.DatabaseTool,
-    user: users_models.DatabaseUser,
-    db: orm.Session,
+    configuration_hook_request: hooks_interface.ConfigurationHookRequest,
 ):
+
     class MockOperator:
         def persistent_volume_exists(self, name: str) -> bool:
             return True
 
+    configuration_hook_request.operator = MockOperator()  # type: ignore
+    configuration_hook_request.tool = jupyter_tool
+
     result = jupyter_hook.JupyterIntegration().configuration_hook(
-        db=db, user=user, tool=jupyter_tool, operator=MockOperator()
+        configuration_hook_request
     )
 
     assert not result["warnings"]
     assert len(result["volumes"]) == 1
+
+    volume = result["volumes"][0]
+    assert isinstance(volume, operators_models.PersistentVolume)
     assert (
-        result["volumes"][0].volume_name
+        volume.volume_name
         == "shared-workspace-" + jupyter_model.configuration["workspace"]
     )
 
@@ -37,15 +43,17 @@ def persistent_volume_exists(self, name: str) -> bool:
 @pytest.mark.usefixtures("project_user", "jupyter_model")
 def test_jupyter_volume_mount_not_found(
     jupyter_tool: tools_models.DatabaseTool,
-    user: users_models.DatabaseUser,
-    db: orm.Session,
+    configuration_hook_request: hooks_interface.ConfigurationHookRequest,
 ):
     class MockOperator:
         def persistent_volume_exists(self, name: str) -> bool:
             return False
 
+    configuration_hook_request.operator = MockOperator()  # type: ignore
+    configuration_hook_request.tool = jupyter_tool
+
     result = jupyter_hook.JupyterIntegration().configuration_hook(
-        db=db, user=user, tool=jupyter_tool, operator=MockOperator()
+        configuration_hook_request
     )
 
     assert not result["volumes"]
@@ -58,14 +66,12 @@ def persistent_volume_exists(self, name: str) -> bool:
 @pytest.mark.usefixtures("jupyter_model")
 def test_jupyter_volume_mount_without_project_access(
     jupyter_tool: tools_models.DatabaseTool,
-    user: users_models.DatabaseUser,
-    db: orm.Session,
+    configuration_hook_request: hooks_interface.ConfigurationHookRequest,
 ):
-    class MockOperator:
-        pass
+    configuration_hook_request.tool = jupyter_tool
 
     result = jupyter_hook.JupyterIntegration().configuration_hook(
-        db=db, user=user, tool=jupyter_tool, operator=MockOperator()
+        configuration_hook_request
     )
 
     assert not result["volumes"]
diff --git a/backend/tests/sessions/hooks/test_networking_hook.py b/backend/tests/sessions/hooks/test_networking_hook.py
index 3ec70ca527..dfa437bfe0 100644
--- a/backend/tests/sessions/hooks/test_networking_hook.py
+++ b/backend/tests/sessions/hooks/test_networking_hook.py
@@ -7,12 +7,13 @@
 
 from capellacollab.sessions import models as sessions_models
 from capellacollab.sessions import operators
+from capellacollab.sessions.hooks import interface as session_hooks_interface
 from capellacollab.sessions.hooks import networking as networking_hook
-from capellacollab.users import models as users_models
 
 
 def test_network_policy_created(
-    user: users_models.DatabaseUser, monkeypatch: pytest.MonkeyPatch
+    monkeypatch: pytest.MonkeyPatch,
+    post_session_creation_hook_request: session_hooks_interface.PostSessionCreationHookRequest,
 ):
     network_policy_counter = 0
 
@@ -31,16 +32,15 @@ def mock_create_namespaced_network_policy(
     )
 
     networking_hook.NetworkingIntegration().post_session_creation_hook(
-        session_id="test",
-        operator=operators.KubernetesOperator(),
-        user=user,
+        post_session_creation_hook_request
     )
 
     assert network_policy_counter == 1
 
 
 def test_network_policy_deleted(
-    session: sessions_models.DatabaseSession, monkeypatch: pytest.MonkeyPatch
+    monkeypatch: pytest.MonkeyPatch,
+    pre_session_termination_hook_request: session_hooks_interface.PreSessionTerminationHookRequest,
 ):
     network_policy_del_counter = 0
 
@@ -59,8 +59,7 @@ def mock_delete_namespaced_network_policy(
     )
 
     networking_hook.NetworkingIntegration().pre_session_termination_hook(
-        operator=operators.KubernetesOperator(),
-        session=session,
+        pre_session_termination_hook_request
     )
 
     assert network_policy_del_counter == 1
diff --git a/backend/tests/sessions/hooks/test_persistent_workspace.py b/backend/tests/sessions/hooks/test_persistent_workspace.py
index c336e54e6e..ccab3452bb 100644
--- a/backend/tests/sessions/hooks/test_persistent_workspace.py
+++ b/backend/tests/sessions/hooks/test_persistent_workspace.py
@@ -18,34 +18,28 @@
 
 
 def test_persistent_workspace_mounting_not_allowed(
-    db: orm.Session,
-    tool: tools_models.DatabaseTool,
-    test_user: users_models.DatabaseUser,
+    configuration_hook_request: hooks_interface.ConfigurationHookRequest,
 ):
-    tool.config.persistent_workspaces.mounting_enabled = False
+    configuration_hook_request.tool.config.persistent_workspaces.mounting_enabled = (
+        False
+    )
 
     with pytest.raises(sessions_exceptions.WorkspaceMountingNotAllowedError):
         persistent_workspace.PersistentWorkspaceHook().configuration_hook(
-            db=db,
-            operator=operators.KubernetesOperator(),
-            user=test_user,
-            session_type=sessions_models.SessionType.PERSISTENT,
-            tool=tool,
+            configuration_hook_request
         )
 
 
 def persistent_workspace_mounting_readonly_session(
-    db: orm.Session,
-    tool: tools_models.DatabaseTool,
-    test_user: users_models.DatabaseUser,
+    configuration_hook_request: hooks_interface.ConfigurationHookRequest,
 ):
+    configuration_hook_request.session_type = (
+        sessions_models.SessionType.READONLY
+    )
+
     response = (
         persistent_workspace.PersistentWorkspaceHook().configuration_hook(
-            db=db,
-            operator=operators.KubernetesOperator(),
-            user=test_user,
-            session_type=sessions_models.SessionType.READONLY,
-            tool=tool,
+            configuration_hook_request
         )
     )
 
@@ -54,9 +48,9 @@ def persistent_workspace_mounting_readonly_session(
 
 def test_workspace_is_created(
     db: orm.Session,
-    tool: tools_models.DatabaseTool,
     test_user: users_models.DatabaseUser,
     monkeypatch: pytest.MonkeyPatch,
+    configuration_hook_request: hooks_interface.ConfigurationHookRequest,
 ):
     created_volumes = 0
     volume_name = None
@@ -77,12 +71,11 @@ def mock_create_namespaced_persistent_volume_claim(
     assert (
         len(users_workspaces_crud.get_workspaces_for_user(db, test_user)) == 0
     )
+
+    configuration_hook_request.operator = operators.KubernetesOperator()
+    configuration_hook_request.user = test_user
     persistent_workspace.PersistentWorkspaceHook().configuration_hook(
-        db=db,
-        operator=operators.KubernetesOperator(),
-        user=test_user,
-        session_type=sessions_models.SessionType.PERSISTENT,
-        tool=tool,
+        configuration_hook_request
     )
     assert created_volumes == 1
     assert isinstance(volume_name, str)
@@ -94,10 +87,10 @@ def mock_create_namespaced_persistent_volume_claim(
 
 def test_existing_workspace_is_mounted(
     db: orm.Session,
-    tool: tools_models.DatabaseTool,
     test_user: users_models.DatabaseUser,
     user_workspace: users_workspaces_models.DatabaseWorkspace,
     monkeypatch: pytest.MonkeyPatch,
+    configuration_hook_request: hooks_interface.ConfigurationHookRequest,
 ):
     created_volumes = 0
     volume_name = None
@@ -116,12 +109,11 @@ def mock_create_namespaced_persistent_volume_claim(self, ns, pvc):
     assert (
         len(users_workspaces_crud.get_workspaces_for_user(db, test_user)) == 1
     )
+
+    configuration_hook_request.user = test_user
+    configuration_hook_request.operator = operators.KubernetesOperator()
     persistent_workspace.PersistentWorkspaceHook().configuration_hook(
-        db=db,
-        operator=operators.KubernetesOperator(),
-        user=test_user,
-        session_type=sessions_models.SessionType.PERSISTENT,
-        tool=tool,
+        configuration_hook_request
     )
     assert created_volumes == 1
     assert isinstance(volume_name, str)
diff --git a/backend/tests/sessions/hooks/test_provisioning_hook.py b/backend/tests/sessions/hooks/test_provisioning_hook.py
index be74fd816e..c606de15eb 100644
--- a/backend/tests/sessions/hooks/test_provisioning_hook.py
+++ b/backend/tests/sessions/hooks/test_provisioning_hook.py
@@ -1,8 +1,6 @@
 # SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
 # SPDX-License-Identifier: Apache-2.0
 
-import json
-
 import fastapi
 import pytest
 from sqlalchemy import orm
@@ -14,38 +12,31 @@
 )
 from capellacollab.sessions import exceptions as sessions_exceptions
 from capellacollab.sessions import models as sessions_models
+from capellacollab.sessions.hooks import interface as hooks_interface
 from capellacollab.sessions.hooks import provisioning as hooks_provisioning
-from capellacollab.tools import crud as tools_crud
 from capellacollab.tools import models as tools_models
 from capellacollab.users import models as users_models
 
 
 @pytest.mark.usefixtures("project_user")
 def test_git_models_are_resolved_correctly(
-    db: orm.Session,
-    user: users_models.DatabaseUser,
-    capella_tool: tools_models.DatabaseTool,
-    capella_tool_version: tools_models.DatabaseVersion,
     project: projects_models.DatabaseProject,
     capella_model: toolmodels_models.DatabaseToolModel,
     git_model: git_models.DatabaseGitModel,
+    configuration_hook_request: hooks_interface.ConfigurationHookRequest,
 ):
     """Make sure that the Git models are correctly translated to GIT_MODELS environment"""
-
+    configuration_hook_request.provisioning = [
+        sessions_models.SessionProvisioningRequest(
+            project_slug=project.slug,
+            model_slug=capella_model.slug,
+            git_model_id=git_model.id,
+            revision="test",
+            deep_clone=False,
+        )
+    ]
     response = hooks_provisioning.ProvisionWorkspaceHook().configuration_hook(
-        db=db,
-        tool=capella_tool,
-        tool_version=capella_tool_version,
-        user=user,
-        provisioning=[
-            sessions_models.SessionProvisioningRequest(
-                project_slug=project.slug,
-                model_slug=capella_model.slug,
-                git_model_id=git_model.id,
-                revision="test",
-                deep_clone=False,
-            )
-        ],
+        configuration_hook_request
     )
 
     expected_response_dict = {
@@ -71,42 +62,35 @@ def test_git_models_are_resolved_correctly(
 
 
 def test_provisioning_fails_missing_permission(
-    db: orm.Session,
-    user: users_models.DatabaseUser,
-    capella_tool: tools_models.DatabaseTool,
-    capella_tool_version: tools_models.DatabaseVersion,
     project: projects_models.DatabaseProject,
     capella_model: toolmodels_models.DatabaseToolModel,
     git_model: git_models.DatabaseGitModel,
+    configuration_hook_request: hooks_interface.ConfigurationHookRequest,
 ):
     """Make sure that provisioning fails when the user does not have the correct permissions"""
+
+    configuration_hook_request.provisioning = [
+        sessions_models.SessionProvisioningRequest(
+            project_slug=project.slug,
+            model_slug=capella_model.slug,
+            git_model_id=git_model.id,
+            revision="main",
+            deep_clone=False,
+        )
+    ]
     with pytest.raises(fastapi.HTTPException):
         hooks_provisioning.ProvisionWorkspaceHook().configuration_hook(
-            db=db,
-            tool=capella_tool,
-            tool_version=capella_tool_version,
-            user=user,
-            provisioning=[
-                sessions_models.SessionProvisioningRequest(
-                    project_slug=project.slug,
-                    model_slug=capella_model.slug,
-                    git_model_id=git_model.id,
-                    revision="main",
-                    deep_clone=False,
-                )
-            ],
+            configuration_hook_request
         )
 
 
 @pytest.mark.usefixtures("project_user")
 def test_provisioning_fails_too_many_models_requested(
-    db: orm.Session,
-    user: users_models.DatabaseUser,
     capella_tool: tools_models.DatabaseTool,
-    capella_tool_version: tools_models.DatabaseVersion,
     project: projects_models.DatabaseProject,
     capella_model: toolmodels_models.DatabaseToolModel,
     git_model: git_models.DatabaseGitModel,
+    configuration_hook_request: hooks_interface.ConfigurationHookRequest,
 ):
     capella_tool.config.provisioning.max_number_of_models = 1
 
@@ -118,46 +102,38 @@ def test_provisioning_fails_too_many_models_requested(
         deep_clone=False,
     )
 
+    configuration_hook_request.provisioning = [
+        session_provisioning_request,
+        session_provisioning_request,
+    ]
     with pytest.raises(
         sessions_exceptions.TooManyModelsRequestedToProvisionError
     ):
         hooks_provisioning.ProvisionWorkspaceHook().configuration_hook(
-            db=db,
-            tool=capella_tool,
-            tool_version=capella_tool_version,
-            user=user,
-            provisioning=[
-                session_provisioning_request,
-                session_provisioning_request,
-            ],
+            configuration_hook_request
         )
 
 
 def test_tool_model_mismatch(
-    db: orm.Session,
-    user: users_models.DatabaseUser,
-    tool: tools_models.DatabaseTool,
-    tool_version: tools_models.DatabaseVersion,
     project: projects_models.DatabaseProject,
     capella_model: toolmodels_models.DatabaseToolModel,
     git_model: git_models.DatabaseGitModel,
+    configuration_hook_request: hooks_interface.ConfigurationHookRequest,
 ):
     """Make sure that provisioning fails when the provided model doesn't match the selected tool"""
+
+    configuration_hook_request.provisioning = [
+        sessions_models.SessionProvisioningRequest(
+            project_slug=project.slug,
+            model_slug=capella_model.slug,
+            git_model_id=git_model.id,
+            revision="main",
+            deep_clone=False,
+        )
+    ]
     with pytest.raises(sessions_exceptions.ToolAndModelMismatchError):
         hooks_provisioning.ProvisionWorkspaceHook().configuration_hook(
-            db=db,
-            tool=tool,
-            tool_version=tool_version,
-            user=user,
-            provisioning=[
-                sessions_models.SessionProvisioningRequest(
-                    project_slug=project.slug,
-                    model_slug=capella_model.slug,
-                    git_model_id=git_model.id,
-                    revision="main",
-                    deep_clone=False,
-                )
-            ],
+            configuration_hook_request
         )
 
 
@@ -165,11 +141,11 @@ def test_provision_session_with_compatible_tool_versions(
     db: orm.Session,
     admin: users_models.DatabaseUser,
     tool_version: tools_models.DatabaseVersion,
-    tool: tools_models.DatabaseTool,
     capella_tool_version: tools_models.DatabaseVersion,
     project: projects_models.DatabaseProject,
     capella_model: toolmodels_models.DatabaseToolModel,
     git_model: git_models.DatabaseGitModel,
+    configuration_hook_request: hooks_interface.ConfigurationHookRequest,
 ):
     """Make sure that provisioning is successful when the tool is compatible with the tool of the model"""
 
@@ -177,19 +153,17 @@ def test_provision_session_with_compatible_tool_versions(
     orm.attributes.flag_modified(tool_version, "config")
     db.commit()
 
+    configuration_hook_request.provisioning = [
+        sessions_models.SessionProvisioningRequest(
+            project_slug=project.slug,
+            model_slug=capella_model.slug,
+            git_model_id=git_model.id,
+            revision="main",
+            deep_clone=False,
+        )
+    ]
+    configuration_hook_request.user = admin
     response = hooks_provisioning.ProvisionWorkspaceHook().configuration_hook(
-        db=db,
-        tool=tool,
-        tool_version=tool_version,
-        user=admin,
-        provisioning=[
-            sessions_models.SessionProvisioningRequest(
-                project_slug=project.slug,
-                model_slug=capella_model.slug,
-                git_model_id=git_model.id,
-                revision="main",
-                deep_clone=False,
-            )
-        ],
+        configuration_hook_request
     )
     assert response["environment"]["CAPELLACOLLAB_SESSION_PROVISIONING"]
diff --git a/backend/tests/sessions/hooks/test_pure_variants.py b/backend/tests/sessions/hooks/test_pure_variants.py
index 6f23039e6e..6b0906ac8d 100644
--- a/backend/tests/sessions/hooks/test_pure_variants.py
+++ b/backend/tests/sessions/hooks/test_pure_variants.py
@@ -51,16 +51,17 @@ def fixture_pure_variants_model(
 
 
 def test_skip_for_read_only_sessions(
-    db: orm.Session,
-    user: users_models.DatabaseUser,
+    configuration_hook_request: hooks_interface.ConfigurationHookRequest,
 ):
     """pure::variants has no read-only support
 
     Therefore, the hook also shouldn't do anything for read-only sessions.
     """
-
+    configuration_hook_request.session_type = (
+        sessions_models.SessionType.READONLY
+    )
     result = pure_variants.PureVariantsIntegration().configuration_hook(
-        db, user, sessions_models.SessionType.READONLY
+        configuration_hook_request
     )
 
     assert result == hooks_interface.ConfigurationHookResult()
@@ -69,8 +70,8 @@ def test_skip_for_read_only_sessions(
 @pytest.mark.usefixtures("project_user")
 def test_skip_when_user_has_no_pv_access(
     db: orm.Session,
-    user: users_models.DatabaseUser,
     pure_variants_model: toolmodels_models.DatabaseToolModel,
+    configuration_hook_request: hooks_interface.ConfigurationHookRequest,
 ):
     """If a user has no access to a project with a model that
     has the pure::variants restriction enabled, skip loading of the license.
@@ -84,7 +85,7 @@ def test_skip_when_user_has_no_pv_access(
     )
 
     result = pure_variants.PureVariantsIntegration().configuration_hook(
-        db, user, sessions_models.SessionType.PERSISTENT
+        configuration_hook_request
     )
 
     assert "environment" not in result
@@ -95,8 +96,8 @@ def test_skip_when_user_has_no_pv_access(
 @pytest.mark.usefixtures("project_user")
 def test_skip_when_license_server_not_configured(
     db: orm.Session,
-    user: users_models.DatabaseUser,
     pure_variants_model: toolmodels_models.DatabaseToolModel,
+    configuration_hook_request: hooks_interface.ConfigurationHookRequest,
 ):
     """If no pure::variants license is configured in the settings,
     skip loading of the license.
@@ -111,7 +112,7 @@ def test_skip_when_license_server_not_configured(
     )
 
     result = pure_variants.PureVariantsIntegration().configuration_hook(
-        db, user, sessions_models.SessionType.PERSISTENT
+        configuration_hook_request
     )
 
     assert "environment" not in result
@@ -122,8 +123,8 @@ def test_skip_when_license_server_not_configured(
 @pytest.mark.usefixtures("project_user", "pure_variants_license")
 def test_inject_pure_variants_license_information(
     db: orm.Session,
-    user: users_models.DatabaseUser,
     pure_variants_model: toolmodels_models.DatabaseToolModel,
+    configuration_hook_request: hooks_interface.ConfigurationHookRequest,
 ):
     """Test that the configured license information is properly
     injected in the session container.
@@ -138,7 +139,7 @@ def test_inject_pure_variants_license_information(
     )
 
     result = pure_variants.PureVariantsIntegration().configuration_hook(
-        db, user, sessions_models.SessionType.PERSISTENT
+        configuration_hook_request
     )
 
     assert result["environment"] == {
diff --git a/backend/tests/sessions/hooks/test_session_preparation.py b/backend/tests/sessions/hooks/test_session_preparation.py
index ab12c919a4..2677774b34 100644
--- a/backend/tests/sessions/hooks/test_session_preparation.py
+++ b/backend/tests/sessions/hooks/test_session_preparation.py
@@ -5,33 +5,32 @@
 from capellacollab.sessions import models as sessions_models
 from capellacollab.sessions.hooks import interface as hooks_interface
 from capellacollab.sessions.hooks import session_preparation
-from capellacollab.tools import models as tools_models
 
 
-def test_session_preparation_hook(tool: tools_models.DatabaseTool):
+def test_session_preparation_hook(
+    configuration_hook_request: hooks_interface.ConfigurationHookRequest,
+):
     """Test that the session preparation hook registers a shared volume"""
-
+    configuration_hook_request.session_type = (
+        sessions_models.SessionType.READONLY
+    )
     result = session_preparation.GitRepositoryCloningHook().configuration_hook(
-        session_type=sessions_models.SessionType.READONLY,
-        session_id="session-id",
-        tool=tool,
+        configuration_hook_request
     )
 
     assert len(result["volumes"]) == 1
     assert len(result["init_volumes"]) == 1
     assert result["volumes"][0] == result["init_volumes"][0]
-    assert result["volumes"][0].name == "session-id-models"
+    assert result["volumes"][0].name == "nxylxqbmfqwvswlqlcbsirvrt-models"
 
 
 def test_session_preparation_hook_with_persistent_session(
-    tool: tools_models.DatabaseTool,
+    configuration_hook_request: hooks_interface.ConfigurationHookRequest,
 ):
     """Test that the session preparation hook doesn't do anything for persistent sessions"""
 
     result = session_preparation.GitRepositoryCloningHook().configuration_hook(
-        session_type=sessions_models.SessionType.PERSISTENT,
-        session_id="session-id",
-        tool=tool,
+        configuration_hook_request
     )
 
     assert result == hooks_interface.ConfigurationHookResult()
diff --git a/backend/tests/sessions/hooks/test_t4c_hook.py b/backend/tests/sessions/hooks/test_t4c_hook.py
index e058e96ae2..3a26641d1d 100644
--- a/backend/tests/sessions/hooks/test_t4c_hook.py
+++ b/backend/tests/sessions/hooks/test_t4c_hook.py
@@ -53,16 +53,12 @@ def fixture_mock_add_user_to_repository_failed(
 @responses.activate
 @pytest.mark.usefixtures("t4c_model", "project_user")
 def test_t4c_configuration_hook(
-    db: orm.Session,
     user: users_models.DatabaseUser,
-    capella_tool_version: tools_models.DatabaseVersion,
     mock_add_user_to_repository: responses.BaseResponse,
+    configuration_hook_request: sessions_hooks_interface.ConfigurationHookRequest,
 ):
     result = t4c.T4CIntegration().configuration_hook(
-        db=db,
-        user=user,
-        tool_version=capella_tool_version,
-        session_type=sessions_models.SessionType.PERSISTENT,
+        configuration_hook_request
     )
 
     assert result["environment"]["T4C_LICENCE_SECRET"]
@@ -76,21 +72,20 @@ def test_t4c_configuration_hook(
 @responses.activate
 @pytest.mark.usefixtures("t4c_model")
 def test_t4c_configuration_hook_as_admin(
-    db: orm.Session,
-    admin: users_models.DatabaseUser,
-    capella_tool_version: tools_models.DatabaseVersion,
     mock_add_user_to_repository: responses.BaseResponse,
+    configuration_hook_request: sessions_hooks_interface.ConfigurationHookRequest,
 ):
+    configuration_hook_request.user.role = users_models.Role.ADMIN
     result = t4c.T4CIntegration().configuration_hook(
-        db=db,
-        user=admin,
-        tool_version=capella_tool_version,
-        session_type=sessions_models.SessionType.PERSISTENT,
+        configuration_hook_request
     )
 
     assert result["environment"]["T4C_LICENCE_SECRET"]
     assert len(json.loads(result["environment"]["T4C_JSON"])) == 1
-    assert result["environment"]["T4C_USERNAME"] == admin.name
+    assert (
+        result["environment"]["T4C_USERNAME"]
+        == configuration_hook_request.user.name
+    )
     assert result["environment"]["T4C_PASSWORD"]
     assert not result["warnings"]
     assert mock_add_user_to_repository.call_count == 1
@@ -100,11 +95,11 @@ def test_t4c_configuration_hook_as_admin(
 @pytest.mark.usefixtures("t4c_model")
 def test_t4c_configuration_hook_with_same_repository_used_twice(
     db: orm.Session,
-    admin: users_models.DatabaseUser,
     project: projects_models.DatabaseProject,
     capella_tool_version: tools_models.DatabaseVersion,
     mock_add_user_to_repository: responses.BaseResponse,
     t4c_repository: settings_t4c_repositories_models.DatabaseT4CRepository,
+    configuration_hook_request: sessions_hooks_interface.ConfigurationHookRequest,
 ):
     model = toolmodels_models.PostToolModel(
         name="test2", description="test", tool_id=capella_tool_version.tool.id
@@ -113,11 +108,9 @@ def test_t4c_configuration_hook_with_same_repository_used_twice(
         db, project, model, capella_tool_version.tool, capella_tool_version
     )
     models_t4c_crud.create_t4c_model(db, db_model, t4c_repository, "default2")
+    configuration_hook_request.user.role = users_models.Role.ADMIN
     result = t4c.T4CIntegration().configuration_hook(
-        db=db,
-        user=admin,
-        tool_version=capella_tool_version,
-        session_type=sessions_models.SessionType.PERSISTENT,
+        configuration_hook_request
     )
 
     assert len(json.loads(result["environment"]["T4C_JSON"])) == 1
@@ -128,16 +121,14 @@ def test_t4c_configuration_hook_with_same_repository_used_twice(
 @responses.activate
 @pytest.mark.usefixtures("t4c_model", "project_user")
 def test_t4c_configuration_hook_failure(
-    db: orm.Session,
     user: users_models.DatabaseUser,
-    capella_tool_version: tools_models.DatabaseVersion,
     mock_add_user_to_repository_failed: responses.BaseResponse,
+    configuration_hook_request: sessions_hooks_interface.ConfigurationHookRequest,
 ):
+    """Test behavior when T4C API call fails"""
+
     result = t4c.T4CIntegration().configuration_hook(
-        db=db,
-        user=user,
-        tool_version=capella_tool_version,
-        session_type=sessions_models.SessionType.PERSISTENT,
+        configuration_hook_request
     )
 
     assert result["environment"]["T4C_LICENCE_SECRET"]
@@ -154,17 +145,14 @@ def test_configuration_hook_for_archived_project(
     project: projects_models.DatabaseProject,
     db: orm.Session,
     user: users_models.DatabaseUser,
-    capella_tool_version: tools_models.DatabaseVersion,
     mock_add_user_to_repository: responses.BaseResponse,
+    configuration_hook_request: sessions_hooks_interface.ConfigurationHookRequest,
 ):
     project.is_archived = True
     db.commit()
 
     result = t4c.T4CIntegration().configuration_hook(
-        db=db,
-        user=user,
-        tool_version=capella_tool_version,
-        session_type=sessions_models.SessionType.PERSISTENT,
+        configuration_hook_request
     )
 
     assert not result["environment"]["T4C_LICENCE_SECRET"]
@@ -180,18 +168,15 @@ def test_configuration_hook_for_archived_project(
 def test_configuration_hook_as_rw_user(
     db: orm.Session,
     user: users_models.DatabaseUser,
-    capella_tool_version: tools_models.DatabaseVersion,
     mock_add_user_to_repository: responses.BaseResponse,
     project_user: projects_users_models.ProjectUserAssociation,
+    configuration_hook_request: sessions_hooks_interface.ConfigurationHookRequest,
 ):
     project_user.permission = projects_users_models.ProjectUserPermission.READ
     db.commit()
 
     result = t4c.T4CIntegration().configuration_hook(
-        db=db,
-        user=user,
-        tool_version=capella_tool_version,
-        session_type=sessions_models.SessionType.PERSISTENT,
+        configuration_hook_request
     )
 
     assert not result["environment"]["T4C_LICENCE_SECRET"]
@@ -209,6 +194,7 @@ def test_configuration_hook_for_compatible_tool(
     user: users_models.DatabaseUser,
     capella_tool_version: tools_models.DatabaseVersion,
     mock_add_user_to_repository: responses.BaseResponse,
+    configuration_hook_request: sessions_hooks_interface.ConfigurationHookRequest,
 ):
     custom_tool = tools_crud.create_tool(
         db, tools_models.CreateTool(name="custom")
@@ -223,11 +209,9 @@ def test_configuration_hook_for_compatible_tool(
         db, custom_tool, create_compatible_tool_version
     )
 
+    configuration_hook_request.tool_version = compatible_tool_version
     result = t4c.T4CIntegration().configuration_hook(
-        db=db,
-        user=user,
-        tool_version=compatible_tool_version,
-        session_type=sessions_models.SessionType.PERSISTENT,
+        configuration_hook_request
     )
 
     assert result["environment"]["T4C_LICENCE_SECRET"]
@@ -239,28 +223,25 @@ def test_configuration_hook_for_compatible_tool(
 
 
 def test_t4c_configuration_hook_non_persistent(
-    db: orm.Session,
-    user: users_models.DatabaseUser,
-    tool_version: tools_models.DatabaseVersion,
+    configuration_hook_request: sessions_hooks_interface.ConfigurationHookRequest,
 ):
+    configuration_hook_request.session_type = (
+        sessions_models.SessionType.READONLY
+    )
     result = t4c.T4CIntegration().configuration_hook(
-        db=db,
-        user=user,
-        tool_version=tool_version,
-        session_type=sessions_models.SessionType.READONLY,
+        configuration_hook_request
     )
 
     assert result == sessions_hooks_interface.ConfigurationHookResult()
 
 
 def test_t4c_connection_hook_non_persistent(
-    user: users_models.DatabaseUser,
     session: sessions_models.DatabaseSession,
+    session_connection_hook_request: sessions_hooks_interface.SessionConnectionHookRequest,
 ):
     session.type = sessions_models.SessionType.READONLY
     result = t4c.T4CIntegration().session_connection_hook(
-        db_session=session,
-        user=user,
+        session_connection_hook_request
     )
 
     assert result == sessions_hooks_interface.SessionConnectionHookResult()
@@ -268,8 +249,8 @@ def test_t4c_connection_hook_non_persistent(
 
 def test_t4c_connection_hook_shared_session(
     db: orm.Session,
-    user: users_models.DatabaseUser,
     session: sessions_models.DatabaseSession,
+    session_connection_hook_request: sessions_hooks_interface.SessionConnectionHookRequest,
 ):
     user2 = users_crud.create_user(
         db,
@@ -280,21 +261,19 @@ def test_t4c_connection_hook_shared_session(
     )
     session.owner = user2
     result = t4c.T4CIntegration().session_connection_hook(
-        db_session=session,
-        user=user,
+        session_connection_hook_request
     )
 
     assert result == sessions_hooks_interface.SessionConnectionHookResult()
 
 
 def test_t4c_connection_hook(
-    user: users_models.DatabaseUser,
     session: sessions_models.DatabaseSession,
+    session_connection_hook_request: sessions_hooks_interface.SessionConnectionHookRequest,
 ):
     session.environment = {"T4C_PASSWORD": "test"}
     result = t4c.T4CIntegration().session_connection_hook(
-        db_session=session,
-        user=user,
+        session_connection_hook_request
     )
 
     assert result["t4c_token"] == "test"
@@ -303,18 +282,21 @@ def test_t4c_connection_hook(
 @responses.activate
 @pytest.mark.usefixtures("t4c_model", "project_user")
 def test_t4c_termination_hook(
-    db: orm.Session,
     session: sessions_models.DatabaseSession,
     user: users_models.DatabaseUser,
     t4c_instance: t4c_models.DatabaseT4CInstance,
     capella_tool_version: tools_models.DatabaseVersion,
+    pre_session_termination_hook_request: sessions_hooks_interface.PreSessionTerminationHookRequest,
 ):
     session.version = capella_tool_version
+    pre_session_termination_hook_request.session = session
     rsp = responses.delete(
         f"{t4c_instance.rest_api}/users/{user.name}?repositoryName=test",
         status=200,
     )
 
-    t4c.T4CIntegration().pre_session_termination_hook(db=db, session=session)
+    t4c.T4CIntegration().pre_session_termination_hook(
+        pre_session_termination_hook_request
+    )
 
     assert rsp.call_count == 1
diff --git a/backend/tests/sessions/test_session_hooks.py b/backend/tests/sessions/test_session_hooks.py
index b1a0fddc0a..315754e9e7 100644
--- a/backend/tests/sessions/test_session_hooks.py
+++ b/backend/tests/sessions/test_session_hooks.py
@@ -18,7 +18,6 @@
 from capellacollab.sessions import util as sessions_util
 from capellacollab.sessions.hooks import interface as hooks_interface
 from capellacollab.sessions.operators import k8s
-from capellacollab.tools import injectables as tools_injectables
 from capellacollab.tools import models as tools_models
 from capellacollab.users import models as users_models
 
@@ -45,52 +44,25 @@ class TestSessionHook(hooks_interface.HookRegistration):
     post_termination_hook_counter = 0
 
     def configuration_hook(
-        self,
-        db: orm.Session,
-        operator: operators.KubernetesOperator,
-        user: users_models.DatabaseUser,
-        tool: tools_models.DatabaseTool,
-        tool_version: tools_models.DatabaseVersion,
-        session_type: sessions_models.SessionType,
-        connection_method: tools_models.ToolSessionConnectionMethod,
-        provisioning: list[sessions_models.SessionProvisioningRequest],
-        session_id: str,
-        **kwargs,
+        self, request: hooks_interface.ConfigurationHookRequest
     ) -> hooks_interface.ConfigurationHookResult:
         self.configuration_hook_counter += 1
         return hooks_interface.ConfigurationHookResult()
 
     def post_session_creation_hook(
-        self,
-        session_id: str,
-        session: k8s.Session,
-        db_session: sessions_models.DatabaseSession,
-        operator: operators.KubernetesOperator,
-        user: users_models.DatabaseUser,
-        connection_method: tools_models.ToolSessionConnectionMethod,
-        **kwargs,
+        self, request: hooks_interface.PostSessionCreationHookRequest
     ) -> hooks_interface.PostSessionCreationHookResult:
         self.post_session_creation_hook_counter += 1
         return hooks_interface.PostSessionCreationHookResult()
 
     def session_connection_hook(
-        self,
-        db: orm.Session,
-        db_session: sessions_models.DatabaseSession,
-        connection_method: tools_models.ToolSessionConnectionMethod,
-        logger: logging.LoggerAdapter,
-        **kwargs,
+        self, request: hooks_interface.SessionConnectionHookRequest
     ) -> hooks_interface.SessionConnectionHookResult:
         self.session_connection_hook_counter += 1
         return hooks_interface.SessionConnectionHookResult()
 
     def pre_session_termination_hook(
-        self,
-        db: orm.Session,
-        operator: operators.KubernetesOperator,
-        session: sessions_models.DatabaseSession,
-        connection_method: tools_models.ToolSessionConnectionMethod,
-        **kwargs,
+        self, request: hooks_interface.PreSessionTerminationHookRequest
     ) -> hooks_interface.PreSessionTerminationHookResult:
         self.post_termination_hook_counter += 1
         return hooks_interface.PreSessionTerminationHookResult()
diff --git a/backend/tests/settings/test_git_instances.py b/backend/tests/settings/test_git_instances.py
index 7b249f6d1f..f6c1e1b6d0 100644
--- a/backend/tests/settings/test_git_instances.py
+++ b/backend/tests/settings/test_git_instances.py
@@ -7,7 +7,7 @@
 from fastapi import testclient
 from sqlalchemy import orm
 
-from capellacollab.settings.modelsources.git import core as git_core
+from capellacollab.settings.modelsources.git import core as instances_git_core
 from capellacollab.settings.modelsources.git import crud as git_crud
 from capellacollab.settings.modelsources.git import models as git_models
 
@@ -104,22 +104,24 @@ def test_fetch_revisions(
     monkeypatch: pytest.MonkeyPatch,
     client: testclient.TestClient,
 ):
-    ls_remote = [
-        "0665eb5bf5dc3a7bdcb30b4354c85eddde2bd847	HEAD",
-        "e0f83d8d57ec1552c5fb76c83f7dff7f0ff86631	refs/heads/test-branch1",
-        "76c71f5468f6e444317146c6c9a3e00033974a1c	refs/heads/test-branch2",
-        "0665eb5bf5dc3a7bdcb30b4354c85eddde2bd847	refs/heads/main",
-        "ea10a5a82f31807d89c1bb7fc61dcd331e49f8fc	refs/pull/100/head",
-        "47cda65668eb258c5e84a8ffd43909ba4fac2661	refs/tags/v1.0.0",
-        "bce139e467d3d60bd21a4097c78e86a87e1a5d21	refs/tags/v1.1.0",
-    ]
+    ls_remote = (
+        "0665eb5bf5dc3a7bdcb30b4354c85eddde2bd847	HEAD\n"
+        "e0f83d8d57ec1552c5fb76c83f7dff7f0ff86631	refs/heads/test-branch1\n"
+        "76c71f5468f6e444317146c6c9a3e00033974a1c	refs/heads/test-branch2\n"
+        "0665eb5bf5dc3a7bdcb30b4354c85eddde2bd847	refs/heads/main\n"
+        "ea10a5a82f31807d89c1bb7fc61dcd331e49f8fc	refs/pull/100/head\n"
+        "47cda65668eb258c5e84a8ffd43909ba4fac2661	refs/tags/v1.0.0\n"
+        "bce139e467d3d60bd21a4097c78e86a87e1a5d21	refs/tags/v1.1.0\n"
+    )
 
     def mock_ls_remote(*args, **kwargs):
         f: asyncio.Future = asyncio.Future()
         f.set_result(ls_remote)
         return f
 
-    monkeypatch.setattr(git_core, "ls_remote", mock_ls_remote)
+    monkeypatch.setattr(
+        instances_git_core, "_ls_remote_command", mock_ls_remote
+    )
 
     response = client.post(
         "/api/v1/settings/modelsources/git/revisions",
diff --git a/docs/docs/admin/tools/configuration.md b/docs/docs/admin/tools/configuration.md
index 9194898896..3856369db5 100644
--- a/docs/docs/admin/tools/configuration.md
+++ b/docs/docs/admin/tools/configuration.md
@@ -167,6 +167,13 @@ variables can be used by the tool:
             The tool has to set the `Content-Security-Policy` header to `frame-ancestors self {CAPELLACOLLAB_ORIGIN_HOST}`. Otherwise, the session viewer can't be used with the tool!
         </td>
     </tr>
+    <tr>
+        <td>`WORKSPACE_DIR`</td>
+        <td>`/workspace`</td>
+        <td>
+            The directory of the (persistent) workspace the application should work with.
+        </td>
+    </tr>
 
 </table>