diff --git a/saagieapi/apps/apps.py b/saagieapi/apps/apps.py
index 05cfe79..882cd6a 100644
--- a/saagieapi/apps/apps.py
+++ b/saagieapi/apps/apps.py
@@ -668,6 +668,7 @@ def edit(
         emails: List = None,
         logins: List = None,
         status_list: List = None,
+        resources: Dict = None,
     ) -> Dict:
         """Edit an app. \
         Each optional parameter can be set to change the value of the corresponding field.
@@ -694,6 +695,8 @@ def edit(
             Receive an email when the app status change to a specific status.
             Each item of the list should be one of these following values: "STARTING","STARTED",
             "ROLLING_BACK","UPGRADING","RECOVERING","RESTARTING","STOPPING","STOPPED","FAILED"
+        resources : dict, optional
+            Resources CPU, RAM & GPU limited and guaranteed for the app.
 
         Returns
         -------
@@ -728,6 +731,9 @@ def edit(
         if emails or logins or status_list:
             params["alerting"] = self.check_alerting(emails, logins, status_list)
 
+        if resources:
+            params["resources"] = resources
+
         params = {"app": params}
         result = self.saagie_api.client.execute(query=gql(GQL_EDIT_APP), variable_values=params)
         logging.info("✅ App [%s] successfully edited", app_id)
@@ -1458,3 +1464,87 @@ def count_history_statuses(self, history_id, version_number, start_time):
         }
 
         return self.saagie_api.client.execute(query=gql(GQL_COUNT_HISTORY_APP_STATUS), variable_values=params)
+
+    def get_logs(
+        self,
+        app_id: str,
+        app_execution_id: str,
+        limit: int = None,
+        skip: int = None,
+        log_stream: str = None,
+        start_at: str = None,
+    ):
+        """Get logs of the app
+
+        Parameters
+        ----------
+        app_id : str
+            UUID of your app
+        app_execution_id : str
+            UUID of the execution of your app
+        limit : int, optional
+            Number of log lines to retrieve
+        skip : int, optional
+            Number of log lines to skip
+        log_stream: str, optional
+            Stream of logs to follow. Values accepted :
+            [ENVVARS_STDOUT, ENVVARS_STDERR, ORCHESTRATION_STDOUT, ORCHESTRATION_STDERR, STDERR, STDOUT]
+            By default, all the streams are retrieved
+        start_at: str, optional
+            Get logs since a specific datetime.
+            Following formats accepted : "2024-04-09 10:00:00" and "2024-04-09T10:00:00"
+
+        Returns
+        -------
+        dict
+            Logs of the app
+
+        Examples
+        --------
+        >>> saagieapi.apps.get_logs(
+        ...     app_id="70e85ade-d6cc-4a90-8d7d-639adbd25e5d",
+        ...     app_execution_id="e3e31074-4a12-450e-96e4-0eae7801dfca",
+        ...     limit=2,
+        ...     skip=5,
+        ...     start_at="2024-04-09 10:00:00"
+        ... )
+        {
+            "appLogs": {
+                "count": 25,
+                "content": [
+                    {
+                        "index": 5,
+                        "value": "[I 2024-04-09 13:38:36.982 ServerApp] jupyterlab_git | extension was successfully linked.",
+                        "containerId": "d7104fa7371c5ed6ef540fa8b0620a654a0e02c57136e29f0fcc03d16e36d74f",
+                        "stream": "STDERR",
+                        "recordAt": "2024-04-09T13:38:36.982473892Z"
+                    },
+                    {
+                        "index": 6,
+                        "value": "[W 2024-04-09 13:38:36.987 NotebookApp] 'ip' has moved from NotebookApp to ServerApp. This config will be passed to ServerApp. Be sure to update your config before our next release.",
+                        "containerId": "d7104fa7371c5ed6ef540fa8b0620a654a0e02c57136e29f0fcc03d16e36d74f",
+                        "stream": "STDERR",
+                        "recordAt": "2024-04-09T13:38:36.987400105Z"
+                    }
+                ]
+            }
+        }
+        """
+        params = {
+            "appId": app_id,
+            "appExecutionId": app_execution_id,
+        }
+
+        if limit:
+            params["limit"] = limit
+
+        if skip:
+            params["skip"] = skip
+
+        if log_stream:
+            params["stream"] = log_stream
+
+        if start_at:
+            params["recordAt"] = start_at
+
+        return self.saagie_api.client.execute(query=gql(GQL_GET_APP_LOG), variable_values=params)
diff --git a/saagieapi/apps/gql_queries.py b/saagieapi/apps/gql_queries.py
index 3435f9c..f8a6770 100644
--- a/saagieapi/apps/gql_queries.py
+++ b/saagieapi/apps/gql_queries.py
@@ -452,3 +452,19 @@
     countAppHistoryStatuses(appHistoryId: $appHistoryId, versionNumber: $versionNumber, startTime: $startTime) 
 }
 """
+
+GQL_GET_APP_LOG = """
+query appLogs($appId: UUID!, $appExecutionId: UUID!, $limit: Int, $skip: Int, $stream: LogStream, $recordAt: String) {
+    appLogs(appId: $appId, appExecutionId: $appExecutionId, limit: $limit, skip: $skip, stream: $stream, recordAt: $recordAt) 
+    {
+        count
+      	content {
+          index
+          value
+          containerId
+          stream
+          recordAt
+        }
+    }
+}
+"""
diff --git a/saagieapi/env_vars/env_vars.py b/saagieapi/env_vars/env_vars.py
index 4847756..9653cb3 100644
--- a/saagieapi/env_vars/env_vars.py
+++ b/saagieapi/env_vars/env_vars.py
@@ -16,9 +16,9 @@ def handle_error(msg, project_id):
     return False
 
 
-def check_scope(scope, project_id, pipeline_id):
-    if scope not in ("GLOBAL", "PROJECT", "PIPELINE"):
-        raise ValueError("❌ 'scope' must be one of GLOBAL, PROJECT or PIPELINE")
+def check_scope(scope, project_id, pipeline_id, app_id):
+    if scope not in ("GLOBAL", "PROJECT", "PIPELINE", "APP"):
+        raise ValueError("❌ 'scope' must be one of GLOBAL, PROJECT, PIPELINE or APP")
 
     if scope == "PROJECT" and project_id is None:
         raise ValueError("❌ 'project_id' must be provided for scope PROJECT")
@@ -26,6 +26,9 @@ def check_scope(scope, project_id, pipeline_id):
     if scope == "PIPELINE" and pipeline_id is None:
         raise ValueError("❌ 'pipeline_id' must be provided for scope PIPELINE")
 
+    if scope == "APP" and app_id is None:
+        raise ValueError("❌ 'app_id' must be provided for scope APP")
+
 
 class EnvVars:
     # pylint: disable=singleton-comparison
@@ -977,6 +980,7 @@ def list(
         scope: str,
         project_id: str = None,
         pipeline_id: str = None,
+        app_id: str = None,
         scope_only: bool = False,
         pprint_result: Optional[bool] = None,
     ) -> Dict:
@@ -992,6 +996,8 @@ def list(
             UUID of your project (see README on how to find it)
         pipeline_id : str, optional
             UUID of your pipeline (see README on how to find it)
+        app_id : str, optional
+            UUID of your app (see README on how to find it)
         scope_only : bool, optional
             Whether to return only the environment variables of the given scope
         pprint_result : bool, optional
@@ -1031,7 +1037,7 @@ def list(
             }
         ]
         """
-        check_scope(scope, project_id, pipeline_id)
+        check_scope(scope, project_id, pipeline_id, app_id)
 
         if scope == "GLOBAL":
             res = self.saagie_api.client.execute(query=gql(GQL_LIST_GLOBAL_ENV_VARS), pprint_result=pprint_result)[
@@ -1049,6 +1055,12 @@ def list(
                 variable_values={"pipelineId": pipeline_id},
                 pprint_result=pprint_result,
             )["pipelineEnvironmentVariables"]
+        elif scope == "APP":
+            res = self.saagie_api.client.execute(
+                query=gql(GQL_LIST_APP_ENV_VARS),
+                variable_values={"appId": app_id},
+                pprint_result=pprint_result,
+            )["appEnvironmentVariables"]
 
         return [env for env in res if env["scope"] == scope] if scope_only else res
 
@@ -1058,6 +1070,7 @@ def get(
         name: str,
         project_id: str = None,
         pipeline_id: str = None,
+        app_id: str = None,
         scope_only: bool = False,
         pprint_result: Optional[bool] = None,
     ) -> Dict:
@@ -1076,6 +1089,8 @@ def get(
             UUID of your project (see README on how to find it)
         pipeline_id : str, optional
             UUID of your pipeline (see README on how to find it)
+        app_id : str, optional
+            UUID of your app (see README on how to find it)
         scope_only : bool, optional
             Whether to return only the environment variables of the given scope
         pprint_result : bool, optional
@@ -1101,9 +1116,9 @@ def get(
             'invalidReasons': None
         }
         """
-        check_scope(scope, project_id, pipeline_id)
+        check_scope(scope, project_id, pipeline_id, app_id)
 
-        env_vars = self.list(scope, project_id, pipeline_id, scope_only, pprint_result)
+        env_vars = self.list(scope, project_id, pipeline_id, app_id, scope_only, pprint_result)
 
         return next((d for d in env_vars if d["name"] == name), None)
 
@@ -1116,6 +1131,7 @@ def create(
         is_password: bool = False,
         project_id: str = None,
         pipeline_id: str = None,
+        app_id: str = None,
     ) -> Dict:
         """Create an environment variable in a given scope
 
@@ -1135,6 +1151,8 @@ def create(
             UUID of your project (see README on how to find it)
         pipeline_id : str, optional
             UUID of your pipeline (see README on how to find it)
+        app_id : str, optional
+            UUID of your app (see README on how to find it)
 
         Returns
         -------
@@ -1158,7 +1176,7 @@ def create(
         }
         """
 
