diff --git a/gso/services/_ipam.py b/gso/services/_ipam.py
index db04353f51a08fab0cf9eeb44682c1d1a9187061..573c3aef75b79744766c52006c9e784f380442db 100644
--- a/gso/services/_ipam.py
+++ b/gso/services/_ipam.py
@@ -1,3 +1,4 @@
+# mypy: ignore-errors
 import ipaddress
 from enum import Enum
 from typing import Union
diff --git a/gso/services/ipam.py b/gso/services/ipam.py
index 64e1fdf9c6c1a4638b028677aad8126606b7ccdd..e7018312057b96c03d197a9909624730a1007a4d 100644
--- a/gso/services/ipam.py
+++ b/gso/services/ipam.py
@@ -1,5 +1,6 @@
+# mypy: ignore-errors
 import ipaddress
-from typing import Union, Optional
+from typing import Optional, Union
 
 from pydantic import BaseSettings
 
diff --git a/gso/services/provisioning_proxy.py b/gso/services/provisioning_proxy.py
index a3ffde6f40a20649bab14d4d089923f9bf83f010..1ecd09a06685ac2499d1e082d56fb0659ab2a4b3 100644
--- a/gso/services/provisioning_proxy.py
+++ b/gso/services/provisioning_proxy.py
@@ -11,7 +11,7 @@ from orchestrator.config.assignee import Assignee
 from orchestrator.domain import SubscriptionModel
 from orchestrator.forms import FormPage, ReadOnlyField
 from orchestrator.forms.validators import Accept, Label, LongText
-from orchestrator.types import State, UUIDstr, strEnum
+from orchestrator.types import FormGenerator, State, UUIDstr, strEnum
 from orchestrator.utils.json import json_dumps
 from pydantic import validator
 
@@ -36,18 +36,15 @@ class CUDOperation(strEnum):
     DELETE = "DELETE"
 
 
-def _send_request(endpoint: str, parameters: dict, process_id: UUIDstr, operation: CUDOperation):
+def _send_request(endpoint: str, parameters: dict, process_id: UUIDstr, operation: CUDOperation) -> None:
     """Send a request to LSO. The callback address is derived using the process ID provided.
 
-    :param str endpoint: The LSO-specific endpoint to call, depending on the
-        type of service object that is acted upon.
-    :param dict parameters: JSON body for the request, which will almost always
-        at least consist of a subscription object, and a boolean value to
-        indicate a dry run.
-    :param UUIDstr process_id: The process ID that this request is a part of,
-        used to call back to when the execution of the playbook is completed.
-    :param :class:`CUDOperation` operation: The specific operation that is
-        performed with the request.
+    :param str endpoint: The LSO-specific endpoint to call, depending on the type of service object that is acted upon.
+    :param dict parameters: JSON body for the request, which will almost always at least consist of a subscription
+        object, and a boolean value to indicate a dry run.
+    :param UUIDstr process_id: The process ID that this request is a part of, used to call back to when the execution
+        of the playbook is completed.
+    :param :class:`CUDOperation` operation: The specific operation that is performed with the request.
     """
     oss = settings.load_oss_params()
     pp_params = oss.PROVISIONING_PROXY
@@ -73,7 +70,7 @@ def _send_request(endpoint: str, parameters: dict, process_id: UUIDstr, operatio
         raise AssertionError(request.content)
 
 
-def provision_device(subscription: DeviceProvisioning, process_id: UUIDstr, dry_run: bool = True):
+def provision_device(subscription: DeviceProvisioning, process_id: UUIDstr, dry_run: bool = True) -> None:
     """Provision a new device using LSO.
 
     :param :class:`DeviceProvisioning` subscription: The subscription object
@@ -89,7 +86,7 @@ def provision_device(subscription: DeviceProvisioning, process_id: UUIDstr, dry_
 
 def provision_ip_trunk(
     subscription: IptrunkProvisioning, process_id: UUIDstr, config_object: str, dry_run: bool = True
-):
+) -> None:
     """Provision an IP trunk service using LSO.
 
     :param :class:`IptrunkProvisioning` subscription: The subscription object
@@ -134,7 +131,7 @@ def provision_ip_trunk(
 #    _send_request('ip_trunk', parameters, process_id, CUDOperation.PUT)
 
 
-def deprovision_ip_trunk(subscription: Iptrunk, process_id: UUIDstr, dry_run: bool = True):
+def deprovision_ip_trunk(subscription: Iptrunk, process_id: UUIDstr, dry_run: bool = True) -> None:
     """Deprovision an IP trunk service using LSO.
 
     :param :class:`IptrunkProvisioning` subscription: The subscription object
@@ -149,17 +146,17 @@ def deprovision_ip_trunk(subscription: Iptrunk, process_id: UUIDstr, dry_run: bo
 
 
 @inputstep("Await provisioning proxy results", assignee=Assignee("SYSTEM"))
-def await_pp_results(subscription: SubscriptionModel, label_text: str) -> State:
+def await_pp_results(subscription: SubscriptionModel, label_text: str) -> FormGenerator:
     class ProvisioningResultPage(FormPage):
         class Config:
             title = f"Deploying {subscription.product.name}..."
 
-        warning_label: Label = label_text
-        pp_run_results: dict = None
+        warning_label: Label = label_text  # type: ignore
+        pp_run_results: dict = None  # type: ignore
         confirm: Accept = Accept("INCOMPLETE")
 
         @validator("pp_run_results", allow_reuse=True, pre=True, always=True)
-        def run_results_must_be_given(cls, run_results):
+        def run_results_must_be_given(cls, run_results: dict) -> dict | None:
             if run_results is None:
                 raise ValueError("Run results may not be empty. Wait for the provisioning proxy to finish.")
             return run_results
@@ -170,7 +167,7 @@ def await_pp_results(subscription: SubscriptionModel, label_text: str) -> State:
 
 
 @inputstep("Confirm provisioning proxy results", assignee=Assignee("SYSTEM"))
-def confirm_pp_results(state: State) -> State:
+def confirm_pp_results(state: State) -> FormGenerator:
     class ConfirmRunPage(FormPage):
         class Config:
             title = (
diff --git a/gso/services/resource_manager.py b/gso/services/resource_manager.py
index 50b1bc891773e035b28fd30ce540ffe790e2dbce..2fc41f625f7595ac02dac7de4af6cb5e290f9ac2 100644
--- a/gso/services/resource_manager.py
+++ b/gso/services/resource_manager.py
@@ -1,3 +1,4 @@
+# mypy: ignore-errors
 import requests
 
 from gso import settings
diff --git a/gso/workflows/device/create_device.py b/gso/workflows/device/create_device.py
index 2b6efc40b56fb341362fc9be6da892d7a35f29d7..f162f5d4cea9a25b4f77de4c5698b7d18566ee0e 100644
--- a/gso/workflows/device/create_device.py
+++ b/gso/workflows/device/create_device.py
@@ -9,7 +9,7 @@ from orchestrator.forms import FormPage
 from orchestrator.forms.validators import Choice
 from orchestrator.targets import Target
 from orchestrator.types import FormGenerator, State, SubscriptionLifecycle, UUIDstr
-from orchestrator.workflow import done, init, step, workflow
+from orchestrator.workflow import StepList, done, init, step, workflow
 from orchestrator.workflows.steps import resync, set_status, store_process_subscription
 from orchestrator.workflows.utils import wrap_create_initial_input_form
 
@@ -35,10 +35,7 @@ def site_selector() -> Choice:
         site_subscriptions[str(site_id)] = site_description
 
     # noinspection PyTypeChecker
-    return Choice(
-        "Select a site",
-        zip(site_subscriptions.keys(), site_subscriptions.items()),
-    )
+    return Choice("Select a site", zip(site_subscriptions.keys(), site_subscriptions.items()))  # type: ignore
 
 
 def initial_input_form_generator(product_name: str) -> FormGenerator:
@@ -46,7 +43,7 @@ def initial_input_form_generator(product_name: str) -> FormGenerator:
         class Config:
             title = product_name
 
-        device_site: site_selector()
+        device_site: site_selector()  # type: ignore
         hostname: str
         ts_address: ipaddress.IPv4Address
         ts_port: int
@@ -68,8 +65,8 @@ def create_subscription(product: UUIDstr) -> State:
     }
 
 
-def iso_from_ipv4(ipv4_address):
-    padded_octets = [f"{x:>03}" for x in ipv4_address.split(".")]
+def iso_from_ipv4(ipv4_address: ipaddress.IPv4Address) -> str:
+    padded_octets = [f"{x:>03}" for x in str(ipv4_address).split(".")]
     joined_octets = "".join(padded_octets)
     re_split = ".".join(re.findall("....", joined_octets))
     return ".".join(["49.51e5.0001", re_split, "00"])
@@ -82,7 +79,7 @@ def get_info_from_ipam(subscription: DeviceProvisioning) -> State:
     lo0_addr = _ipam.allocate_service_host(hostname=lo0_name, service_type="LO", cname_aliases=[lo0_alias])
     subscription.device.device_lo_ipv4_address = lo0_addr.v4
     subscription.device.device_lo_ipv6_address = lo0_addr.v6
-    subscription.device.device_lo_iso_address = iso_from_ipv4(str(subscription.device.device_lo_ipv4_address))
+    subscription.device.device_lo_iso_address = iso_from_ipv4(subscription.device.device_lo_ipv4_address)
     subscription.device.device_si_ipv4_network = _ipam.allocate_service_ipv4_network(
         service_type="SI", comment=f"SI for {lo0_name}"
     ).v4
@@ -100,13 +97,13 @@ def initialize_subscription(
     subscription: device.DeviceInactive,
     hostname: str,
     ts_address: ipaddress.IPv4Address,
-    ts_port: str,
+    ts_port: int,
     device_vendor: device_pb.DeviceVendor,
     device_site: str,
     device_role: device_pb.DeviceRole,
 ) -> State:
     subscription.device.device_ts_address = str(ts_address)
-    subscription.device.device_ts_port = str(ts_port)
+    subscription.device.device_ts_port = ts_port
     subscription.device.device_vendor = device_vendor
     subscription.device.device_site = Site.from_subscription(device_site).site
     fqdn = (
@@ -161,7 +158,7 @@ def provision_device_real(subscription: DeviceProvisioning, process_id: UUIDstr)
     initial_input_form=wrap_create_initial_input_form(initial_input_form_generator),
     target=Target.CREATE,
 )
-def create_device():
+def create_device() -> StepList:
     return (
         init
         >> create_subscription
diff --git a/gso/workflows/device/get_facts.py b/gso/workflows/device/get_facts.py
index ada97fe114045a173ba0d08b8a2e34d29e39a082..1a2ec8234010c968d37bbb897afc583035b54837 100644
--- a/gso/workflows/device/get_facts.py
+++ b/gso/workflows/device/get_facts.py
@@ -1,46 +1,36 @@
 from orchestrator.forms import FormPage
 from orchestrator.forms.validators import Label
 from orchestrator.targets import Target
-
-# from orchestrator.types import SubscriptionLifecycle
 from orchestrator.types import InputForm, UUIDstr
-from orchestrator.workflow import done, init, step, workflow
-
-# from orchestrator.workflows.steps import (
-#     resync,
-#     set_status,
-#     store_process_subscription,
-#     unsync,
-# )
+from orchestrator.workflow import StepList, done, init, step, workflow
 from orchestrator.workflows.utils import wrap_modify_initial_input_form
 
 from gso.products.product_types.device import Device
 
 
-def initial_input_form_generator(subscription_id: UUIDstr, organisation: UUIDstr) -> InputForm:
+def initial_input_form_generator(subscription_id: UUIDstr) -> InputForm:
     subscription = Device.from_subscription(subscription_id)
 
     class TerminateForm(FormPage):
-        are_you_sure: Label = f"Are you sure you want to get facts from \
-            {subscription.description}?"
+        are_you_sure: Label = f"Are you sure you want to get facts from {subscription.description}?"  # type: ignore
 
     return TerminateForm
 
 
 @step("Get facts")
-def get_facts(subscription_id) -> None:
+def get_facts(subscription_id: UUIDstr) -> dict:
     subscription = Device.from_subscription(subscription_id)
-    import ansible_runner
-
-    r = ansible_runner.run(
-        private_data_dir="/opt",
-        playbook="get_facts.yaml",
-        inventory=subscription.device.fqdn,
-    )
-    out = r.stdout.read()
-    out_splitted = out.splitlines()
+    # import ansible_runner
+    #
+    # r = ansible_runner.run(
+    #     private_data_dir="/opt",
+    #     playbook="get_facts.yaml",
+    #     inventory=subscription.device.device_fqdn,
+    # )
+    # out = r.stdout.read()
+    # out_splitted = out.splitlines()
 
-    return {"output": out_splitted}
+    return {"output": subscription}
 
 
 @workflow(
@@ -48,7 +38,7 @@ def get_facts(subscription_id) -> None:
     initial_input_form=wrap_modify_initial_input_form(initial_input_form_generator),
     target=Target.SYSTEM,
 )
-def get_facts_from_device():
+def get_facts_from_device() -> StepList:
     return (
         init
         >> get_facts
diff --git a/gso/workflows/device/terminate_device.py b/gso/workflows/device/terminate_device.py
index 25e02298a16e412e25fbddb7e7651e7f03e082b5..b7564d2a197a652c8b77edc830a5e374488b4459 100644
--- a/gso/workflows/device/terminate_device.py
+++ b/gso/workflows/device/terminate_device.py
@@ -4,8 +4,8 @@ import logging
 from orchestrator.forms import FormPage
 from orchestrator.forms.validators import Label
 from orchestrator.targets import Target
-from orchestrator.types import SubscriptionLifecycle, UUIDstr, FormGenerator
-from orchestrator.workflow import done, init, step, workflow, StepList
+from orchestrator.types import FormGenerator, SubscriptionLifecycle, UUIDstr
+from orchestrator.workflow import StepList, done, init, step, workflow
 from orchestrator.workflows.steps import resync, set_status, store_process_subscription, unsync
 from orchestrator.workflows.utils import wrap_modify_initial_input_form
 
@@ -21,9 +21,9 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
     subscription = Device.from_subscription(subscription_id)
 
     class TerminateForm(FormPage):
-        are_you_sure: Label = f"Are you sure you want to remove {subscription.description}?"
+        are_you_sure: Label = f"Are you sure you want to remove {subscription.description}?"  # type: ignore
 
-    return TerminateForm
+    return TerminateForm  # type: ignore
 
 
 def _deprovision_in_user_management_system(fqdn: str) -> None:
@@ -34,8 +34,8 @@ def _deprovision_in_user_management_system(fqdn: str) -> None:
 @step("Deprovision loopback IPs from IPAM/DNS")
 def deprovision_loopback_ips(subscription: Device) -> dict[str, V4HostAddress | V6HostAddress]:
     input_host_addresses = ipam.HostAddresses(
-        v4=ipaddress.ip_address(subscription.device.device_lo_ipv4_address),
-        v6=ipaddress.ip_address(subscription.device.device_lo_ipv6_address),
+        v4=ipaddress.IPv4Address(subscription.device.device_lo_ipv4_address),
+        v6=ipaddress.IPv6Address(subscription.device.device_lo_ipv6_address),
     )
     fqdn_as_list = subscription.device.device_fqdn.split(".")
     hostname = str(fqdn_as_list[0]) + "." + str(fqdn_as_list[1]) + "." + str(fqdn_as_list[2])
diff --git a/gso/workflows/iptrunk/create_iptrunk.py b/gso/workflows/iptrunk/create_iptrunk.py
index 12f396ee463d502c9232e41cbc7e73a8f83c6eb7..2a5a0b471a0096fcd0478e1cc5ccd3b25be5c781 100644
--- a/gso/workflows/iptrunk/create_iptrunk.py
+++ b/gso/workflows/iptrunk/create_iptrunk.py
@@ -1,12 +1,11 @@
 from uuid import uuid4
 
 from orchestrator.db.models import ProductTable, SubscriptionTable
-
 from orchestrator.forms import FormPage
 from orchestrator.forms.validators import Choice, UniqueConstrainedList
 from orchestrator.targets import Target
 from orchestrator.types import FormGenerator, State, SubscriptionLifecycle, UUIDstr
-from orchestrator.workflow import done, init, step, workflow, StepList
+from orchestrator.workflow import StepList, done, init, step, workflow
 from orchestrator.workflows.steps import resync, set_status, store_process_subscription
 from orchestrator.workflows.utils import wrap_create_initial_input_form
 
@@ -49,13 +48,13 @@ def initial_input_form_generator(product_name: str) -> FormGenerator:
     class AeMembersListA(UniqueConstrainedList[str]):
         min_items = initial_user_input.iptrunk_minimum_links
 
-    DeviceEnumA: str = Choice("Select a device", zip(devices.keys(), devices.items()))
+    DeviceEnumA = Choice("Select a device", zip(devices.keys(), devices.items()))  # type: ignore
 
     class CreateIptrunkSideAForm(FormPage):
         class Config:
             title = "Provide subscription details for side A of the trunk."
 
-        iptrunk_sideA_node_id: DeviceEnumA
+        iptrunk_sideA_node_id: DeviceEnumA  # type: ignore
         iptrunk_sideA_ae_iface: str
         iptrunk_sideA_ae_geant_a_sid: str
         iptrunk_sideA_ae_members: AeMembersListA
@@ -65,7 +64,7 @@ def initial_input_form_generator(product_name: str) -> FormGenerator:
 
     # We remove the selected device for side A, to prevent any loops
     devices.pop(str(user_input_side_a.iptrunk_sideA_node_id.name))
-    DeviceEnumB: str = Choice("Select a device", zip(devices.keys(), devices.items()))
+    DeviceEnumB = Choice("Select a device", zip(devices.keys(), devices.items()))  # type: ignore
 
     class AeMembersListB(UniqueConstrainedList[str]):
         min_items = len(user_input_side_a.iptrunk_sideA_ae_members)
@@ -75,7 +74,7 @@ def initial_input_form_generator(product_name: str) -> FormGenerator:
         class Config:
             title = "Provide subscription details for side B of the trunk."
 
-        iptrunk_sideB_node_id: DeviceEnumB
+        iptrunk_sideB_node_id: DeviceEnumB  # type: ignore
         iptrunk_sideB_ae_iface: str
         iptrunk_sideB_ae_geant_a_sid: str
         iptrunk_sideB_ae_members: AeMembersListB
diff --git a/gso/workflows/iptrunk/modify_generic.py b/gso/workflows/iptrunk/modify_generic.py
index 4c083463521cd39bb7c8e20110c246d538c884a5..8d813f18ab108b7bbc1e2bdfe245cf42c360ddc4 100644
--- a/gso/workflows/iptrunk/modify_generic.py
+++ b/gso/workflows/iptrunk/modify_generic.py
@@ -4,7 +4,7 @@ from orchestrator.forms import FormPage, ReadOnlyField
 from orchestrator.forms.validators import UniqueConstrainedList
 from orchestrator.targets import Target
 from orchestrator.types import FormGenerator, State, UUIDstr
-from orchestrator.workflow import done, init, step, workflow
+from orchestrator.workflow import StepList, done, init, step, workflow
 from orchestrator.workflows.steps import resync, store_process_subscription, unsync
 from orchestrator.workflows.utils import wrap_modify_initial_input_form
 
@@ -22,7 +22,7 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
         geant_s_sid: str = subscription.iptrunk.geant_s_sid
         iptrunk_description: str = subscription.iptrunk.iptrunk_description
         iptrunk_type: IptrunkType = subscription.iptrunk.iptrunk_type
-        iptrunk_speed: PhyPortCapacity = subscription.iptrunk.iptrunk_speed
+        iptrunk_speed: PhyPortCapacity = subscription.iptrunk.iptrunk_speed  # type: ignore
         iptrunk_minimum_links: int = subscription.iptrunk.iptrunk_minimum_links
         iptrunk_isis_metric: int = ReadOnlyField(subscription.iptrunk.iptrunk_isis_metric)
         iptrunk_ipv4_network: ipaddress.IPv4Network = ReadOnlyField(subscription.iptrunk.iptrunk_ipv4_network)
@@ -40,9 +40,9 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
         iptrunk_sideA_node: str = ReadOnlyField(subscription.iptrunk.iptrunk_sideA_node.device_fqdn)
         iptrunk_sideA_ae_iface: str = ReadOnlyField(subscription.iptrunk.iptrunk_sideA_ae_iface)
         iptrunk_sideA_ae_geant_a_sid: str = subscription.iptrunk.iptrunk_sideA_ae_geant_a_sid
-        iptrunk_sideA_ae_members: AeMembersListA = subscription.iptrunk.iptrunk_sideA_ae_members
+        iptrunk_sideA_ae_members: AeMembersListA = subscription.iptrunk.iptrunk_sideA_ae_members  # type: ignore
         iptrunk_sideA_ae_members_descriptions: AeMembersListA = (
-            subscription.iptrunk.iptrunk_sideA_ae_members_description
+            subscription.iptrunk.iptrunk_sideA_ae_members_description  # type: ignore
         )
 
     user_input_side_a = yield ModifyIptrunkSideAForm
@@ -58,9 +58,9 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
         iptrunk_sideB_node: str = ReadOnlyField(subscription.iptrunk.iptrunk_sideB_node.device_fqdn)
         iptrunk_sideB_ae_iface: str = ReadOnlyField(subscription.iptrunk.iptrunk_sideB_ae_iface)
         iptrunk_sideB_ae_geant_a_sid: str = subscription.iptrunk.iptrunk_sideB_ae_geant_a_sid
-        iptrunk_sideB_ae_members: AeMembersListB = subscription.iptrunk.iptrunk_sideB_ae_members
+        iptrunk_sideB_ae_members: AeMembersListB = subscription.iptrunk.iptrunk_sideB_ae_members  # type: ignore
         iptrunk_sideB_ae_members_descriptions: AeMembersListB = (
-            subscription.iptrunk.iptrunk_sideB_ae_members_description
+            subscription.iptrunk.iptrunk_sideB_ae_members_description  # type: ignore
         )
 
     user_input_side_b = yield ModifyIptrunkSideBForm
@@ -147,7 +147,7 @@ def provision_ip_trunk_lldp_iface_real(subscription: Iptrunk, process_id: UUIDst
     initial_input_form=wrap_modify_initial_input_form(initial_input_form_generator),
     target=Target.MODIFY,
 )
-def modify_generic():
+def modify_generic() -> StepList:
     return (
         init
         >> store_process_subscription(Target.MODIFY)
diff --git a/gso/workflows/iptrunk/modify_isis_metric.py b/gso/workflows/iptrunk/modify_isis_metric.py
index 17beb8aa7335d5adb7aa4adcb18861335c5361ba..18fafa55806b0837269460c8e0da11e6df92e747 100644
--- a/gso/workflows/iptrunk/modify_isis_metric.py
+++ b/gso/workflows/iptrunk/modify_isis_metric.py
@@ -1,7 +1,7 @@
 from orchestrator.forms import FormPage
 from orchestrator.targets import Target
 from orchestrator.types import FormGenerator, State, UUIDstr
-from orchestrator.workflow import done, init, step, workflow
+from orchestrator.workflow import StepList, done, init, step, workflow
 from orchestrator.workflows.steps import resync, store_process_subscription, unsync
 from orchestrator.workflows.utils import wrap_modify_initial_input_form
 
@@ -65,7 +65,7 @@ def provision_ip_trunk_isis_iface_real(subscription: Iptrunk, process_id: UUIDst
     initial_input_form=wrap_modify_initial_input_form(initial_input_form_generator),
     target=Target.MODIFY,
 )
-def modify_isis_metric():
+def modify_isis_metric() -> StepList:
     return (
         init
         >> store_process_subscription(Target.MODIFY)
diff --git a/gso/workflows/iptrunk/terminate_iptrunk.py b/gso/workflows/iptrunk/terminate_iptrunk.py
index 471b9c3e55ebc72d35ea8f5b916ada4847753a16..0388b7c7e3c116aaea497c779de0053460030866 100644
--- a/gso/workflows/iptrunk/terminate_iptrunk.py
+++ b/gso/workflows/iptrunk/terminate_iptrunk.py
@@ -4,23 +4,24 @@ import ipaddress
 from orchestrator.forms import FormPage
 from orchestrator.forms.validators import Label
 from orchestrator.targets import Target
-from orchestrator.types import InputForm, State, SubscriptionLifecycle, UUIDstr
-from orchestrator.workflow import done, init, step, workflow
+from orchestrator.types import FormGenerator, State, SubscriptionLifecycle, UUIDstr
+from orchestrator.workflow import StepList, done, init, step, workflow
 from orchestrator.workflows.steps import resync, set_status, store_process_subscription, unsync
 from orchestrator.workflows.utils import wrap_modify_initial_input_form
 
 from gso.products.product_types.iptrunk import Iptrunk
 from gso.services import ipam, provisioning_proxy
+from gso.services.ipam import V4ServiceNetwork, V6ServiceNetwork
 from gso.services.provisioning_proxy import await_pp_results, confirm_pp_results
 
 
-def initial_input_form_generator(subscription_id: UUIDstr) -> InputForm:
+def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
     subscription = Iptrunk.from_subscription(subscription_id)
 
     class TerminateForm(FormPage):
-        are_you_sure: Label = f"Are you sure you want to remove {subscription.description}?"
+        are_you_sure: Label = f"Are you sure you want to remove {subscription.description}?"  # type: ignore
 
-    return TerminateForm
+    return TerminateForm  # type: ignore
 
 
 @step("Set iptrunk ISIS metric to 9000")
@@ -63,7 +64,7 @@ def deprovision_ip_trunk_real(subscription: Iptrunk, process_id: UUIDstr) -> Sta
 
 
 @step("Deprovision IPv4 networks")
-def deprovision_ip_trunk_ipv4(subscription: Iptrunk) -> None:
+def deprovision_ip_trunk_ipv4(subscription: Iptrunk) -> dict[str, V4ServiceNetwork | V6ServiceNetwork]:
     service_network = ipam.delete_service_network(
         network=ipaddress.ip_network(subscription.iptrunk.iptrunk_ipv4_network),
         service_type="TRUNK",
@@ -72,7 +73,7 @@ def deprovision_ip_trunk_ipv4(subscription: Iptrunk) -> None:
 
 
 @step("Deprovision IPv6 networks")
-def deprovision_ip_trunk_ipv6(subscription: Iptrunk) -> None:
+def deprovision_ip_trunk_ipv6(subscription: Iptrunk) -> dict[str, V4ServiceNetwork | V6ServiceNetwork]:
     service_network = ipam.delete_service_network(
         network=ipaddress.ip_network(subscription.iptrunk.iptrunk_ipv6_network),
         service_type="TRUNK",
@@ -85,7 +86,7 @@ def deprovision_ip_trunk_ipv6(subscription: Iptrunk) -> None:
     initial_input_form=wrap_modify_initial_input_form(initial_input_form_generator),
     target=Target.TERMINATE,
 )
-def terminate_iptrunk():
+def terminate_iptrunk() -> StepList:
     return (
         init
         >> store_process_subscription(Target.TERMINATE)
diff --git a/gso/workflows/site/create_site.py b/gso/workflows/site/create_site.py
index 2b3a01266bb3166a3e11d3f0ecbd6e115fbbdb40..30dc2c17e180024f67b83901ad4b343aaee36026 100644
--- a/gso/workflows/site/create_site.py
+++ b/gso/workflows/site/create_site.py
@@ -3,7 +3,7 @@ from uuid import uuid4
 from orchestrator.forms import FormPage
 from orchestrator.targets import Target
 from orchestrator.types import FormGenerator, State, SubscriptionLifecycle, UUIDstr
-from orchestrator.workflow import done, init, step, workflow, StepList
+from orchestrator.workflow import StepList, done, init, step, workflow
 from orchestrator.workflows.steps import resync, set_status, store_process_subscription
 from orchestrator.workflows.utils import wrap_create_initial_input_form
 
diff --git a/pyproject.toml b/pyproject.toml
index ce6ec4bfe4cca6701f84ab0ec716bb07580661b5..e3b30045968e907623b058f4f53ff004d7263748 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,3 +1,20 @@
+[project]
+name = "geant-service-orchestrator"
+dynamic = ['version']
+description = "GEANT Service Orchestrator"
+authors = [
+    {name = "GEANT", email = "swd@geant.org"}
+]
+dependencies = [
+    "orchestrator-core==1.0.0",
+    "pydantic",
+    "requests",
+]
+requires-python = ">=3.9,<3.12"
+
+[tool.setuptools]
+packages = ["gso"]
+
 [tool.isort]
 profile = "black"
 line_length = 120
@@ -17,7 +34,6 @@ exclude = '''
     | \.tox
     | venv
     | gso/migrations
-    | gso/services/_ipam\.py
   )/
 )
 '''
@@ -25,8 +41,7 @@ exclude = '''
 [tool.mypy]
 exclude = [
     "venv",
-    "test/*",
-    "gso/services/_ipam.py"  # TODO: remove
+    "test/*"
 ]
 ignore_missing_imports = true
 disallow_untyped_calls = true
@@ -70,6 +85,8 @@ ignore = [
     "D106",
     "D107",
     "D202",
+    "D203",
+    "D213",
     "E501",
     "N806",
     "B905",
diff --git a/setup.py b/setup.py
deleted file mode 100644
index e6e2fd8bd0a414877cbda505b5b2e2042e6556d2..0000000000000000000000000000000000000000
--- a/setup.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from setuptools import find_packages, setup
-
-setup(
-    name="geant-service-orchestrator",
-    version="0.1",
-    author="GEANT",
-    author_email="swd@geant.org",
-    description="GEANT Service Orchestrator",
-    url="https://gitlab.geant.org/goat/geant-service-orchestrator",
-    packages=find_packages(),
-    install_requires=[
-        "orchestrator-core==1.0.0",
-        "pydantic",
-        "requests",
-    ],
-)
diff --git a/tox.ini b/tox.ini
index 2d4a26e64267682720a9316185439fac62b26fbb..c355ae922d5d3fc2bca61abf476612e634f689cc 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [flake8]
-ignore = D100,D101,D102,D103,D104,D105,D106,D107,D202,E501,RST301,RST304,W503,E203,C417,T202
+ignore = D100,D101,D102,D103,D104,D105,D106,D107,D202,E501,RST301,RST304,W503,E203,C417,T202,S101
 ; extend-ignore = E203
 exclude = .git,.*_cache,.eggs,*.egg-info,__pycache__,venv,.tox,gso/migrations
 enable-extensions = G
@@ -9,7 +9,6 @@ ban-relative-imports = true
 per-file-ignores =
 	# Allow first argument to be cls instead of self for pydantic validators
 	gso/*: B902
-	test/*: S101
 
 [testenv]
 deps =
@@ -19,6 +18,7 @@ deps =
     mypy
     ruff
     isort
+    types-requests
     -r requirements.txt
 
 commands =