-        check_scope(scope, project_id, pipeline_id)
+        check_scope(scope, project_id, pipeline_id, app_id)
 
         params = {
             "envVar": {
@@ -1170,10 +1188,12 @@ def create(
             },
         }
 
-        if scope == "PIPELINE":
-            params["entityId"] = pipeline_id
-        elif scope == "PROJECT":
+        if scope == "PROJECT":
             params["entityId"] = project_id
+        elif scope == "PIPELINE":
+            params["entityId"] = pipeline_id
+        elif scope == "APP":
+            params["entityId"] = app_id
 
         result = self.saagie_api.client.execute(query=gql(GQL_CREATE_ENV_VAR), variable_values=params)
         logging.info("✅ Environment variable [%s] successfully created", name)
@@ -1189,6 +1209,7 @@ def update(
         is_password: bool = None,
         project_id: str = None,
         pipeline_id: str = None,
+        app_id: str = None,
     ) -> Dict:
         """
         Update environment variable with provided function variables if it exists
@@ -1211,6 +1232,8 @@ def update(
             UUID of your project (see README on how to find it)
         pipeline_id : str, optional
             UUID of your pipeline (see README on how to find it)
+        app_id : str, optional
+            UUID of your app (see README on how to find it)
 
         Returns
         -------
@@ -1239,9 +1262,9 @@ def update(
         }
         """
 
-        check_scope(scope, project_id, pipeline_id)
+        check_scope(scope, project_id, pipeline_id, app_id)
 
-        existing_env_var = self.list(scope, project_id, pipeline_id, scope_only=True, pprint_result=False)
+        existing_env_var = self.list(scope, project_id, pipeline_id, app_id, scope_only=True, pprint_result=False)
 
         var = next((d for d in existing_env_var if d["name"] == name), None)
         if var is None:
@@ -1255,6 +1278,8 @@ def update(
             params["entityId"] = project_id
         elif scope == "PIPELINE":
             params["entityId"] = pipeline_id
+        elif scope == "APP":
+            params["entityId"] = app_id
 
         params["envVar"].pop("isValid", None)
         params["envVar"].pop("overriddenValues", None)
@@ -1282,6 +1307,7 @@ def create_or_update(
         is_password: bool = None,
         project_id: str = None,
         pipeline_id: str = None,
+        app_id: str = None,
     ) -> Dict:
         """
         Create a new environment variable or update it if it already exists
@@ -1302,6 +1328,8 @@ def create_or_update(
             UUID of your project (see README on how to find it)
         pipeline_id : str, optional
             UUID of your pipeline (see README on how to find it)
+        app_id : str, optional
+            UUID of your app (see README on how to find it)
 
         Returns
         -------
@@ -1324,9 +1352,9 @@ def create_or_update(
         }
         """
 
-        check_scope(scope, project_id, pipeline_id)
+        check_scope(scope, project_id, pipeline_id, app_id)
 
-        existing_env_var = self.list(scope, project_id, pipeline_id, scope_only=True, pprint_result=False)
+        existing_env_var = self.list(scope, project_id, pipeline_id, app_id, scope_only=True, pprint_result=False)
 
         var = next((d for d in existing_env_var if d["name"] == name), None)
 
@@ -1340,6 +1368,7 @@ def create_or_update(
                 "is_password": is_password,
                 "project_id": project_id,
                 "pipeline_id": pipeline_id,
+                "app_id": app_id,
             }
             args2 = {k: v for k, v in args.items() if v is not None}
             return self.create(**args2)
@@ -1352,6 +1381,7 @@ def create_or_update(
             is_password=is_password,
             project_id=project_id,
             pipeline_id=pipeline_id,
+            app_id=app_id,
         )
 
     def delete(
@@ -1360,6 +1390,7 @@ def delete(
         name: str,
         project_id: str = None,
         pipeline_id: str = None,
+        app_id: str = None,
     ) -> Dict:
         """Delete a given environment variable inside a given scope
 
@@ -1373,6 +1404,8 @@ def delete(
             UUID of your project (see README on how to find it)
         pipeline_id : str, optional
             UUID of your pipeline (see README on how to find it)
+        app_id : str, optional
+            UUID of your app (see README on how to find it)
 
         Returns
         -------
@@ -1392,9 +1425,9 @@ def delete(
             "deleteEnvironmentVariable": True
         }
         """
-        check_scope(scope, project_id, pipeline_id)
+        check_scope(scope, project_id, pipeline_id, app_id)
 
-        existing_env_var = self.list(scope, project_id, pipeline_id, scope_only=True, pprint_result=False)
+        existing_env_var = self.list(scope, project_id, pipeline_id, app_id, scope_only=True, pprint_result=False)
 
         var = next((d for d in existing_env_var if d["name"] == name), None)
         if var is None:
diff --git a/saagieapi/env_vars/gql_queries.py b/saagieapi/env_vars/gql_queries.py
index a51ea34..8a0481f 100644
--- a/saagieapi/env_vars/gql_queries.py
+++ b/saagieapi/env_vars/gql_queries.py
@@ -103,6 +103,35 @@
 }
 """
 
+GQL_LIST_APP_ENV_VARS = """
+query appEnvironmentVariablesQuery($appId: UUID!, 
+                                   $scopeFilter: EnvVarScope) {  
+    appEnvironmentVariables(appId: $appId, 
+                            scope: $scopeFilter
+    ) {    
+        id
+        scope
+        name
+        value
+        description
+        isPassword
+        isValid
+        overriddenValues {
+            id
+            scope
+            value
+            description
+            isPassword
+        }
+        invalidReasons{
+            type
+            concernedProperty
+            message
+        }
+    }
+}
+"""
+
 GQL_CREATE_PIPELINE_ENV_VAR = """
 mutation replaceEnvironmentVariablesByRawForScope($entityId: UUID, 
                                                   $scope: EnvVarScope!, 
diff --git a/saagieapi/jobs/jobs.py b/saagieapi/jobs/jobs.py
index 070d2e9..d38b86d 100644
--- a/saagieapi/jobs/jobs.py
+++ b/saagieapi/jobs/jobs.py
@@ -728,7 +728,7 @@ def create(
             params["resources"] = resources
 
         if source_url:
-            params["source_url"] = source_url
+            params["sourceUrl"] = source_url
 
         result = self.__launch_request(file, GQL_CREATE_JOB, params)
         logging.info("✅ Job [%s] successfully created", job_name)
diff --git a/saagieapi/pipelines/gql_queries.py b/saagieapi/pipelines/gql_queries.py
index c10ba86..d549f60 100644
--- a/saagieapi/pipelines/gql_queries.py
+++ b/saagieapi/pipelines/gql_queries.py
@@ -87,6 +87,7 @@
                 creator
                 isCurrent
                 isMajor
+                sourceUrl
             }
             creationDate
             creator
@@ -178,6 +179,7 @@
             creator
             isCurrent
             isMajor
+            sourceUrl
         }
         creationDate
         creator
@@ -272,6 +274,102 @@
             creator
             isCurrent
             isMajor
+            sourceUrl
+        }
+        creationDate
+        creator
+        isScheduled
+        cronScheduling
+        scheduleStatus
+        scheduleTimezone
+        isLegacyPipeline
+        hasExecutionVariablesEnabled
+    }
+}
+"""
+
+GQL_GET_PIPELINE_BY_ALIAS = """
+query graphPipelineByAliasQuery(
+    $projectId: UUID!, 
+    $pipelineAlias: String!,
+    $instancesLimit: Int, 
+    $versionsLimit: Int, 
+    $versionsOnlyCurrent: Boolean
+) {
+    graphPipelineByAlias(projectId: $projectId, alias: $pipelineAlias) {
+        id
+        name
+        alias
+        description
+        alerting{
+            emails
+            loginEmails{
+                login
+                email
+            }
+            statusList
+        }
+        pipelineInstanceCount
+        instances(limit: $instancesLimit){
+            id
+            status
+            startTime
+            endTime
+            runWithExecutionVariables
+            initialExecutionVariables{
+                key
+                value
+                isPassword
+            }
+            jobsInstance{
+                id
+                jobId
+                number
+                startTime
+                endTime
+            }
+            conditionsInstance{
+                id
+                conditionNodeId
+                isSuccess
+                startTime
+                endTime
+            }
+        }
+        versions(limit: $versionsLimit, onlyCurrent: $versionsOnlyCurrent) {
+            number
+            releaseNote
+            graph{
+                jobNodes{
+                    id
+                    job{
+                        id
+                        name
+                    }
+                    position{
+                        x
+                        y
+                    }
+                    nextNodes
+                }
+                conditionNodes{
+                    id
+                    position{
+                        x
+                        y
+                    }
+                    nextNodesSuccess
+                    nextNodesFailure
+                    condition {
+                        toString
+                    }
+                }
+            }
+            creationDate
+            creator
+            isCurrent
+            isMajor
+            sourceUrl
         }
         creationDate
         creator
@@ -388,7 +486,8 @@
                                      $scheduleTimezone:TimeZone,
                                      $jobNodes: [JobNodeInput!], 
                                      $conditionNodes: [ConditionNodeInput!],
-                                     $alias: String!) {
+                                     $alias: String!,
+                                     $sourceUrl: String) {
     createGraphPipeline(
         pipeline: {
             name: $name
@@ -404,6 +503,7 @@
                 conditionNodes: $conditionNodes
             }
             alias: $alias
+            sourceUrl: $sourceUrl
         }
     ) {
         id
@@ -423,14 +523,16 @@
 mutation($id: UUID!, 
 		 $jobNodes: [JobNodeInput!], 
          $conditionNodes: [ConditionNodeInput!], 
-         $releaseNote: String){
+         $releaseNote: String,
+         $sourceUrl: String){
     addGraphPipelineVersion(
         pipelineId: $id,
         graph: {
             jobNodes: $jobNodes,
             conditionNodes: $conditionNodes
         },
-        releaseNote: $releaseNote
+        releaseNote: $releaseNote,
+        sourceUrl: $sourceUrl
     ){
         number,
         releaseNote,
diff --git a/saagieapi/pipelines/pipelines.py b/saagieapi/pipelines/pipelines.py
index 5f50090..d698b80 100644
--- a/saagieapi/pipelines/pipelines.py
+++ b/saagieapi/pipelines/pipelines.py
@@ -660,6 +660,170 @@ def get_info_by_name(
             query=gql(GQL_GET_PIPELINE_BY_NAME), variable_values=params, pprint_result=pprint_result
         )
 
+    def get_info_by_alias(
+        self,
+        project_id: str,
+        pipeline_alias: str,
+        instances_limit: Optional[int] = None,
+        versions_limit: Optional[int] = None,
+        versions_only_current: bool = False,
+        pprint_result: Optional[bool] = None,
+    ) -> Dict:
+        """Get a given pipeline information by giving its alias
+
+        Parameters
+        ----------
+        project_id : str
+            UUID of your pipeline  (see README on how to find it)
+        pipeline_alias : str
+            Alias of your pipeline
+        instances_limit : int, optional
+            Maximum limit of instances to fetch per job. Fetch from most recent
+            to the oldest
+        versions_limit : int, optional
+            Maximum limit of versions to fetch per pipeline. Fetch from most recent
+            to the oldest
+        versions_only_current : bool, optional
+            Whether to only fetch the current version of each pipeline
+        pprint_result : bool, optional
+            Whether to pretty print the result of the query, default to
+            saagie_api.pprint_global
+
+        Returns
+        -------
+        dict
+            Dict of pipeline's information
+
+        Examples
+        --------
+        >>> saagieapi.pipelines.get_info_by_alias(
+        ...     project_id=""8321e13c-892a-4481-8552-5be4d6cc5df4",
+        ...     pipeline_id="Pipeline A"
+        ... )
+        {
+            "graphPipelineByAlias": {
+                "id": "5d1999f5-fa70-47d9-9f41-55ad48333629",
+                "name": "Pipeline A",
+                "description": "My Pipeline A",
+                "alerting": "NULL",
+                "pipelineInstanceCount": 0,
+                "instances": [
+                    {
+                        "id": "cc11c32a-66c5-43ad-b176-444cee7079ff",
+                        "status": "SUCCEEDED",
+                        "startTime": "2022-03-15T11:42:07.559Z",
+                        "endTime": "2022-03-15T11:43:17.716Z",
+                        "runWithExecutionVariables": True,
+                        "initialExecutionVariables": [
+                            {
+                                "key": "TEST_PASSWORD",
+                                "value": None,
+                                "isPassword": True
+                            },
+                            {
+                                "key": "TEST_PROJECT",
+                                "value": "TEST_PROJECT",
+                                "isPassword": False
+                            }
+                        ],
+                        "jobsInstance": [
+                            {
+                                "id": "f8e77fc3-9c4d-450b-8efd-9d3080b38edb",
+                                "jobId": "9a71afa4-aed4-4061-87d2-b279a3adf8c3",
+                                "number": 80,
+                                "startTime": "2022-03-15T11:42:07.559Z",
+                                "endTime": "2022-03-15T11:43:17.716Z"
+                            }
+                        ],
+                        "conditionsInstance": [
+                            {
+                                "id": "2292a535-affb-4b1c-973d-690c185d949e",
+                                "conditionNodeId": "c2f23720-e361-11ed-894d-6b696861cc8f",
+                                "isSuccess": true,
+                                "startTime": "2022-03-15T11:42:30.559Z",
+                                "endTime": "2022-03-15T11:42:45.559Z"
+                            }
+                        ],
+                    },
+                    {
+                        "id": "d7aba110-3bd9-4505-b70c-84c4d212345",
+                        "status": "SUCCEEDED",
+                        "startTime": "2022-02-04T00:00:00.062Z",
+                        "endTime": "2022-02-04T00:00:27.249Z",
+                        "runWithExecutionVariables": False,
+                        "initialExecutionVariables": [],
+                        "jobsInstance": [],
+                        "conditionsInstance": [],
+                    }
+                ],
+                "versions": [
+                    {
+                        "number": 1,
+                        "releaseNote": None,
+                        "graph": {
+                            "jobNodes": [
+                                {
+                                    "id": "00000000-0000-0000-0000-000000000000",
+                                    "job": {
+                                        "id": "6f56e714-37e4-4596-ae20-7016a1d954e9",
+                                        "name": "Spark 2.4 java"
+                                    },
+                                    "position": None,
+                                    "nextNodes": ["00000000-0000-0000-0000-000000000001"]
+                                },
+                                {
+                                    "id": "00000000-0000-0000-0000-000000000001",
+                                    "job": {
+                                        "id": "6ea1b022-db8b-4af7-885b-56ddc9ba764a", "name": "bash"
+                                    },
+                                    "position": None,
+                                    "nextNodes": []
+                                }
+                            ],
+                            "conditionNodes": [
+                                {
+                                    "id": "00000000-0000-0000-0000-000000000001",
+                                    "position": {
+                                        "x": 310.00092,
+                                        "y": 75
+                                    },
+                                    "nextNodesSuccess": [
+                                        "00000000-0000-0000-0000-000000000002"
+                                    ],
+                                    "nextNodesFailure": [],
+                                    "condition": {
+                                        "toString": "ConditionExpression(expression=\"tube_name.contains(\"Tube\") || double(diameter) > 1.0\")"
+                                    }
+                                }
+                            ]
+                        },
+                        "creationDate": "2022-01-31T10:36:42.327Z",
+                        "creator": "john.doe",
+                        "isCurrent": True,
+                        "isMajor": False
+                    }
+                ],
+                "creationDate": "2022-01-31T10:36:42.327Z",
+                "creator": "john.doe",
+                "isScheduled": False,
+                "cronScheduling": None,
+                "scheduleStatus": None,
+                "scheduleTimezone": "UTC",
+                "isLegacyPipeline": False
+            }
+        }
+        """  # pylint: disable=line-too-long
+        params = {
+            "projectId": project_id,
+            "pipelineAlias": pipeline_alias,
+            "instancesLimit": instances_limit,
+            "versionsLimit": versions_limit,
+            "versionsOnlyCurrent": versions_only_current,
+        }
+        return self.saagie_api.client.execute(
+            query=gql(GQL_GET_PIPELINE_BY_ALIAS), variable_values=params, pprint_result=pprint_result
+        )
+
     def get_instance(self, pipeline_instance_id: str, pprint_result: Optional[bool] = None) -> Dict:
         """
         Get the information of a given pipeline instance id
@@ -739,6 +903,7 @@ def create_graph(
         cron_scheduling: str = None,
         schedule_timezone: str = "UTC",
         has_execution_variables_enabled: bool = None,
+        source_url: str = "",
     ) -> Dict:
         """
         Create a pipeline in a given project
@@ -764,9 +929,9 @@ def create_graph(
         release_note: str, optional
             Release note of the pipeline
         emails: List[String], optional
-            Emails to receive alerts for the job, each item should be a valid email,
+            Emails to receive alerts for the pipeline, each item should be a valid email,
         status_list: List[String], optional
-            Receive an email when the job status change to a specific status
+            Receive an email when the pipeline status change to a specific status
             Each item of the list should be one of these following values: "REQUESTED", "QUEUED",
             "RUNNING", "FAILED", "KILLED", "KILLING", "SUCCEEDED", "UNKNOWN", "AWAITING", "SKIPPED"
         cron_scheduling : str, optional
@@ -775,6 +940,8 @@ def create_graph(
             Timezone of the scheduling
         has_execution_variables_enabled: bool, optional
             Boolean to activate or desactivate the execution variables
+        source_url: str, optional
+            URL of the source code used for the pipeline (link to the commit for example)
 
         Returns
         -------
@@ -845,6 +1012,9 @@ def create_graph(
         if has_execution_variables_enabled:
             params["hasExecutionVariablesEnabled"] = has_execution_variables_enabled
 
+        if source_url:
+            params["sourceUrl"] = source_url
+
         result = self.saagie_api.client.execute(query=gql(GQL_CREATE_GRAPH_PIPELINE), variable_values=params)
         logging.info("✅ Pipeline [%s] successfully created", name)
         return result
@@ -874,16 +1044,21 @@ def delete(self, pipeline_id: str) -> Dict:
         logging.info("✅ Pipeline [%s] successfully deleted", pipeline_id)
         return result
 
-    def upgrade(self, pipeline_id: str, graph_pipeline: GraphPipeline, release_note: str = "") -> Dict:
+    def upgrade(
+        self, pipeline_id: str, graph_pipeline: GraphPipeline, release_note: str = "", source_url: str = ""
+    ) -> Dict:
         """
         Upgrade a pipeline in a given project
 
         Parameters
         ----------
-        pipeline_id: str, ID of pipeline
+        pipeline_id: str,
+            UUID of pipeline
         graph_pipeline : GraphPipeline
         release_note: str, optional
             Release note of the pipeline
+        source_url: str, optional
+            URL of the source code used for the pipeline (link to the commit for example)
 
         Returns
         -------
@@ -957,6 +1132,9 @@ def upgrade(self, pipeline_id: str, graph_pipeline: GraphPipeline, release_note:
             "releaseNote": release_note,
         }
 
+        if source_url:
+            params["sourceUrl"] = source_url
+
         result = self.saagie_api.client.execute(query=gql(GQL_UPGRADE_PIPELINE), variable_values=params)
         logging.info("✅ Pipeline [%s] successfully upgraded", pipeline_id)
         return result
@@ -1090,6 +1268,7 @@ def create_or_upgrade(
         cron_scheduling: str = None,
         schedule_timezone: str = "UTC",
         has_execution_variables_enabled: bool = None,
+        source_url: str = None,
     ) -> Dict:
         """Create or upgrade a pipeline in a given project
 
@@ -1115,11 +1294,11 @@ def create_or_upgrade(
         release_note: str, optional
             Release note of the pipeline
         emails: List[String], optional
-            Emails to receive alerts for the job, each item should be a valid email,
+            Emails to receive alerts for the pipeline, each item should be a valid email,
             If you want to remove alerting, please set emails to [] or list()
             if not filled, defaults to current value
         status_list: List[String], optional
-            Receive an email when the job status change to a specific status
+            Receive an email when the pipeline status change to a specific status
             Each item of the list should be one of these following values: "REQUESTED", "QUEUED",
             "RUNNING", "FAILED", "KILLED", "KILLING", "SUCCEEDED", "UNKNOWN", "AWAITING", "SKIPPED"
         is_scheduled : bool, optional
@@ -1134,6 +1313,8 @@ def create_or_upgrade(
             Example: "UTC", "Pacific/Pago_Pago"
         has_execution_variables_enabled: bool, optional
             Boolean to activate or desactivate the execution variables
+        source_url: str, optional
+            URL of the source code used for the pipeline (link to the commit for example)
 
         Returns
         -------
@@ -1187,7 +1368,7 @@ def create_or_upgrade(
                     has_execution_variables_enabled=has_execution_variables_enabled,
                 )["editPipeline"]
             }
-            responses["addGraphPipelineVersion"] = self.upgrade(pipeline_id, graph_pipeline, release_note)[
+            responses["addGraphPipelineVersion"] = self.upgrade(pipeline_id, graph_pipeline, release_note, source_url)[
                 "addGraphPipelineVersion"
             ]
 
@@ -1207,6 +1388,7 @@ def create_or_upgrade(
                 "cron_scheduling": cron_scheduling,
                 "schedule_timezone": schedule_timezone,
                 "has_execution_variables_enabled": has_execution_variables_enabled,
+                "source_url": source_url,
             }.items()
             if v is not None  # Remove None values from the dict
         }
diff --git a/tests/integration/apps_integration_test.py b/tests/integration/apps_integration_test.py
index 45ce680..2c9f935 100644
--- a/tests/integration/apps_integration_test.py
+++ b/tests/integration/apps_integration_test.py
@@ -347,3 +347,18 @@ def test_count_history_statuses(create_global_project, create_then_delete_app_fr
         )
 
         assert "countAppHistoryStatuses" in result
+
+    @staticmethod
+    def test_get_app_logs(create_global_project, create_then_delete_app_from_scratch):
+        conf = create_global_project
+        app_id = create_then_delete_app_from_scratch
+
+        app_info = conf.saagie_api.apps.run(app_id=app_id)
+
+        app_info = conf.saagie_api.apps.get_info(app_id=app_id)
+
+        app_execution_id = app_info["app"]["history"]["currentExecutionId"]
+
+        result = conf.saagie_api.apps.get_logs(app_id=app_id, app_execution_id=app_execution_id)
+
+        assert "appLogs" in result
diff --git a/tests/integration/pipelines_integration_test.py b/tests/integration/pipelines_integration_test.py
index 397d177..2c09898 100644
--- a/tests/integration/pipelines_integration_test.py
+++ b/tests/integration/pipelines_integration_test.py
@@ -454,3 +454,14 @@ def test_duplicate_pipeline(create_global_project, create_then_delete_graph_pipe
         conf.saagie_api.pipelines.delete(pipeline_id=result["duplicatePipeline"]["id"])
 
         assert "duplicatePipeline" in result
+
+    @staticmethod
+    def test_get_info_by_alias(create_global_project, create_then_delete_graph_pipeline):
+        conf = create_global_project
+        pipeline_id, _ = create_then_delete_graph_pipeline
+
+        result = conf.saagie_api.pipelines.get_info_by_alias(
+            pipeline_alias="ALIAS_TEST_VIA_API", project_id=conf.project_id
+        )
+
+        assert result["graphPipelineByAlias"]["id"] == pipeline_id
diff --git a/tests/unit/apps_unit_test.py b/tests/unit/apps_unit_test.py
index ebf9466..f6423b3 100644
--- a/tests/unit/apps_unit_test.py
+++ b/tests/unit/apps_unit_test.py
@@ -252,6 +252,7 @@ def test_edit_app(self, saagie_api_mock):
         description = "new_description"
         emails = ["test@email.com"]
         status = ["RECOVERING", "FAILED"]
+        resources = {"cpu": {"request": 1.5, "limit": 2.2}, "memory": {"request": 2.0}}
 
         params = {
             "app": {
@@ -259,6 +260,7 @@ def test_edit_app(self, saagie_api_mock):
                 "name": name,
                 "description": description,
                 "alerting": {"statusList": status, "emails": emails},
+                "resources": resources,
             }
         }
 
@@ -267,7 +269,14 @@ def test_edit_app(self, saagie_api_mock):
         with patch.object(app, "get_info") as info, patch.object(app, "check_alerting") as chk_alert:
             info.return_value = {"app": {"alerting": {"statusList": status, "emails": emails}}}
             chk_alert.return_value = {"statusList": status, "emails": emails}
-            app.edit(app_id=app_id, app_name=name, description=description, emails=emails, status_list=status)
+            app.edit(
+                app_id=app_id,
+                app_name=name,
+                description=description,
+                emails=emails,
+                status_list=status,
+                resources=resources,
+            )
 
         saagie_api_mock.client.execute.assert_called_with(query=expected_query, variable_values=params)
 
@@ -776,3 +785,39 @@ def test_count_history_app_statuses(self, saagie_api_mock):
         app.count_history_statuses(history_id=history_id, version_number=1, start_time="2024-04-10T14:26:27.073Z")
 
         saagie_api_mock.client.execute.assert_called_with(query=expected_query, variable_values=params)
+
+    def test_get_app_logs_gql(self):
+        query = gql(GQL_GET_APP_LOG)
+        self.client.validate(query)
+
+    def test_get_app_logs(self, saagie_api_mock):
+        app = Apps(saagie_api_mock)
+
+        app_id = "70e85ade-d6cc-4a90-8d7d-639adbd25e5d"
+        app_execution_id = "e3e31074-4a12-450e-96e4-0eae7801dfca"
+        limit = 2
+        skip = 5
+        stream = "STDERR"
+        start_at = "2024-04-09 10:00:00"
+
+        params = {
+            "appId": app_id,
+            "appExecutionId": app_execution_id,
+            "limit": limit,
+            "skip": skip,
+            "stream": stream,
+            "recordAt": start_at,
+        }
+
+        expected_query = gql(GQL_GET_APP_LOG)
+
+        app.get_logs(
+            app_id=app_id,
+            app_execution_id=app_execution_id,
+            limit=limit,
+            skip=skip,
+            log_stream=stream,
+            start_at=start_at,
+        )
+
+        saagie_api_mock.client.execute.assert_called_with(query=expected_query, variable_values=params)
diff --git a/tests/unit/env_vars_unit_test.py b/tests/unit/env_vars_unit_test.py
index 092edb2..e0ada2e 100644
--- a/tests/unit/env_vars_unit_test.py
+++ b/tests/unit/env_vars_unit_test.py
@@ -20,133 +20,147 @@ class TestEnvVars:
     def setup_method(self):
         self.client = create_gql_client()
 
-    def test_list_global_env_vars_gql(self):
-        query = gql(GQL_LIST_GLOBAL_ENV_VARS)
-        self.client.validate(query)
-
-    def test_create_env_var_gql(self):
-        query = gql(GQL_CREATE_ENV_VAR)
-        self.client.validate(query)
-
-    def test_update_env_var_gql(self):
-        query = gql(GQL_UPDATE_ENV_VAR)
-        self.client.validate(query)
-
-    def test_delete_env_var_gql(self):
-        query = gql(GQL_DELETE_ENV_VAR)
-        self.client.validate(query)
-
-    def test_list_project_env_vars_gql(self):
-        query = gql(GQL_LIST_PROJECT_ENV_VARS)
-        self.client.validate(query)
-
-    def test_list_pipeline_env_vars_gql(self):
-        query = gql(GQL_LIST_PIPELINE_ENV_VARS)
-        self.client.validate(query)
-
-    def test_create_pipeline_env_vars_gql(self):
-        query = gql(GQL_CREATE_PIPELINE_ENV_VAR)
-        self.client.validate(query)
-
     @pytest.fixture
     def saagie_api_mock(self):
         saagie_api_mock = Mock()
         saagie_api_mock.client.execute = MagicMock()
         return saagie_api_mock
 
-    # Test of check_scope
     def test_check_scope_global(self):
-        check_scope(scope="GLOBAL", project_id=None, pipeline_id=None)
+        check_scope(scope="GLOBAL", project_id=None, pipeline_id=None, app_id=None)
 
     def test_check_scope_error(self):
         with pytest.raises(ValueError):
-            check_scope(scope="WRONG", project_id=None, pipeline_id=None)
+            check_scope(scope="WRONG", project_id=None, pipeline_id=None, app_id=None)
 
     def test_check_scope_project_success(self):
-        check_scope(scope="PROJECT", project_id="project_id", pipeline_id=None)
+        check_scope(scope="PROJECT", project_id="project_id", pipeline_id=None, app_id=None)
 
     def test_check_scope_project_error(self):
         with pytest.raises(ValueError):
-            check_scope(scope="PROJECT", project_id=None, pipeline_id=None)
+            check_scope(scope="PROJECT", project_id=None, pipeline_id=None, app_id=None)
 
     def test_check_scope_pipeline_success(self):
-        check_scope(scope="PIPELINE", project_id=None, pipeline_id="pipeline_id")
+        check_scope(scope="PIPELINE", project_id=None, pipeline_id="pipeline_id", app_id=None)
 
     def test_check_scope_pipeline_error(self):
         with pytest.raises(ValueError):
-            check_scope(scope="PIPELINE", project_id=None, pipeline_id=None)
+            check_scope(scope="PIPELINE", project_id=None, pipeline_id=None, app_id=None)
+
+    def test_list_global_env_vars_gql(self):
+        query = gql(GQL_LIST_GLOBAL_ENV_VARS)
+        self.client.validate(query)
 
-    # Test the `list` method
     def test_list_global_vars(self, saagie_api_mock):
-        # Create an instance of EnvVars with the mock saagie_api
         env_vars = EnvVars(saagie_api_mock)
 
-        # Set up the expected parameters
         scope = "GLOBAL"
-        project_id = None
-        pipeline_id = None
         scope_only = False
         pprint_result = True
 
-        # Define the expected query
         expected_query = gql(GQL_LIST_GLOBAL_ENV_VARS)
 
-        # Call the function under test
-        env_vars.list(scope, project_id, pipeline_id, scope_only, pprint_result)
+        env_vars.list(scope=scope, scope_only=scope_only, pprint_result=pprint_result)
 
-        # Assert that the query was executed with the expected parameters
         saagie_api_mock.client.execute.assert_called_with(query=expected_query, pprint_result=pprint_result)
 
+    def test_list_project_env_vars_gql(self):
+        query = gql(GQL_LIST_PROJECT_ENV_VARS)
+        self.client.validate(query)
+
     def test_list_project_vars(self, saagie_api_mock):
-        # Create an instance of EnvVars with the mock saagie_api
         env_vars = EnvVars(saagie_api_mock)
 
-        # Set up the expected parameters
         scope = "PROJECT"
         project_id = "MY_PROJECT_ID"
-        pipeline_id = None
         scope_only = False
         pprint_result = True
 
-        # Define the expected query
         expected_query = gql(GQL_LIST_PROJECT_ENV_VARS)
 
-        # Call the function under test
-        env_vars.list(scope, project_id, pipeline_id, scope_only, pprint_result)
+        env_vars.list(scope=scope, project_id=project_id, scope_only=scope_only, pprint_result=pprint_result)
 
-        # Assert that the query was executed with the expected parameters
         saagie_api_mock.client.execute.assert_called_with(
             query=expected_query, variable_values={"projectId": project_id}, pprint_result=pprint_result
         )
 
+    def test_list_pipeline_env_vars_gql(self):
+        query = gql(GQL_LIST_PIPELINE_ENV_VARS)
+        self.client.validate(query)
+
     def test_list_pipeline_vars(self, saagie_api_mock):
-        # Create an instance of EnvVars with the mock saagie_api
         env_vars = EnvVars(saagie_api_mock)
 
-        # Set up the expected parameters
         scope = "PIPELINE"
-        project_id = "MY_PROJECT_ID"
         pipeline_id = "MY_PIPELINE_ID"
         scope_only = False
         pprint_result = True
 
-        # Define the expected query
         expected_query = gql(GQL_LIST_PIPELINE_ENV_VARS)
 
-        # Call the function under test
-        env_vars.list(scope, project_id, pipeline_id, scope_only, pprint_result)
+        env_vars.list(scope=scope, pipeline_id=pipeline_id, scope_only=scope_only, pprint_result=pprint_result)
 
-        # Assert that the query was executed with the expected parameters
         saagie_api_mock.client.execute.assert_called_with(
             query=expected_query, variable_values={"pipelineId": pipeline_id}, pprint_result=pprint_result
         )
 
-    # Test the `create` method
-    @patch("saagieapi.env_vars.env_vars.check_scope", return_value=True)
-    def test_create_global_variable(self, check_scope_mock, saagie_api_mock):
+    def test_list_app_env_vars_gql(self):
+        query = gql(GQL_LIST_APP_ENV_VARS)
+        self.client.validate(query)
+
+    def test_list_app_vars(self, saagie_api_mock):
+        env_vars = EnvVars(saagie_api_mock)
+
+        scope = "APP"
+        app_id = "MY_APP_ID"
+        scope_only = False
+        pprint_result = True
+
+        expected_query = gql(GQL_LIST_APP_ENV_VARS)
+
+        env_vars.list(scope=scope, app_id=app_id, scope_only=scope_only, pprint_result=pprint_result)
+
+        saagie_api_mock.client.execute.assert_called_with(
+            query=expected_query, variable_values={"appId": app_id}, pprint_result=pprint_result
+        )
+
+    def test_get_valid_env_var(self, saagie_api_mock):
+        saagie_api_mock.env_vars.env_vars.check_scope.return_value = True
+        instance = EnvVars(saagie_api_mock)
+
+        scope = "GLOBAL"
+        name = "TEST_VARIABLE"
+
+        with patch.object(instance, "list") as list_env:
+            list_env.return_value = [
+                {"id": "env_var_id", "name": "TEST_VARIABLE", "value": "value1", "scope": "GLOBAL"}
+            ]
+            result = instance.get(scope, name)
+
+        assert result["name"] == name
+
+    def test_get_invalid_env_var(self, saagie_api_mock):
+        saagie_api_mock.env_vars.env_vars.check_scope.return_value = True
+        instance = EnvVars(saagie_api_mock)
+
+        scope = "GLOBAL"
+        name = "INVALID_ENV_VAR"
+
+        with patch.object(instance, "list") as list_env:
+            list_env.return_value = [
+                {"id": "env_var_id", "name": "TEST_VARIABLE", "value": "value1", "scope": "GLOBAL"}
+            ]
+            result = instance.get(scope, name)
+
+        assert result is None
+
+    def test_create_env_var_gql(self):
+        query = gql(GQL_CREATE_ENV_VAR)
+        self.client.validate(query)
+
+    def test_create_global_variable(self, saagie_api_mock):
+        saagie_api_mock.env_vars.env_vars.check_scope.return_value = True
         instance = EnvVars(saagie_api_mock)
-        mock_env_vars = {"saveEnvironmentVariable": {"id": "env_var_id"}}
-        saagie_api_mock.client.execute.return_value = mock_env_vars
 
         name = "env_var1"
         value = "value1"
@@ -154,23 +168,26 @@ def test_create_global_variable(self, check_scope_mock, saagie_api_mock):
         is_password = False
         scope = "GLOBAL"
 
-        result = instance.create(scope=scope, name=name, value=value, description=description, is_password=is_password)
+        params = {
+            "envVar": {
+                "name": name,
+                "value": value,
+                "description": description,
+                "isPassword": is_password,
+                "scope": scope,
+            },
+        }
 
-        # Assertions
-        check_scope_mock.assert_called_with(scope, None, None)
-        arg1 = saagie_api_mock.client.execute.call_args.kwargs["query"]
-        assert arg1 == gql(GQL_CREATE_ENV_VAR)
-        assert "saveEnvironmentVariable" in result
-        assert "id" in result["saveEnvironmentVariable"]
-        assert isinstance(result["saveEnvironmentVariable"]["id"], str)
+        expected_query = gql(GQL_CREATE_ENV_VAR)
 
-    @patch("saagieapi.env_vars.env_vars.check_scope", return_value=True)
-    def test_create_project_variable(self, check_scope_mock, saagie_api_mock):
+        instance.create(scope=scope, name=name, value=value, description=description, is_password=is_password)
+
+        saagie_api_mock.client.execute.assert_called_with(query=expected_query, variable_values=params)
+
+    def test_create_project_variable(self, saagie_api_mock):
+        saagie_api_mock.env_vars.env_vars.check_scope.return_value = True
         instance = EnvVars(saagie_api_mock)
-        mock_env_vars = {"saveEnvironmentVariable": {"id": "env_var_id"}}
-        saagie_api_mock.client.execute.return_value = mock_env_vars
 
-        # Set up the expected parameters
         name = "env_var1"
         value = "value1"
         description = "description1"
@@ -178,441 +195,430 @@ def test_create_project_variable(self, check_scope_mock, saagie_api_mock):
         scope = "PROJECT"
         project_id = "MY_PROJECT_ID"
 
-        result = instance.create(
+        params = {
+            "envVar": {
+                "name": name,
+                "value": value,
+                "description": description,
+                "isPassword": is_password,
+                "scope": scope,
+            },
+            "entityId": project_id,
+        }
+
+        expected_query = gql(GQL_CREATE_ENV_VAR)
+
+        instance.create(
             scope=scope, name=name, value=value, description=description, project_id=project_id, is_password=is_password
         )
 
-        # Assertions
-        check_scope_mock.assert_called_with(scope, project_id, None)
-        arg1 = saagie_api_mock.client.execute.call_args.kwargs["query"]
-        assert arg1 == gql(GQL_CREATE_ENV_VAR)
-        assert "saveEnvironmentVariable" in result
-        assert "id" in result["saveEnvironmentVariable"]
-        assert isinstance(result["saveEnvironmentVariable"]["id"], str)
+        saagie_api_mock.client.execute.assert_called_with(query=expected_query, variable_values=params)
 
-    @patch("saagieapi.env_vars.env_vars.check_scope", return_value=True)
-    def test_create_pipeline_variable(self, check_scope_mock, saagie_api_mock):
+    def test_create_project_env_vars_error(self, saagie_api_mock):
+        instance = EnvVars(saagie_api_mock)
+        with pytest.raises(ValueError):
+            instance.create(scope="PROJECT", name="env_var_proj_1", value="value1")
+
+    def test_create_pipeline_variable(self, saagie_api_mock):
+        saagie_api_mock.env_vars.env_vars.check_scope.return_value = True
         instance = EnvVars(saagie_api_mock)
-        mock_env_vars = {"saveEnvironmentVariable": {"id": "env_var_id"}}
-        saagie_api_mock.client.execute.return_value = mock_env_vars
 
-        # Set up the expected parameters
         name = "env_var1"
         value = "value1"
         description = "description1"
         is_password = False
         scope = "PIPELINE"
         pipeline_id = "MY_PIPELINE_ID"
-        project_id = "MY_PROJECT_ID"
 
-        result = instance.create(
+        params = {
+            "envVar": {
+                "name": name,
+                "value": value,
+                "description": description,
+                "isPassword": is_password,
+                "scope": scope,
+            },
+            "entityId": pipeline_id,
+        }
+
+        expected_query = gql(GQL_CREATE_ENV_VAR)
+
+        instance.create(
             scope=scope,
             name=name,
             value=value,
             description=description,
-            project_id=project_id,
             pipeline_id=pipeline_id,
             is_password=is_password,
         )
 
-        # Assertions
-        check_scope_mock.assert_called_with(scope, project_id, pipeline_id)
-        arg1 = saagie_api_mock.client.execute.call_args.kwargs["query"]
-        assert arg1 == gql(GQL_CREATE_ENV_VAR)
-        assert "saveEnvironmentVariable" in result
-        assert "id" in result["saveEnvironmentVariable"]
-        assert isinstance(result["saveEnvironmentVariable"]["id"], str)
+        saagie_api_mock.client.execute.assert_called_with(query=expected_query, variable_values=params)
 
     def test_create_pipeline_env_vars_error(self, saagie_api_mock):
         instance = EnvVars(saagie_api_mock)
         with pytest.raises(ValueError):
             instance.create(scope="PIPELINE", name="env_var1", value="value1")
 
-    def test_create_project_env_vars_error(self, saagie_api_mock):
+    def test_create_app_variable(self, saagie_api_mock):
+        saagie_api_mock.env_vars.env_vars.check_scope.return_value = True
         instance = EnvVars(saagie_api_mock)
-        with pytest.raises(ValueError):
-            instance.create(scope="PROJECT", name="env_var_proj_1", value="value1")
 
-    # Test the `delete` method
-    @patch("saagieapi.env_vars.env_vars.check_scope", return_value=True)
-    @patch(
-        "saagieapi.env_vars.env_vars.EnvVars.list",
-        return_value=[
-            {
-                "id": "env_var_id",
-                "name": "env_var1",
-                "value": "value1",
-                "description": "description1",
-                "scope": "GLOBAL",
-            }
-        ],
-    )
-    def test_delete_env_var(self, env_list_mock, check_scope_mock, saagie_api_mock):
-        # Set up the expected parameters
-        instance = EnvVars(saagie_api_mock)
-        mock_env_vars = {"deleteEnvironmentVariable": True}
-        saagie_api_mock.client.execute.return_value = mock_env_vars
-        scope = "GLOBAL"
         name = "env_var1"
+        value = "value1"
+        description = "description1"
+        is_password = False
+        scope = "APP"
+        app_id = "MY_APP_ID"
+
+        params = {
+            "envVar": {
+                "name": name,
+                "value": value,
+                "description": description,
+                "isPassword": is_password,
+                "scope": scope,
+            },
+            "entityId": app_id,
+        }
 
-        # Test
-        env_var_result = instance.delete(scope=scope, name=name)
+        expected_query = gql(GQL_CREATE_ENV_VAR)
 
-        # Assertions
-        check_scope_mock.assert_called_with(scope, None, None)
-        env_list_mock.assert_called_with(scope, None, None, scope_only=True, pprint_result=False)
-        arg1 = saagie_api_mock.client.execute.call_args.kwargs["query"]
-        assert arg1 == gql(GQL_DELETE_ENV_VAR)
-        assert "deleteEnvironmentVariable" in env_var_result
-
-    @patch("saagieapi.env_vars.env_vars.check_scope", return_value=True)
-    @patch(
-        "saagieapi.env_vars.env_vars.EnvVars.list",
-        return_value=[
-            {
-                "id": "env_var_id",
-                "name": "env_var1",
-                "value": "value1",
-                "description": "description1",
-                "scope": "GLOBAL",
-            }
-        ],
-    )
-    def test_delete_env_var_not_exists(self, env_list_mock, check_scope_mock, saagie_api_mock):
-        instance = EnvVars(saagie_api_mock)
-        scope = "GLOBAL"
-        name = "NON_EXISTING_VARIABLE"
+        instance.create(
+            scope=scope,
+            name=name,
+            value=value,
+            description=description,
+            app_id=app_id,
+            is_password=is_password,
+        )
 
-        # Assert that the result is as expected
-        with pytest.raises(ValueError):
-            instance.delete(scope=scope, name=name)
-        check_scope_mock.assert_called_with(scope, None, None)
-        env_list_mock.assert_called_with(scope, None, None, scope_only=True, pprint_result=False)
-        saagie_api_mock.client.execute.assert_not_called()
-
-    # Test the `update` method
-    @patch("saagieapi.env_vars.env_vars.check_scope", return_value=True)
-    @patch(
-        "saagieapi.env_vars.env_vars.EnvVars.list",
-        return_value=[
-            {
-                "id": "env_var_id",
-                "name": "TEST_VARIABLE",
-                "value": "value1",
-                "description": "description1",
-                "scope": "GLOBAL",
-                "isPassword": False,
-            }
-        ],
-    )
-    def test_update_existing_global_variable(self, env_list_mock, check_scope_mock, saagie_api_mock):
+        saagie_api_mock.client.execute.assert_called_with(query=expected_query, variable_values=params)
+
+    def test_update_env_var_gql(self):
+        query = gql(GQL_UPDATE_ENV_VAR)
+        self.client.validate(query)
+
+    def test_update_existing_global_variable(self, saagie_api_mock):
+        saagie_api_mock.env_vars.env_vars.check_scope.return_value = True
         instance = EnvVars(saagie_api_mock)
-        # Parameters
+
         scope = "GLOBAL"
         name = "TEST_VARIABLE"
         value = "new value"
 
-        mock_env_vars = {"saveEnvironmentVariable": {"id": "env_var_id"}}
-        saagie_api_mock.client.execute.return_value = mock_env_vars
-
-        result = instance.update(scope=scope, name=name, value=value)
+        params = {
+            "envVar": {
+                "id": "env_var_id",
+                "name": name,
+                "value": value,
+                "description": "description1",
+                "isPassword": False,
+                "scope": scope,
+            },
+        }
 
-        # Assert that the result is as expected
-        check_scope_mock.assert_called_with(scope, None, None)
-        env_list_mock.assert_called_with(scope, None, None, scope_only=True, pprint_result=False)
-        assert "saveEnvironmentVariable" in result
+        expected_query = gql(GQL_UPDATE_ENV_VAR)
+
+        with patch.object(instance, "list") as list_env:
+            list_env.return_value = [
+                {
+                    "id": "env_var_id",
+                    "name": "TEST_VARIABLE",
+                    "value": "value1",
+                    "description": "description1",
+                    "scope": "GLOBAL",
+                    "isPassword": False,
+                }
+            ]
+            instance.update(scope=scope, name=name, value=value)
 
-        # Test the `update` method
+        saagie_api_mock.client.execute.assert_called_with(query=expected_query, variable_values=params)
 
-    @patch("saagieapi.env_vars.env_vars.check_scope", return_value=True)
-    @patch(
-        "saagieapi.env_vars.env_vars.EnvVars.list",
-        return_value=[
-            {
-                "id": "env_var_id",
-                "name": "TEST_VARIABLE",
-                "value": "value1",
-                "description": "description1",
-                "scope": "GLOBAL",
-                "isPassword": True,
-            }
-        ],
-    )
-    def test_update_existing_global_password_variable(self, env_list_mock, check_scope_mock, saagie_api_mock):
+    def test_update_existing_global_password_variable(self, saagie_api_mock):
+        saagie_api_mock.env_vars.env_vars.check_scope.return_value = True
         instance = EnvVars(saagie_api_mock)
-        # Parameters
+
         scope = "GLOBAL"
         name = "TEST_VARIABLE"
         value = "new value"
 
-        mock_env_vars = {"saveEnvironmentVariable": {"id": "env_var_id"}}
-        saagie_api_mock.client.execute.return_value = mock_env_vars
+        params = {
+            "envVar": {
+                "id": "env_var_id",
+                "name": name,
+                "value": value,
+                "description": "description1",
+                "isPassword": True,
+                "scope": scope,
+            },
+        }
 
-        result = instance.update(scope=scope, name=name, value=value, is_password=True)
+        expected_query = gql(GQL_UPDATE_ENV_VAR)
+
+        with patch.object(instance, "list") as list_env:
+            list_env.return_value = [
+                {
+                    "id": "env_var_id",
+                    "name": "TEST_VARIABLE",
+                    "value": "value1",
+                    "description": "description1",
+                    "scope": "GLOBAL",
+                    "isPassword": True,
+                }
+            ]
+            instance.update(scope=scope, name=name, value=value)
 
-        # Assert that the result is as expected
-        check_scope_mock.assert_called_with(scope, None, None)
-        env_list_mock.assert_called_with(scope, None, None, scope_only=True, pprint_result=False)
-        assert "saveEnvironmentVariable" in result
+        saagie_api_mock.client.execute.assert_called_with(query=expected_query, variable_values=params)
 
-    @patch("saagieapi.env_vars.env_vars.check_scope", return_value=True)
-    @patch(
-        "saagieapi.env_vars.env_vars.EnvVars.list",
-        return_value=[
-            {
-                "id": "env_var_id",
-                "name": "TEST_VARIABLE",
-                "value": "value1",
-                "description": "description1",
-                "scope": "PROJECT",
-                "isPassword": False,
-            }
-        ],
-    )
-    def test_update_existing_project_variable(self, env_list_mock, check_scope_mock, saagie_api_mock):
+    def test_update_existing_project_variable(self, saagie_api_mock):
+        saagie_api_mock.env_vars.env_vars.check_scope.return_value = True
         instance = EnvVars(saagie_api_mock)
-        # Parameters
+
         scope = "PROJECT"
         name = "TEST_VARIABLE"
+        new_name = "NEW_TEST_VARIABLE"
         value = "new value"
+        desc = "new description"
         project_id = "MY_PROJECT_ID"
 
-        mock_env_vars = {"saveEnvironmentVariable": {"id": "env_var_id"}}
-        saagie_api_mock.client.execute.return_value = mock_env_vars
-        # Act
-        result = instance.update(scope=scope, name=name, value=value, project_id=project_id)
-
-        # Assert that the result is as expected
-        check_scope_mock.assert_called_with(scope, project_id, None)
-        env_list_mock.assert_called_with(scope, project_id, None, scope_only=True, pprint_result=False)
-        assert "saveEnvironmentVariable" in result
-
-    @patch("saagieapi.env_vars.env_vars.check_scope", return_value=True)
-    @patch(
-        "saagieapi.env_vars.env_vars.EnvVars.list",
-        return_value=[
-            {
+        params = {
+            "envVar": {
                 "id": "env_var_id",
-                "name": "TEST_VARIABLE",
-                "value": "value1",
-                "description": "description1",
-                "scope": "PROJECT",
+                "name": new_name,
+                "value": value,
+                "description": desc,
                 "isPassword": False,
-            }
-        ],
-    )
-    def test_update_existing_project_variable_name(self, env_list_mock, check_scope_mock, saagie_api_mock):
-        instance = EnvVars(saagie_api_mock)
-        # Parameters
-        scope = "PROJECT"
-        name = "TEST_VARIABLE"
-        new_name = "NEW_TEST_VARIABLE"
-        value = "new value"
-        project_id = "MY_PROJECT_ID"
-        new_description = "new description for tests purpose"
+                "scope": scope,
+            },
+            "entityId": project_id,
+        }
 
-        mock_env_vars = {"saveEnvironmentVariable": {"id": "env_var_id"}}
-        saagie_api_mock.client.execute.return_value = mock_env_vars
-        # Act
-        result = instance.update(
-            scope=scope, name=name, value=value, project_id=project_id, new_name=new_name, description=new_description
-        )
+        expected_query = gql(GQL_UPDATE_ENV_VAR)
+
+        with patch.object(instance, "list") as list_env:
+            list_env.return_value = [
+                {
+                    "id": "env_var_id",
+                    "name": "TEST_VARIABLE",
+                    "value": "value1",
+                    "description": "description1",
+                    "scope": "PROJECT",
+                    "isPassword": False,
+                }
+            ]
+            instance.update(
+                scope=scope, name=name, new_name=new_name, value=value, description=desc, project_id=project_id
+            )
 
-        # Assert that the result is as expected
-        check_scope_mock.assert_called_with(scope, project_id, None)
-        env_list_mock.assert_called_with(scope, project_id, None, scope_only=True, pprint_result=False)
-        assert "saveEnvironmentVariable" in result
+        saagie_api_mock.client.execute.assert_called_with(query=expected_query, variable_values=params)
 
-    @patch("saagieapi.env_vars.env_vars.check_scope", return_value=True)
-    @patch(
-        "saagieapi.env_vars.env_vars.EnvVars.list",
-        return_value=[
-            {
-                "id": "env_var_id",
-                "name": "TEST_VARIABLE",
-                "value": "value1",
-                "description": "description1",
-                "scope": "PIPELINE",
-                "isPassword": False,
-            }
-        ],
-    )
-    def test_update_existing_pipeline_variable(self, env_list_mock, check_scope_mock, saagie_api_mock):
+    def test_update_existing_pipeline_variable(self, saagie_api_mock):
+        saagie_api_mock.env_vars.env_vars.check_scope.return_value = True
         instance = EnvVars(saagie_api_mock)
-        # Parameters
+
         scope = "PIPELINE"
         name = "TEST_VARIABLE"
         value = "new value"
-        project_id = "MY_PROJECT_ID"
         pipeline_id = "MY_PIPELINE_ID"
-        # Mocks
-        mock_env_vars = {"saveEnvironmentVariable": {"id": "env_var_id"}}
-        saagie_api_mock.client.execute.return_value = mock_env_vars
 
-        # Act
-        result = instance.update(scope=scope, name=name, value=value, project_id=project_id, pipeline_id=pipeline_id)
+        params = {
+            "envVar": {
+                "id": "env_var_id",
+                "name": name,
+                "value": value,
+                "description": "description1",
+                "isPassword": True,
+                "scope": scope,
+            },
+            "entityId": pipeline_id,
+        }
+
+        expected_query = gql(GQL_UPDATE_ENV_VAR)
+
+        with patch.object(instance, "list") as list_env:
+            list_env.return_value = [
+                {
+                    "id": "env_var_id",
+                    "name": "TEST_VARIABLE",
+                    "value": "value1",
+                    "description": "description1",
+                    "scope": "PIPELINE",
+                    "isPassword": False,
+                }
+            ]
+            instance.update(scope=scope, name=name, value=value, is_password=True, pipeline_id=pipeline_id)
+
+        saagie_api_mock.client.execute.assert_called_with(query=expected_query, variable_values=params)
 
-        # Assert that the result is as expected
-        check_scope_mock.assert_called_with(scope, project_id, pipeline_id)
-        env_list_mock.assert_called_with(scope, project_id, pipeline_id, scope_only=True, pprint_result=False)
-        assert "saveEnvironmentVariable" in result
+    def test_update_existing_app_variable(self, saagie_api_mock):
+        saagie_api_mock.env_vars.env_vars.check_scope.return_value = True
+        instance = EnvVars(saagie_api_mock)
 
-    @patch("saagieapi.env_vars.env_vars.check_scope", return_value=True)
-    @patch(
-        "saagieapi.env_vars.env_vars.EnvVars.list",
-        return_value=[
-            {
+        scope = "APP"
+        name = "TEST_VARIABLE"
+        value = "new value"
+        app_id = "MY_APP_ID"
+
+        params = {
+            "envVar": {
                 "id": "env_var_id",
-                "name": "TEST_VARIABLE",
-                "value": "value1",
+                "name": name,
+                "value": value,
                 "description": "description1",
-                "scope": "PROJECT",
-                "isPassword": False,
-            }
-        ],
-    )
-    def test_update_non_existing_variable(self, env_list_mock, check_scope_mock, saagie_api_mock):
-        # Arrange
+                "isPassword": True,
+                "scope": scope,
+            },
+            "entityId": app_id,
+        }
+
+        expected_query = gql(GQL_UPDATE_ENV_VAR)
+
+        with patch.object(instance, "list") as list_env:
+            list_env.return_value = [
+                {
+                    "id": "env_var_id",
+                    "name": "TEST_VARIABLE",
+                    "value": "value1",
+                    "description": "description1",
+                    "scope": "APP",
+                    "isPassword": False,
+                }
+            ]
+            instance.update(scope=scope, name=name, value=value, is_password=True, app_id=app_id)
+
+        saagie_api_mock.client.execute.assert_called_with(query=expected_query, variable_values=params)
+
+    def test_update_non_existing_variable(self, saagie_api_mock):
+        saagie_api_mock.env_vars.env_vars.check_scope.return_value = True
+        instance = EnvVars(saagie_api_mock)
+
         scope = "PROJECT"
         name = "NON_EXISTING_VARIABLE"
         project_id = "MY_PROJECT_ID"
 
-        instance = EnvVars(saagie_api_mock)
-
-        # Act and Assert
-        with pytest.raises(ValueError):
+        with patch.object(instance, "list") as list_env, pytest.raises(ValueError):
+            list_env.return_value = [
+                {
+                    "id": "env_var_id",
+                    "name": "TEST_VARIABLE",
+                    "value": "value1",
+                    "description": "description1",
+                    "scope": "PROJECT",
+                    "isPassword": False,
+                }
+            ]
             instance.update(scope=scope, name=name, project_id=project_id)
-        check_scope_mock.assert_called_with(scope, project_id, None)
-        env_list_mock.assert_called_with(scope, project_id, None, scope_only=True, pprint_result=False)
-
-    # Test the `get` method
-    @patch("saagieapi.env_vars.env_vars.check_scope", return_value=True)
-    @patch(
-        "saagieapi.env_vars.env_vars.EnvVars.list",
-        return_value=[{"id": "env_var_id", "name": "TEST_VARIABLE", "value": "value1", "scope": "GLOBAL"}],
-    )
-    def test_get_valid_env_var(self, env_list_mock, check_scope_mock, saagie_api_mock):
-        scope = "GLOBAL"
-        name = "TEST_VARIABLE"
-        instance = EnvVars(saagie_api_mock)
-
-        result = instance.get(scope, name)
 
-        # Assert that the result is as expected
-        assert isinstance(result, dict)
-        assert result["name"] == name
-        assert result["scope"] == scope
-        check_scope_mock.assert_called_with(scope, None, None)
-        env_list_mock.assert_called_with(scope, None, None, False, None)
-
-    @patch("saagieapi.env_vars.env_vars.check_scope", return_value=True)
-    @patch(
-        "saagieapi.env_vars.env_vars.EnvVars.list",
-        return_value=[{"id": "env_var_id", "name": "TEST_VARIABLE", "value": "value1", "scope": "GLOBAL"}],
-    )
-    def test_get_invalid_env_var(self, env_list_mock, check_scope_mock, saagie_api_mock):
-        # Define the input parameters for the `get` method
-        scope = "GLOBAL"
-        name = "INVALID_ENV_VAR"
+    def test_create_or_update_env_var_create(self, saagie_api_mock):
+        saagie_api_mock.env_vars.env_vars.check_scope.return_value = True
         instance = EnvVars(saagie_api_mock)
 
-        result = instance.get(scope, name)
-
-        # Assert that the result is None since the environment variable does not exist
-        assert result is None
-        check_scope_mock.assert_called_with(scope, None, None)
-        env_list_mock.assert_called_with(scope, None, None, False, None)
-
-    # Test the `create_or_update` method
-    @patch("saagieapi.env_vars.env_vars.EnvVars.update", return_value=True)
-    @patch("saagieapi.env_vars.env_vars.EnvVars.create", return_value=True)
-    @patch("saagieapi.env_vars.env_vars.check_scope", return_value=True)
-    @patch(
-        "saagieapi.env_vars.env_vars.EnvVars.list",
-        return_value=[{"id": "env_var_id", "name": "TEST_VARIABLE", "value": "value1", "scope": "GLOBAL"}],
-    )
-    def test_create_new_variable_with_create_or_update(
-        self, env_list_mock, check_scope_mock, create_env_mock, update_env_mock, saagie_api_mock
-    ):
-        # Set up the parameters for the create_or_update method
         scope = "PROJECT"
         name = "NEW_VARIABLE"
         value = "new value"
         description = None
         is_password = True
         project_id = "PROJECT_ID"
-        pipeline_id = None
 
-        instance = EnvVars(saagie_api_mock)
+        create_res = {"saveEnvironmentVariable": {"id": "8aaee333-a9f4-40f5-807a-44f8efa65a2f"}}
 
-        # Call the method being tested
-        instance.create_or_update(
-            scope=scope,
-            name=name,
-            value=value,
-            description=description,
-            is_password=is_password,
-            project_id=project_id,
-            pipeline_id=pipeline_id,
-        )
+        with patch.object(instance, "list") as list_env, patch.object(instance, "create") as create_env:
+            list_env.return_value = [
+                {"id": "env_var_id", "name": "TEST_VARIABLE", "value": "value1", "scope": "GLOBAL"}
+            ]
+            create_env.return_value = create_res
+            res = instance.create_or_update(
+                scope=scope,
+                name=name,
+                value=value,
+                description=description,
+                is_password=is_password,
+                project_id=project_id,
+            )
 
-        # Assertions
-        env_list_mock.assert_called_with(scope, project_id, pipeline_id, scope_only=True, pprint_result=False)
-        create_env_mock.assert_called_once_with(
-            scope=scope, name=name, value=value, is_password=is_password, project_id=project_id
-        )
-        check_scope_mock.assert_called_with(scope, project_id, pipeline_id)
-        update_env_mock.assert_not_called()
-
-    @patch("saagieapi.env_vars.env_vars.EnvVars.update", return_value=True)
-    @patch("saagieapi.env_vars.env_vars.EnvVars.create", return_value=True)
-    @patch("saagieapi.env_vars.env_vars.check_scope", return_value=True)
-    @patch(
-        "saagieapi.env_vars.env_vars.EnvVars.list",
-        return_value=[{"id": "env_var_id", "name": "TEST_VARIABLE", "value": "value1", "scope": "GLOBAL"}],
-    )
-    def test_update_existing_variable_with_create_or_update(
-        self, env_list_mock, check_scope_mock, create_env_mock, update_env_mock, saagie_api_mock
-    ):
-        # Test case to update an existing environment variable
+        assert res == create_res
+
+    def test_create_or_update_env_var_update(self, saagie_api_mock):
+        saagie_api_mock.env_vars.env_vars.check_scope.return_value = True
         instance = EnvVars(saagie_api_mock)
 
-        # Set up the parameters for the create_or_update method
         scope = "GLOBAL"
-        name = "TEST_VARIABLE"  # Updating an existing variable
+        name = "TEST_VARIABLE"
         value = "updated value"
         description = None
         is_password = False
         project_id = None
         pipeline_id = None
 
-        # Call the method being tested
-        instance.create_or_update(
-            scope=scope,
-            name=name,
-            value=value,
-            description=description,
-            is_password=is_password,
-            project_id=project_id,
-            pipeline_id=pipeline_id,
-        )
+        update_res = {"saveEnvironmentVariable": {"id": "8aaee333-a9f4-40f5-807a-44f8efa65a2f"}}
 
-        # Assertions
-        env_list_mock.assert_called_with(scope, project_id, pipeline_id, scope_only=True, pprint_result=False)
-        update_env_mock.assert_called_once_with(
-            scope=scope,
-            name=name,
-            value=value,
-            description=description,
-            new_name=None,
-            is_password=is_password,
-            project_id=project_id,
-            pipeline_id=pipeline_id,
-        )
-        create_env_mock.assert_not_called()
-        check_scope_mock.assert_called_with(scope, project_id, pipeline_id)
+        with patch.object(instance, "list") as list_env, patch.object(instance, "update") as update_env:
+            list_env.return_value = [
+                {"id": "env_var_id", "name": "TEST_VARIABLE", "value": "value1", "scope": "GLOBAL"}
+            ]
+            update_env.return_value = update_res
+            res = instance.create_or_update(
+                scope=scope,
+                name=name,
+                value=value,
+                description=description,
+                is_password=is_password,
+                project_id=project_id,
+                pipeline_id=pipeline_id,
+            )
+
+        assert res == update_res
+
+    def test_delete_env_var_gql(self):
+        query = gql(GQL_DELETE_ENV_VAR)
+        self.client.validate(query)
+
+    def test_delete_env_var(self, saagie_api_mock):
+        saagie_api_mock.env_vars.env_vars.check_scope.return_value = True
+        instance = EnvVars(saagie_api_mock)
+
+        scope = "GLOBAL"
+        name = "env_var1"
+
+        expected_query = gql(GQL_DELETE_ENV_VAR)
+
+        with patch.object(instance, "list") as list_env:
+            list_env.return_value = [
+                {
+                    "id": "env_var_id",
+                    "name": "env_var1",
+                    "value": "value1",
+                    "description": "description1",
+                    "scope": "GLOBAL",
+                }
+            ]
+            instance.delete(scope=scope, name=name)
+
+        saagie_api_mock.client.execute.assert_called_with(query=expected_query, variable_values={"id": "env_var_id"})
+
+    def test_delete_env_var_not_exists(self, saagie_api_mock):
+        saagie_api_mock.env_vars.env_vars.check_scope.return_value = True
+        instance = EnvVars(saagie_api_mock)
+
+        scope = "GLOBAL"
+        name = "NON_EXISTING_VARIABLE"
+
+        with patch.object(instance, "list") as list_env, pytest.raises(ValueError):
+            list_env.return_value = [
+                {
+                    "id": "env_var_id",
+                    "name": "env_var1",
+                    "value": "value1",
+                    "description": "description1",
+                    "scope": "GLOBAL",
+                }
+            ]
+            instance.delete(scope=scope, name=name)
+
+    def test_create_pipeline_env_vars_gql(self):
+        query = gql(GQL_CREATE_PIPELINE_ENV_VAR)
+        self.client.validate(query)
 
-    # Test the `bulk_create_for_pipeline` method
     def test_bulk_create(self, saagie_api_mock):
         instance = EnvVars(saagie_api_mock)
         mock_env_vars = {
@@ -635,18 +641,9 @@ def test_bulk_create(self, saagie_api_mock):
         assert result["replaceEnvironmentVariablesByRawForScope"][0]["id"] == "env_var_id"
         assert result["replaceEnvironmentVariablesByRawForScope"][0]["name"] == "BULK1"
 
-    # Test the `export` method
     @patch("logging.info")
-    @patch("saagieapi.utils.folder_functions.create_folder")
-    @patch("saagieapi.utils.folder_functions.write_to_json_file")
-    @patch("saagieapi.utils.folder_functions.write_error")
-    @patch("saagieapi.env_vars.env_vars.EnvVars.list", return_value=[])
     def test_export_project_without_var(
         self,
-        env_list_mock,
-        write_error_mock,
-        write_to_json_file_mock,
-        create_folder_mock,
         logging_info_mock,
         saagie_api_mock,
     ):
@@ -656,32 +653,18 @@ def test_export_project_without_var(
         project_only = True
         instance = EnvVars(saagie_api_mock)
 
-        # Call the method being tested
-        result = instance.export(project_id, output_folder, error_folder, project_only)
+        with patch.object(instance, "list") as list_env:
+            list_env.return_value = []
+            result = instance.export(project_id, output_folder, error_folder, project_only)
 
-        # Assertions
         assert result is True
-        env_list_mock.assert_called_once_with(scope="PROJECT", project_id=project_id, scope_only=True)
-        create_folder_mock.assert_not_called()
-        write_to_json_file_mock.assert_not_called()
-        write_error_mock.assert_not_called()
         logging_info_mock.assert_called_once_with(
             "✅ The project [%s] doesn't have any environment variable", project_id
         )
 
     @patch("logging.warning")
-    @patch("logging.error")
-    @patch("saagieapi.utils.folder_functions.create_folder")
-    @patch("saagieapi.utils.folder_functions.write_to_json_file")
-    @patch("saagieapi.utils.folder_functions.write_error")
-    @patch("saagieapi.env_vars.env_vars.EnvVars.list", side_effect=Exception("Error getting environment variables"))
     def test_export_list_exception(
         self,
-        env_list_mock,
-        write_error_mock,
-        write_to_json_file_mock,
-        create_folder_mock,
-        logging_error_mock,
         logging_warning_mock,
         saagie_api_mock,
     ):
@@ -691,40 +674,18 @@ def test_export_list_exception(
         project_only = True
         instance = EnvVars(saagie_api_mock)
 
-        # Call the method being tested
-        result = instance.export(project_id, output_folder, error_folder, project_only)
+        with patch.object(instance, "list") as list_env:
+            list_env.side_effect = Exception("Error getting environment variables")
+            result = instance.export(project_id, output_folder, error_folder, project_only)
 
-        # Assertions
         assert not result
-        env_list_mock.assert_called_once_with(scope="PROJECT", project_id=project_id, scope_only=True)
-        create_folder_mock.assert_not_called()
-        write_to_json_file_mock.assert_not_called()
-        write_error_mock.assert_not_called()
         logging_warning_mock.assert_called_once_with(
             "Cannot get the information of environment variable of the project [%s]", project_id
         )
-        arg1 = logging_error_mock.call_args[0][0]
-        arg2 = logging_error_mock.call_args[0][1]
-        assert arg1 == "Something went wrong %s"
-        assert isinstance(arg2, Exception)
-        assert str(arg2) == "Error getting environment variables"
 
     @patch("logging.info")
-    @patch("logging.error")
-    @patch("saagieapi.env_vars.env_vars.create_folder")
-    @patch("saagieapi.env_vars.env_vars.write_to_json_file")
-    @patch("saagieapi.env_vars.env_vars.write_error")
-    @patch(
-        "saagieapi.env_vars.env_vars.EnvVars.list",
-        return_value=[{"id": "env_var_id", "name": "TEST_VARIABLE", "value": "value1", "scope": "PROJECT"}],
-    )
     def test_export_project(
         self,
-        env_list_mock,
-        write_error_mock,
-        write_to_json_file_mock,
-        create_folder_mock,
-        logging_error_mock,
         logging_info_mock,
         saagie_api_mock,
     ):
@@ -734,31 +695,26 @@ def test_export_project(
         project_only = True
         instance = EnvVars(saagie_api_mock)
 
-        result = instance.export(project_id, output_folder, error_folder, project_only)
+        with patch.object(instance, "list") as list_env:
+            list_env.return_value = [
+                {"id": "env_var_id", "name": "TEST_VARIABLE", "value": "value1", "scope": "PROJECT"}
+            ]
+            result = instance.export(project_id, output_folder, error_folder, project_only)
 
         # Assertions
         assert result is True
-        write_error_mock.assert_not_called()
-        env_list_mock.assert_called_once_with(scope="PROJECT", project_id=project_id, scope_only=True)
         logging_info_mock.assert_called_with(
             "✅ Environment variables of the project [%s] have been successfully exported", project_id
         )
-        logging_error_mock.assert_not_called()
-        create_folder_mock.assert_called()
-        write_to_json_file_mock.assert_called()
 
-    @patch("logging.error")
     @patch("logging.warning")
     @patch("saagieapi.env_vars.env_vars.create_folder", side_effect=Exception("Exception raised"))
-    @patch("saagieapi.env_vars.env_vars.write_to_json_file")
     @patch("saagieapi.env_vars.env_vars.write_error")
     def test_export_folder_exception(
         self,
         write_error_mock,
-        write_to_json_file_mock,
         create_folder_mock,
         logging_warning_mock,
-        logging_error_mock,
         saagie_api_mock,
     ):
         project_id = "50033e21-83c2-4431-a723-d54c2693b964"
@@ -766,42 +722,22 @@ def test_export_folder_exception(
         error_folder = "./output/error/"
         project_only = True
         instance = EnvVars(saagie_api_mock)
-        mock_existing_env_var = [
-            {
-                "id": "334c2e0e-e8ea-4639-911e-757bf36bc91b",
-                "name": "TEST_PASSWORD",
-                "scope": "PROJECT",
-                "value": None,
-                "description": "This is a password",
-                "isPassword": True,
-                "isValid": True,
-                "overriddenValues": [],
-                "invalidReasons": None,
-            }
-        ]
 
-        instance.list = MagicMock(return_value=mock_existing_env_var)
-
-        result = instance.export(project_id, output_folder, error_folder, project_only)
+        with patch.object(instance, "list") as list_env:
+            list_env.return_value = [
+                {"id": "env_var_id", "name": "TEST_VARIABLE", "value": "value1", "scope": "PROJECT"}
+            ]
+            result = instance.export(project_id, output_folder, error_folder, project_only)
 
-        # Assertions
         assert not result
-        instance.list.assert_called_once_with(scope="PROJECT", project_id=project_id, scope_only=True)
         create_folder_mock.assert_called()
-        write_to_json_file_mock.assert_not_called()
         write_error_mock.assert_called()
         logging_warning_mock.assert_called_once_with(
             "❌ Environment variables of the project [%s] have not been successfully exported", project_id
         )
-        arg1 = logging_error_mock.call_args[0][0]
-        arg2 = logging_error_mock.call_args[0][1]
-        assert arg1 == "Something went wrong %s"
-        assert isinstance(arg2, Exception)
-        assert str(arg2) == "Exception raised"
 
     @patch("logging.info")
     def test_import_from_json_success(self, logging_info_mock, saagie_api_mock):
-        # Prepare test data and environment
         json_file = "/path/to/the/json/file.json"
         project_id = "MY_PROJECT_ID"
         instance = EnvVars(saagie_api_mock)
diff --git a/tests/unit/pipelines_unit_test.py b/tests/unit/pipelines_unit_test.py
index 8bba781..7d17746 100644
--- a/tests/unit/pipelines_unit_test.py
+++ b/tests/unit/pipelines_unit_test.py
@@ -183,6 +183,32 @@ def test_get_pipeline_by_name(self, saagie_api_mock):
             query=expected_query, variable_values=params, pprint_result=None
         )
 
+    def test_get_pipeline_by_alias_gql(self):
+        query = gql(GQL_GET_PIPELINE_BY_ALIAS)
+        self.client.validate(query)
+
+    def test_get_pipeline_by_alias(self, saagie_api_mock):
+        pipeline = Pipelines(saagie_api_mock)
+
+        project_id = "860b8dc8-e634-4c98-b2e7-f9ec32ab4771"
+        pipeline_alias = "pipeline_alias"
+
+        params = {
+            "projectId": project_id,
+            "pipelineAlias": pipeline_alias,
+            "instancesLimit": None,
+            "versionsLimit": None,
+            "versionsOnlyCurrent": False,
+        }
+
+        expected_query = gql(GQL_GET_PIPELINE_BY_ALIAS)
+
+        pipeline.get_info_by_alias(project_id=project_id, pipeline_alias=pipeline_alias)
+
+        saagie_api_mock.client.execute.assert_called_with(
+            query=expected_query, variable_values=params, pprint_result=None
+        )
+
     def test_get_pipeline_instance_gql(self):
         query = gql(GQL_GET_PIPELINE_INSTANCE)
         self.client.validate(query)
@@ -216,6 +242,7 @@ def test_create_graph_pipeline(self, saagie_api_mock):
         name = "Amazing Pipeline"
         alias = "amazing_pipeline"
         desc = "new pipeline"
+        source_url = "https://my.super.link"
 
         job_node1 = JobNode("5d1999f5-fa70-47d9-9f41-55ad48333629")
         job_node2 = JobNode("5d1999f5-fa70-47d9-9f41-55ad48333629")
@@ -235,6 +262,7 @@ def test_create_graph_pipeline(self, saagie_api_mock):
             name=name,
             alias=alias,
             description=desc,
+            source_url=source_url,
         )
 
         expected_query = gql(GQL_CREATE_GRAPH_PIPELINE)
@@ -250,6 +278,7 @@ def test_create_graph_pipeline(self, saagie_api_mock):
             "conditionNodes": graph_pipeline.list_conditions_nodes,
             "alias": alias,
             "isScheduled": False,
+            "sourceUrl": source_url,
         }
 
         saagie_api_mock.client.execute.assert_called_with(query=expected_query, variable_values=params)
@@ -350,6 +379,7 @@ def test_upgrade_pipeline(self, saagie_api_mock):
         pipeline = Pipelines(saagie_api_mock)
 
         pipeline_id = "860b8dc8-e634-4c98-b2e7-f9ec32ab4771"
+        source_url = "https://my.super.link"
 
         job_node1 = JobNode("5d1999f5-fa70-47d9-9f41-55ad48333629")
         job_node2 = JobNode("5d1999f5-fa70-47d9-9f41-55ad48333629")
@@ -363,10 +393,7 @@ def test_upgrade_pipeline(self, saagie_api_mock):
         graph_pipeline = GraphPipeline()
         graph_pipeline.add_root_node(job_node1)
 
-        pipeline.upgrade(
-            pipeline_id=pipeline_id,
-            graph_pipeline=graph_pipeline,
-        )
+        pipeline.upgrade(pipeline_id=pipeline_id, graph_pipeline=graph_pipeline, source_url=source_url)
 
         expected_query = gql(GQL_UPGRADE_PIPELINE)
 
@@ -377,6 +404,7 @@ def test_upgrade_pipeline(self, saagie_api_mock):
             "jobNodes": graph_pipeline.list_job_nodes,
             "conditionNodes": graph_pipeline.list_conditions_nodes,
             "releaseNote": "",
+            "sourceUrl": source_url,
         }
 
         saagie_api_mock.client.execute.assert_called_with(query=expected_query, variable_values=params)
diff --git a/tests/unit/resources/schema.graphqls b/tests/unit/resources/schema.graphqls
index f076c51..22292ad 100644
--- a/tests/unit/resources/schema.graphqls
+++ b/tests/unit/resources/schema.graphqls
@@ -135,6 +135,15 @@ type Query {
     skip:Int
   ): [EnvironmentVariable!]
 
+  # List environment variables of a specific app. Include all variables usable by this app.
+  #
+  # You can only list environment variables if you have at least the viewer role in the project associated to the app or in all projects.
+  appEnvironmentVariables(
+    appId: UUID!,
+    # Allows to get only variables in the scope specified in input. By default if no parameter is specified, all scopes are include.
+    scope: EnvVarScope
+  ): [EnvironmentVariable!]
+
   # List technologies, grouped by category, in a given project by providing its UUID. In addition, technologies can be filtered by category.
   #
   # You can only list technologies if you have at least the viewer role in the project or in all projects.
@@ -289,7 +298,7 @@ type Query {
   # You can only get a pipeline's details if you have at least the viewer role in the project associated to the pipeline or in all projects.
   #
   # This endpoint allows to retrieve a pipeline with versions in linear or graph format.
-  graphPipelineByAlias(projectId: UUID!, name: String!): Pipeline!
+  graphPipelineByAlias(projectId: UUID!, alias: String!): Pipeline!
 
   # List users by realm.
   usersByRealm: [UserEmail]
@@ -643,14 +652,19 @@ type Mutation {
   # Add a new pipeline version in linear format to an existing pipeline.
   #
   # You can only add a version to a pipeline if you have at least the editor role in the project associated to the pipeline or in all projects.
+  # The parameters sourceUrl and releaseNote are optional.
+  # SourceUrl should be an http or https valid url. It represents the source code url for the pipeline definition.
   #
   # This deprecated endpoint allows to add a version to a linear pipeline. To add a graph pipeline version, use the endpoint `addGraphPipelineVersion` instead.
-  addPipelineVersion(pipelineId: UUID!, jobsId: [UUID!]!, releaseNote: String): PipelineVersion! @deprecated(reason: "Only for old linears pipelines without graph, use addGraphPipelineVersion instead")
+  addPipelineVersion(pipelineId: UUID!, jobsId: [UUID!]!, releaseNote: String, sourceUrl: String): PipelineVersion! @deprecated(reason: "Only for old linears pipelines without graph, use addGraphPipelineVersion instead")
 
   # Add a new pipeline version in graph format based on DAG model (Directed Acyclic Graph) to an existing pipeline.
   #
   # You can only add a version to a pipeline if you have at least the editor role in the project associated to the pipeline or in all projects.
-  addGraphPipelineVersion(pipelineId: UUID!, graph: PipelineGraphInput!, releaseNote: String): PipelineVersion!
+  # The parameters sourceUrl and releaseNote are optional.
+  # SourceUrl should be an http or https valid url. It represents the source code url for the pipeline definition.
+  #
+  addGraphPipelineVersion(pipelineId: UUID!, graph: PipelineGraphInput!, releaseNote: String, sourceUrl: String): PipelineVersion!
 
   # Rollback a pipeline to a specific version.
   #
@@ -856,6 +870,8 @@ input JobVersionInput {
   doesUseGPU: Boolean
   connectionId: UUID
   externalJobParams: [ExternalJobParamInput!]
+
+  # Version reference url (http or https) pushed only by API
   sourceUrl: String
 }
 
@@ -984,6 +1000,9 @@ input PipelineInput {
   isScheduled: Boolean!
   cronScheduling: Cron
   scheduleTimezone: TimeZone
+
+  # Version reference url (http or https) pushed only by API
+  sourceUrl: String
 }
 
 # Input to create a pipeline with a Directed Acyclic Graph
@@ -1001,6 +1020,9 @@ input GraphPipelineInput {
   isScheduled: Boolean!
   cronScheduling: Cron
   scheduleTimezone: TimeZone
+
+  # Version reference url pushed only by API
+  sourceUrl: String
 }
 
 # Input for a Directed Acyclic Graph (DAG) of nodes of jobs and conditions of a pipeline in graph format
@@ -1201,6 +1223,7 @@ type Volume {
   duplicationStatus: DuplicationStatus
   duplicationTargetId: UUID
   originalVolumeId: UUID
+  expansionStatus: ExpansionStatus
   isLocked: Boolean
 }
 
@@ -1431,6 +1454,7 @@ enum EnvironmentVariableValidationErrorType {
   NULL_VALUE_UNEXPECTED,
   NAME_ALREADY_USED,
   PROJECT_IS_NOT_FOUND,
+  APP_IS_NOT_FOUND,
   JOB_IS_NOT_FOUND,
   UNEXPECTED_LINKED_ENTITY_ON_GLOBAL_SCOPE,
   UNEXPECTED_LINKED_ENTITY_ID_IS_NULL
@@ -1559,6 +1583,8 @@ type JobVersion {
   doesUseGPU: Boolean @deprecated(reason: "Use Job.doesUseGPU instead")
   connectionId: UUID
   externalJobParams: [ExternalJobParam!]
+
+  # Version reference url (http or https) pushed only by API.
   sourceUrl: String
 }
 
@@ -1758,6 +1784,9 @@ type PipelineVersion {
   isCurrent: Boolean!
   isMajor: Boolean!
   deletableState: PipelineVersionDeletableState
+
+  # Version reference url (http or https) pushed only by API
+  sourceUrl: String
 }
 
 type PipelineVersionDeletableState {
@@ -2046,6 +2075,7 @@ enum EnvVarScope {
   PIPELINE
   # Value is deprecated : it will be removed in future releases
   JOB
+  APP
 }
 
 enum StatusReason {
@@ -2118,6 +2148,11 @@ enum DuplicationStatus {
   FAILED
 }
 
+enum ExpansionStatus {
+  EXPANSION_IN_PROGRESS
+  WAITING_FOR_LINK_TO_AN_APP
+}
+
 enum TriggerType {
   MANUAL_JOB,
   MANUAL_APP,
