diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 45164592a74d3f5e59204397932331edde5141a3..a825c2db1f8b768cee467c4c074bfb1b50fdac17 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -10,8 +10,3 @@ repos:
           - --preview
           - --ignore=PLR0917,PLR0914
           - --extend-exclude=test/*
-      # Run the formatter.
-      - id: ruff-format
-        args:
-          - --preview
-          - --exclude=test/*
diff --git a/Changelog.md b/Changelog.md
index d87116de079b184e123062fd106f19bcd2abc6c9..43ee4a2cea5e839c0e626e65a53c650d31ca4e83 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,6 +1,12 @@
 # Changelog
 
 All notable changes to this project will be documented in this file.
+
+## [0.8] - 2024-02-28
+- Add two new workflows for "activating" `Router` and `Iptrunk` products.
+- Update lifecycle states for `Router` and `Iptrunk` products.
+- Fix an issue in the Infoblox client when using a custom `dns_view`.
+
 ## [0.7] - 2024-02-21
 - Infoblox client: added support for the `network_view` (IPAM).
 
diff --git a/docs/source/glossary.rst b/docs/source/glossary.rst
index 0bbcce276f79bee067aef114ea5b0f0c3101ab39..432f73de9e8cdd7f7fd38c5e99f9b434fb1fd82c 100644
--- a/docs/source/glossary.rst
+++ b/docs/source/glossary.rst
@@ -51,6 +51,9 @@ Glossary of terms
   NET
     Network Entity Title: used for :term:`ISIS` routing.
 
+  OOB
+    Out-of-band access
+
   OSS
     Operational Support Systems
 
diff --git a/docs/source/module/products/product_blocks/index.rst b/docs/source/module/products/product_blocks/index.rst
index 309e18b62f81f25b4cfffe6c60fd77b46bd07d06..d106bb2ca83b6c2ffca86694249113bd310c0560 100644
--- a/docs/source/module/products/product_blocks/index.rst
+++ b/docs/source/module/products/product_blocks/index.rst
@@ -14,6 +14,8 @@ Submodules
 .. toctree::
    :maxdepth: 1
 
+   super_pop_switch
+   office_router
    iptrunk
    router
    site
diff --git a/docs/source/module/products/product_blocks/office_router.rst b/docs/source/module/products/product_blocks/office_router.rst
new file mode 100644
index 0000000000000000000000000000000000000000..4d6b64f990c83bb5b7ae288c0d0f460b5e1a7a63
--- /dev/null
+++ b/docs/source/module/products/product_blocks/office_router.rst
@@ -0,0 +1,6 @@
+``gso.products.product_blocks.office_router``
+=============================================
+
+.. automodule:: gso.products.product_blocks.office_router
+   :members:
+   :show-inheritance:
diff --git a/docs/source/module/products/product_blocks/super_pop_switch.rst b/docs/source/module/products/product_blocks/super_pop_switch.rst
new file mode 100644
index 0000000000000000000000000000000000000000..1f3808a75ba3b391cefb61fb3edc6fecb6c49b91
--- /dev/null
+++ b/docs/source/module/products/product_blocks/super_pop_switch.rst
@@ -0,0 +1,6 @@
+``gso.products.product_blocks.super_pop_switch``
+================================================
+
+.. automodule:: gso.products.product_blocks.super_pop_switch
+   :members:
+   :show-inheritance:
diff --git a/docs/source/module/products/product_types/index.rst b/docs/source/module/products/product_types/index.rst
index 0f79a699cb591afbadf08bb70e001610ef4adcf6..de30f01e26c15cb6f279a2720c1d90d1c21f4f15 100644
--- a/docs/source/module/products/product_types/index.rst
+++ b/docs/source/module/products/product_types/index.rst
@@ -14,6 +14,8 @@ Submodules
 .. toctree::
    :maxdepth: 1
 
+   super_pop_switch
+   office_router
    iptrunk
    router
    site
diff --git a/docs/source/module/products/product_types/office_router.rst b/docs/source/module/products/product_types/office_router.rst
new file mode 100644
index 0000000000000000000000000000000000000000..d6e5b9974eaa2d53b8641ba099a564415a22d768
--- /dev/null
+++ b/docs/source/module/products/product_types/office_router.rst
@@ -0,0 +1,6 @@
+``gso.products.product_types.office_router``
+============================================
+
+.. automodule:: gso.products.product_types.office_router
+   :members:
+   :show-inheritance:
diff --git a/docs/source/module/products/product_types/super_pop_switch.rst b/docs/source/module/products/product_types/super_pop_switch.rst
new file mode 100644
index 0000000000000000000000000000000000000000..442e48ffe84d076d35f072e6a7ae72460cf0b37f
--- /dev/null
+++ b/docs/source/module/products/product_types/super_pop_switch.rst
@@ -0,0 +1,6 @@
+``gso.products.product_types.super_pop_switch``
+===============================================
+
+.. automodule:: gso.products.product_types.super_pop_switch
+   :members:
+   :show-inheritance:
diff --git a/docs/source/module/utils/index.rst b/docs/source/module/utils/index.rst
index 43b99a3592c9fb4f25d6d6aeb9b53be61586fbab..0bb43e3e0af577d3d15f9ec1cc6e189a19928eb4 100644
--- a/docs/source/module/utils/index.rst
+++ b/docs/source/module/utils/index.rst
@@ -12,6 +12,7 @@ Submodules
    :maxdepth: 2
    :titlesonly:
 
+   shared_choices
    device_info
    exceptions
    helpers
diff --git a/docs/source/module/utils/shared_choices.rst b/docs/source/module/utils/shared_choices.rst
new file mode 100644
index 0000000000000000000000000000000000000000..46460a304905ef81cdd14481cb316d242adc62bf
--- /dev/null
+++ b/docs/source/module/utils/shared_choices.rst
@@ -0,0 +1,6 @@
+``gso.utils.shared_choices``
+============================
+
+.. automodule:: gso.utils.shared_choices
+   :members:
+   :show-inheritance:
diff --git a/docs/source/module/workflows/iptrunk/activate_iptrunk.rst b/docs/source/module/workflows/iptrunk/activate_iptrunk.rst
new file mode 100644
index 0000000000000000000000000000000000000000..9082a9df171dfa20c0a536f37d2fac6b09a9cd7b
--- /dev/null
+++ b/docs/source/module/workflows/iptrunk/activate_iptrunk.rst
@@ -0,0 +1,6 @@
+``gso.workflows.iptrunk.activate_iptrunk``
+==========================================
+
+.. automodule:: gso.workflows.iptrunk.activate_iptrunk
+   :members:
+   :show-inheritance:
diff --git a/docs/source/module/workflows/iptrunk/index.rst b/docs/source/module/workflows/iptrunk/index.rst
index 089aa1249ae7597e14953f60da9120d38de4c3f8..f046983fd0c9650d0aa72315a48d875525b312f7 100644
--- a/docs/source/module/workflows/iptrunk/index.rst
+++ b/docs/source/module/workflows/iptrunk/index.rst
@@ -12,6 +12,7 @@ Submodules
    :maxdepth: 2
    :titlesonly:
 
+   activate_iptrunk
    create_iptrunk
    migrate_iptrunk
    modify_isis_metric
diff --git a/docs/source/module/workflows/router/activate_router.rst b/docs/source/module/workflows/router/activate_router.rst
new file mode 100644
index 0000000000000000000000000000000000000000..383502dd3d283c9bdcaf36d7b1cabae88fd06a29
--- /dev/null
+++ b/docs/source/module/workflows/router/activate_router.rst
@@ -0,0 +1,6 @@
+``gso.workflows.router.activate_router``
+========================================
+
+.. automodule:: gso.workflows.router.activate_router
+   :members:
+   :show-inheritance:
diff --git a/docs/source/module/workflows/router/index.rst b/docs/source/module/workflows/router/index.rst
index 11c3f71c734ac62f2e3ea194222eb6478816b335..e582a4402c8c17ba1292fcb1763cdcce193109a0 100644
--- a/docs/source/module/workflows/router/index.rst
+++ b/docs/source/module/workflows/router/index.rst
@@ -12,5 +12,8 @@ Submodules
    :maxdepth: 2
    :titlesonly:
 
+   activate_router
    create_router
+   redeploy_base_config
    terminate_router
+   update_ibgp_mesh
diff --git a/docs/source/module/workflows/router/redeploy_base_config.rst b/docs/source/module/workflows/router/redeploy_base_config.rst
new file mode 100644
index 0000000000000000000000000000000000000000..1348f983d743d9c174cc0c6629a1447b838234c6
--- /dev/null
+++ b/docs/source/module/workflows/router/redeploy_base_config.rst
@@ -0,0 +1,6 @@
+``gso.workflows.router.redeploy_base_config``
+=============================================
+
+.. automodule:: gso.workflows.router.redeploy_base_config
+   :members:
+   :show-inheritance:
diff --git a/docs/source/module/workflows/router/update_ibgp_mesh.rst b/docs/source/module/workflows/router/update_ibgp_mesh.rst
new file mode 100644
index 0000000000000000000000000000000000000000..213aacb758b8f7dad57260f4e9aa5d6e7d3edf12
--- /dev/null
+++ b/docs/source/module/workflows/router/update_ibgp_mesh.rst
@@ -0,0 +1,6 @@
+``gso.workflows.router.update_ibgp_mesh``
+=========================================
+
+.. automodule:: gso.workflows.router.update_ibgp_mesh
+   :members:
+   :show-inheritance:
diff --git a/docs/source/module/workflows/tasks/import_office_router.rst b/docs/source/module/workflows/tasks/import_office_router.rst
new file mode 100644
index 0000000000000000000000000000000000000000..a6b37df5df88e9e57590be22495dfa213cfba177
--- /dev/null
+++ b/docs/source/module/workflows/tasks/import_office_router.rst
@@ -0,0 +1,6 @@
+``gso.workflows.tasks.import_office_router``
+============================================
+
+.. automodule:: gso.workflows.tasks.import_office_router
+   :members:
+   :show-inheritance:
diff --git a/docs/source/module/workflows/tasks/import_super_pop_switch.rst b/docs/source/module/workflows/tasks/import_super_pop_switch.rst
new file mode 100644
index 0000000000000000000000000000000000000000..575db1e0e343ffbe1436c6822276524bbcf456a0
--- /dev/null
+++ b/docs/source/module/workflows/tasks/import_super_pop_switch.rst
@@ -0,0 +1,6 @@
+``gso.workflows.tasks.import_super_pop_switch``
+===============================================
+
+.. automodule:: gso.workflows.tasks.import_super_pop_switch
+   :members:
+   :show-inheritance:
diff --git a/docs/source/module/workflows/tasks/index.rst b/docs/source/module/workflows/tasks/index.rst
index 8feb3de360ac547ed291e2dfceb07d84e5724c75..1931adef62a66b169c5e5b423df4f31022cfa4d5 100644
--- a/docs/source/module/workflows/tasks/index.rst
+++ b/docs/source/module/workflows/tasks/index.rst
@@ -12,6 +12,8 @@ Submodules
    :maxdepth: 2
    :titlesonly:
 
+   import_super_pop_switch
+   import_office_router
    import_iptrunk
    import_router
    import_site
diff --git a/gso/api/v1/imports.py b/gso/api/v1/imports.py
index 684d7b7eb43bd878b5d3919a768f7a70ab33172c..dd77bc80e6145ae35d75fdd9514cf564d606d98c 100644
--- a/gso/api/v1/imports.py
+++ b/gso/api/v1/imports.py
@@ -11,11 +11,12 @@ from pydantic import BaseModel, root_validator, validator
 
 from gso.auth.security import opa_security_default
 from gso.products.product_blocks.iptrunk import IptrunkType, PhyPortCapacity
-from gso.products.product_blocks.router import RouterRole, RouterVendor
+from gso.products.product_blocks.router import RouterRole
 from gso.products.product_blocks.site import SiteTier
 from gso.services import subscriptions
 from gso.services.crm import CustomerNotFoundError, get_customer_by_name
 from gso.utils.helpers import BaseSiteValidatorModel, LAGMember
+from gso.utils.shared_enums import PortNumber, Vendor
 
 router = APIRouter(prefix="/imports", tags=["Imports"], dependencies=[Depends(opa_security_default)])
 
@@ -50,7 +51,7 @@ class RouterImportModel(BaseModel):
     router_site: str
     hostname: str
     ts_port: int
-    router_vendor: RouterVendor
+    router_vendor: Vendor
     router_role: RouterRole
     router_lo_ipv4_address: ipaddress.IPv4Address
     router_lo_ipv6_address: ipaddress.IPv6Address
@@ -136,6 +137,27 @@ class IptrunkImportModel(BaseModel):
         return values
 
 
+class SuperPopSwitchImportModel(BaseModel):
+    """Required fields for importing an existing :class:`gso.product.product_types.super_pop_switch`."""
+
+    customer: str
+    super_pop_switch_site: str
+    hostname: str
+    super_pop_switch_ts_port: PortNumber
+    super_pop_switch_mgmt_ipv4_address: ipaddress.IPv4Address
+
+
+class OfficeRouterImportModel(BaseModel):
+    """Required fields for importing an existing :class:`gso.product.product_types.office_router`."""
+
+    customer: str
+    office_router_site: str
+    office_router_fqdn: str
+    office_router_ts_port: PortNumber
+    office_router_lo_ipv4_address: ipaddress.IPv4Address
+    office_router_lo_ipv6_address: ipaddress.IPv6Address
+
+
 def _start_process(process_name: str, data: dict) -> UUID:
     """Start a process and handle common exceptions."""
     pid: UUID = processes.start_process(process_name, [data])
@@ -184,7 +206,7 @@ def import_router(router_data: RouterImportModel) -> dict[str, Any]:
     :raises HTTPException: If there's an error in the process.
     """
     pid = _start_process("import_router", router_data.dict())
-    return {"detail": "Router added successfully", "pid": pid}
+    return {"detail": "Router has been added successfully", "pid": pid}
 
 
 @router.post("/iptrunks", status_code=status.HTTP_201_CREATED, response_model=ImportResponseModel)
@@ -200,4 +222,36 @@ def import_iptrunk(iptrunk_data: IptrunkImportModel) -> dict[str, Any]:
     :raises HTTPException: If there's an error in the process.
     """
     pid = _start_process("import_iptrunk", iptrunk_data.dict())
-    return {"detail": "Iptrunk added successfully", "pid": pid}
+    return {"detail": "Iptrunk has been added successfully", "pid": pid}
+
+
+@router.post("/super-pop-switches", status_code=status.HTTP_201_CREATED, response_model=ImportResponseModel)
+def import_super_pop_switch(super_pop_switch_data: SuperPopSwitchImportModel) -> dict[str, Any]:
+    """Import a Super PoP switch by running the import_super_pop_switch workflow.
+
+    :param super_pop_switch_data: The Super PoP switch information to be imported.
+    :type super_pop_switch_data: SuperPopSwitchImportModel
+
+    :return: A dictionary containing the process id of the started process and detail message.
+    :rtype: dict[str, Any]
+
+    :raises HTTPException: If there's an error in the process.
+    """
+    pid = _start_process("import_super_pop_switch", super_pop_switch_data.dict())
+    return {"detail": "Super PoP switch has been added successfully", "pid": pid}
+
+
+@router.post("/office-routers", status_code=status.HTTP_201_CREATED, response_model=ImportResponseModel)
+def import_office_router(office_router_data: OfficeRouterImportModel) -> dict[str, Any]:
+    """Import a office router by running the import_office_router workflow.
+
+    :param office_router_data: The office router information to be imported.
+    :type office_router_data: OfficeRouterImportModel
+
+    :return: A dictionary containing the process id of the started process and detail message.
+    :rtype: dict[str, Any]
+
+    :raises HTTPException: If there's an error in the process.
+    """
+    pid = _start_process("import_office_router", office_router_data.dict())
+    return {"detail": "Office router has been added successfully", "pid": pid}
diff --git a/gso/cli/imports.py b/gso/cli/imports.py
index 6aa55c5dfc6bb5701d4cf81f5db6d6a1c3244d12..1a20c14be3840b76a6085f8756af943ef27c9b3d 100644
--- a/gso/cli/imports.py
+++ b/gso/cli/imports.py
@@ -11,17 +11,23 @@ from pydantic import ValidationError
 
 from gso.api.v1.imports import (
     IptrunkImportModel,
+    OfficeRouterImportModel,
     RouterImportModel,
     SiteImportModel,
+    SuperPopSwitchImportModel,
     import_iptrunk,
+    import_office_router,
     import_router,
     import_site,
+    import_super_pop_switch,
 )
 from gso.services.subscriptions import get_active_subscriptions_by_field_and_value
 
 app: typer.Typer = typer.Typer()
 
-T = TypeVar("T", SiteImportModel, RouterImportModel, IptrunkImportModel)
+T = TypeVar(
+    "T", SiteImportModel, RouterImportModel, IptrunkImportModel, SuperPopSwitchImportModel, OfficeRouterImportModel
+)
 
 common_filepath_option = typer.Option(
     default="data.json",
@@ -89,6 +95,20 @@ def import_routers(filepath: str = common_filepath_option) -> None:
     generic_import_data(filepath, RouterImportModel, import_router, "hostname")
 
 
+@app.command()
+def import_super_pop_switches(filepath: str = common_filepath_option) -> None:
+    """Import Super PoP Switches into GSO."""
+    # Use the import_data function to handle common import logic
+    generic_import_data(filepath, SuperPopSwitchImportModel, import_super_pop_switch, "hostname")
+
+
+@app.command()
+def import_office_routers(filepath: str = common_filepath_option) -> None:
+    """Import office routers into GSO."""
+    # Use the import_data function to handle common import logic
+    generic_import_data(filepath, OfficeRouterImportModel, import_office_router, "office_router_fqdn")
+
+
 def get_router_subscription_id(node_name: str) -> str | None:
     """Get the subscription id for a router by its node name."""
     subscriptions = get_active_subscriptions_by_field_and_value(
diff --git a/gso/migrations/versions/2024-02-14_1c3c90ea1d8c_add_super_pop_switch_product.py b/gso/migrations/versions/2024-02-14_1c3c90ea1d8c_add_super_pop_switch_product.py
new file mode 100644
index 0000000000000000000000000000000000000000..aac35ee3331ab554593c1015cd655d69869b8b3c
--- /dev/null
+++ b/gso/migrations/versions/2024-02-14_1c3c90ea1d8c_add_super_pop_switch_product.py
@@ -0,0 +1,113 @@
+"""Add Super PoP switch product..
+
+Revision ID: 1c3c90ea1d8c
+Revises: bacd55c26106
+Create Date: 2024-02-14 11:00:11.858023
+
+"""
+import sqlalchemy as sa
+from alembic import op
+
+# revision identifiers, used by Alembic.
+revision = '1c3c90ea1d8c'
+down_revision = '113a81d2a40a'
+branch_labels = None
+depends_on = None
+
+
+def upgrade() -> None:
+    conn = op.get_bind()
+    conn.execute(sa.text("""
+INSERT INTO products (name, description, product_type, tag, status) VALUES ('Super PoP switch', 'Super PoP switch', 'SuperPopSwitch', 'Super_POP_SWITCH', 'active') RETURNING products.product_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_blocks (name, description, tag, status) VALUES ('SuperPopSwitchBlock', 'Super PoP switch block', 'SUPER_POP_SWITCH_BLK', 'active') RETURNING product_blocks.product_block_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO resource_types (resource_type, description) VALUES ('super_pop_switch_ts_port', 'The port of the terminal server that this Super PoP switch is connected to. Used to offer out of band access.') RETURNING resource_types.resource_type_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO resource_types (resource_type, description) VALUES ('super_pop_switch_fqdn', 'Super PoP switch FQDN.') RETURNING resource_types.resource_type_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO resource_types (resource_type, description) VALUES ('super_pop_switch_mgmt_ipv4_address', 'The IPv4 management address of the Super PoP switch.') RETURNING resource_types.resource_type_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_product_blocks (product_id, product_block_id) VALUES ((SELECT products.product_id FROM products WHERE products.name IN ('Super PoP switch')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SuperPopSwitchBlock')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_relations (in_use_by_id, depends_on_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SuperPopSwitchBlock')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SiteBlock')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SuperPopSwitchBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('super_pop_switch_fqdn')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SuperPopSwitchBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('super_pop_switch_ts_port')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SuperPopSwitchBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('super_pop_switch_mgmt_ipv4_address')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SuperPopSwitchBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('vendor')))
+    """))
+
+
+def downgrade() -> None:
+    conn = op.get_bind()
+    conn.execute(sa.text("""
+DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SuperPopSwitchBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('super_pop_switch_fqdn'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SuperPopSwitchBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('super_pop_switch_fqdn'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SuperPopSwitchBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('super_pop_switch_ts_port'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SuperPopSwitchBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('super_pop_switch_ts_port'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SuperPopSwitchBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('super_pop_switch_mgmt_ipv4_address'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SuperPopSwitchBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('super_pop_switch_mgmt_ipv4_address'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SuperPopSwitchBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('vendor'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SuperPopSwitchBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('vendor'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values WHERE subscription_instance_values.resource_type_id IN (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('super_pop_switch_ts_port', 'super_pop_switch_fqdn', 'super_pop_switch_mgmt_ipv4_address'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM resource_types WHERE resource_types.resource_type IN ('super_pop_switch_ts_port', 'super_pop_switch_fqdn', 'super_pop_switch_mgmt_ipv4_address')
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_product_blocks WHERE product_product_blocks.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Super PoP switch')) AND product_product_blocks.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SuperPopSwitchBlock'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_relations WHERE product_block_relations.in_use_by_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SuperPopSwitchBlock')) AND product_block_relations.depends_on_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SiteBlock'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instances WHERE subscription_instances.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SuperPopSwitchBlock'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_blocks WHERE product_blocks.name IN ('SuperPopSwitchBlock')
+    """))
+    conn.execute(sa.text("""
+DELETE FROM processes WHERE processes.pid IN (SELECT processes_subscriptions.pid FROM processes_subscriptions WHERE processes_subscriptions.subscription_id IN (SELECT subscriptions.subscription_id FROM subscriptions WHERE subscriptions.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Super PoP switch'))))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM processes_subscriptions WHERE processes_subscriptions.subscription_id IN (SELECT subscriptions.subscription_id FROM subscriptions WHERE subscriptions.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Super PoP switch')))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instances WHERE subscription_instances.subscription_id IN (SELECT subscriptions.subscription_id FROM subscriptions WHERE subscriptions.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Super PoP switch')))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscriptions WHERE subscriptions.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Super PoP switch'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM products WHERE products.name IN ('Super PoP switch')
+    """))
diff --git a/gso/migrations/versions/2024-02-20_6e4952687205_add_office_router_product.py b/gso/migrations/versions/2024-02-20_6e4952687205_add_office_router_product.py
new file mode 100644
index 0000000000000000000000000000000000000000..631facbc24057107a3ae7274e379e397fe308fd5
--- /dev/null
+++ b/gso/migrations/versions/2024-02-20_6e4952687205_add_office_router_product.py
@@ -0,0 +1,125 @@
+"""Add office router product..
+
+Revision ID: 6e4952687205
+Revises: 1c3c90ea1d8c
+Create Date: 2024-02-20 11:22:30.804258
+
+"""
+import sqlalchemy as sa
+from alembic import op
+
+# revision identifiers, used by Alembic.
+revision = '6e4952687205'
+down_revision = '1c3c90ea1d8c'
+branch_labels = None
+depends_on = None
+
+
+def upgrade() -> None:
+    conn = op.get_bind()
+    conn.execute(sa.text("""
+INSERT INTO products (name, description, product_type, tag, status) VALUES ('Office router', 'Office router product', 'OfficeRouter', 'OFFICE_ROUTER', 'active') RETURNING products.product_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_blocks (name, description, tag, status) VALUES ('OfficeRouterBlock', 'Office router product block', 'OFFICE_ROUTER_BLOCK', 'active') RETURNING product_blocks.product_block_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO resource_types (resource_type, description) VALUES ('office_router_fqdn', 'Office router FQDN') RETURNING resource_types.resource_type_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO resource_types (resource_type, description) VALUES ('office_router_ts_port', 'The port of the terminal server that this office router is connected to') RETURNING resource_types.resource_type_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO resource_types (resource_type, description) VALUES ('office_router_lo_ipv4_address', 'The IPv4 loopback address of the office router.') RETURNING resource_types.resource_type_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO resource_types (resource_type, description) VALUES ('office_router_lo_ipv6_address', 'The IPv6 loopback address of the office router.') RETURNING resource_types.resource_type_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_product_blocks (product_id, product_block_id) VALUES ((SELECT products.product_id FROM products WHERE products.name IN ('Office router')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('OfficeRouterBlock')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_relations (in_use_by_id, depends_on_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('OfficeRouterBlock')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SiteBlock')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('OfficeRouterBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('office_router_fqdn')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('OfficeRouterBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('office_router_ts_port')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('OfficeRouterBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('office_router_lo_ipv4_address')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('OfficeRouterBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('office_router_lo_ipv6_address')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('OfficeRouterBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('vendor')))
+    """))
+
+
+def downgrade() -> None:
+    conn = op.get_bind()
+    conn.execute(sa.text("""
+DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('OfficeRouterBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('office_router_fqdn'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('OfficeRouterBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('office_router_fqdn'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('OfficeRouterBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('office_router_ts_port'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('OfficeRouterBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('office_router_ts_port'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('OfficeRouterBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('office_router_lo_ipv4_address'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('OfficeRouterBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('office_router_lo_ipv4_address'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('OfficeRouterBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('office_router_lo_ipv6_address'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('OfficeRouterBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('office_router_lo_ipv6_address'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('OfficeRouterBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('vendor'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('OfficeRouterBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('vendor'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values WHERE subscription_instance_values.resource_type_id IN (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('office_router_fqdn', 'office_router_ts_port', 'office_router_lo_ipv4_address', 'office_router_lo_ipv6_address'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM resource_types WHERE resource_types.resource_type IN ('office_router_fqdn', 'office_router_ts_port', 'office_router_lo_ipv4_address', 'office_router_lo_ipv6_address')
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_product_blocks WHERE product_product_blocks.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Office router')) AND product_product_blocks.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('OfficeRouterBlock'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_relations WHERE product_block_relations.in_use_by_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('OfficeRouterBlock')) AND product_block_relations.depends_on_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SiteBlock'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instances WHERE subscription_instances.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('OfficeRouterBlock'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_blocks WHERE product_blocks.name IN ('OfficeRouterBlock')
+    """))
+    conn.execute(sa.text("""
+DELETE FROM processes WHERE processes.pid IN (SELECT processes_subscriptions.pid FROM processes_subscriptions WHERE processes_subscriptions.subscription_id IN (SELECT subscriptions.subscription_id FROM subscriptions WHERE subscriptions.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Office router'))))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM processes_subscriptions WHERE processes_subscriptions.subscription_id IN (SELECT subscriptions.subscription_id FROM subscriptions WHERE subscriptions.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Office router')))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instances WHERE subscription_instances.subscription_id IN (SELECT subscriptions.subscription_id FROM subscriptions WHERE subscriptions.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Office router')))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscriptions WHERE subscriptions.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Office router'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM products WHERE products.name IN ('Office router')
+    """))
diff --git a/gso/migrations/versions/2024-02-21_113a81d2a40a_add_router_activation_workflow.py b/gso/migrations/versions/2024-02-21_113a81d2a40a_add_router_activation_workflow.py
new file mode 100644
index 0000000000000000000000000000000000000000..61237f322953bfaa4c55c0cfcb4690f3237cb988
--- /dev/null
+++ b/gso/migrations/versions/2024-02-21_113a81d2a40a_add_router_activation_workflow.py
@@ -0,0 +1,39 @@
+"""Add Router activation workflow.
+
+Revision ID: 113a81d2a40a
+Revises: bacd55c26106
+Create Date: 2024-02-21 10:28:18.340922
+
+"""
+import sqlalchemy as sa
+from alembic import op
+
+# revision identifiers, used by Alembic.
+revision = '113a81d2a40a'
+down_revision = 'bacd55c26106'
+branch_labels = None
+depends_on = None
+
+
+from orchestrator.migrations.helpers import create_workflow, delete_workflow
+
+new_workflows = [
+    {
+        "name": "activate_router",
+        "target": "MODIFY",
+        "description": "Activate a router",
+        "product_type": "Router"
+    }
+]
+
+
+def upgrade() -> None:
+    conn = op.get_bind()
+    for workflow in new_workflows:
+        create_workflow(conn, workflow)
+
+
+def downgrade() -> None:
+    conn = op.get_bind()
+    for workflow in new_workflows:
+        delete_workflow(conn, workflow["name"])
diff --git a/gso/migrations/versions/2024-02-27_5bea5647f61d_add_ip_trunk_activation_workflow.py b/gso/migrations/versions/2024-02-27_5bea5647f61d_add_ip_trunk_activation_workflow.py
new file mode 100644
index 0000000000000000000000000000000000000000..c7b03f9f4f1c2ccc9b953d8afa4b38819d0ea7e5
--- /dev/null
+++ b/gso/migrations/versions/2024-02-27_5bea5647f61d_add_ip_trunk_activation_workflow.py
@@ -0,0 +1,39 @@
+"""Add IP Trunk activation workflow.
+
+Revision ID: 5bea5647f61d
+Revises: 113a81d2a40a
+Create Date: 2024-02-27 17:01:57.300326
+
+"""
+import sqlalchemy as sa
+from alembic import op
+
+# revision identifiers, used by Alembic.
+revision = '5bea5647f61d'
+down_revision = '113a81d2a40a'
+branch_labels = None
+depends_on = None
+
+
+from orchestrator.migrations.helpers import create_workflow, delete_workflow
+
+new_workflows = [
+    {
+        "name": "activate_iptrunk",
+        "target": "MODIFY",
+        "description": "Activate an IP Trunk",
+        "product_type": "Iptrunk"
+    }
+]
+
+
+def upgrade() -> None:
+    conn = op.get_bind()
+    for workflow in new_workflows:
+        create_workflow(conn, workflow)
+
+
+def downgrade() -> None:
+    conn = op.get_bind()
+    for workflow in new_workflows:
+        delete_workflow(conn, workflow["name"])
diff --git a/gso/products/__init__.py b/gso/products/__init__.py
index bd721395366659c40907dd13a07fc28ec2fa7d76..2fd25d9fe556299706f618213d06a9ae9f04763e 100644
--- a/gso/products/__init__.py
+++ b/gso/products/__init__.py
@@ -9,8 +9,10 @@ from orchestrator.domain import SUBSCRIPTION_MODEL_REGISTRY
 from pydantic_forms.types import strEnum
 
 from gso.products.product_types.iptrunk import Iptrunk
+from gso.products.product_types.office_router import OfficeRouter
 from gso.products.product_types.router import Router
 from gso.products.product_types.site import Site
+from gso.products.product_types.super_pop_switch import SuperPopSwitch
 
 
 class ProductType(strEnum):
@@ -19,6 +21,8 @@ class ProductType(strEnum):
     IP_TRUNK = "IP trunk"
     ROUTER = "Router"
     SITE = "Site"
+    SUPER_POP_SWITCH = "Super PoP switch"
+    OFFICE_ROUTER = "Office router"
 
 
 SUBSCRIPTION_MODEL_REGISTRY.update(
@@ -26,5 +30,7 @@ SUBSCRIPTION_MODEL_REGISTRY.update(
         "IP trunk": Iptrunk,
         "Router": Router,
         "Site": Site,
+        "Super PoP switch": SuperPopSwitch,
+        "Office router": OfficeRouter,
     },
 )
diff --git a/gso/products/product_blocks/office_router.py b/gso/products/product_blocks/office_router.py
new file mode 100644
index 0000000000000000000000000000000000000000..fec7ad8d16366baf12ec3528748f71aa2fa36d90
--- /dev/null
+++ b/gso/products/product_blocks/office_router.py
@@ -0,0 +1,56 @@
+"""Product block for :class:`office router` products."""
+
+import ipaddress
+
+from orchestrator.domain.base import ProductBlockModel
+from orchestrator.types import SubscriptionLifecycle
+
+from gso.products.product_blocks.site import (
+    SiteBlock,
+    SiteBlockInactive,
+    SiteBlockProvisioning,
+)
+from gso.utils.shared_enums import PortNumber, Vendor
+
+
+class OfficeRouterBlockInactive(
+    ProductBlockModel,
+    lifecycle=[SubscriptionLifecycle.INITIAL],
+    product_block_name="OfficeRouterBlock",
+):
+    """An office router that's being currently inactive. See :class:`OfficeRouterBlock`."""
+
+    office_router_fqdn: str | None = None
+    office_router_ts_port: PortNumber | None = None
+    office_router_lo_ipv4_address: ipaddress.IPv4Address | None = None
+    office_router_lo_ipv6_address: ipaddress.IPv6Address | None = None
+    office_router_site: SiteBlockInactive | None
+    vendor: Vendor | None = None
+
+
+class OfficeRouterBlockProvisioning(OfficeRouterBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]):
+    """An office router that's being provisioned. See :class:`RouterBlock`."""
+
+    office_router_fqdn: str | None = None
+    office_router_ts_port: PortNumber | None = None
+    office_router_lo_ipv4_address: ipaddress.IPv4Address | None = None
+    office_router_lo_ipv6_address: ipaddress.IPv6Address | None = None
+    office_router_site: SiteBlockProvisioning | None
+    vendor: Vendor | None = None
+
+
+class OfficeRouterBlock(OfficeRouterBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
+    """An office router that's currently deployed in the network."""
+
+    #:  Office router FQDN.
+    office_router_fqdn: str
+    #:  The port of the terminal server that this office router is connected to. Used to offer out of band access.
+    office_router_ts_port: PortNumber
+    #:  The IPv4 loopback address of the office router.
+    office_router_lo_ipv4_address: ipaddress.IPv4Address
+    #:  The IPv6 loopback address of the office router.
+    office_router_lo_ipv6_address: ipaddress.IPv6Address
+    #:  The :class:`Site` that this office router resides in. Both physically and computationally.
+    office_router_site: SiteBlock
+    #:  The vendor of an office router. Defaults to Juniper.
+    vendor: Vendor = Vendor.JUNIPER
diff --git a/gso/products/product_blocks/router.py b/gso/products/product_blocks/router.py
index 5d1a307d85825759b734a0c06b665d1e7eb06866..f91bf1c70507a2f7814bfe69643c70489cb0c4c2 100644
--- a/gso/products/product_blocks/router.py
+++ b/gso/products/product_blocks/router.py
@@ -4,14 +4,13 @@ import ipaddress
 
 from orchestrator.domain.base import ProductBlockModel
 from orchestrator.types import SubscriptionLifecycle, strEnum
-from pydantic import ConstrainedInt
 
-from gso import settings
 from gso.products.product_blocks.site import (
     SiteBlock,
     SiteBlockInactive,
     SiteBlockProvisioning,
 )
+from gso.utils.shared_enums import PortNumber, Vendor
 
 
 class RouterRole(strEnum):
@@ -22,23 +21,6 @@ class RouterRole(strEnum):
     AMT = "amt"
 
 
-class RouterVendor(strEnum):
-    """Enumerator for the different product vendors that are supported."""
-
-    JUNIPER = "juniper"
-    NOKIA = "nokia"
-
-
-class PortNumber(ConstrainedInt):
-    """Constrained integer for valid port numbers.
-
-    The range from 49152 to 65535 is marked as ephemeral, and can therefore not be selected for permanent allocation.
-    """
-
-    gt = 0
-    le = 49151
-
-
 class RouterBlockInactive(
     ProductBlockModel,
     lifecycle=[SubscriptionLifecycle.INITIAL],
@@ -54,13 +36,7 @@ class RouterBlockInactive(
     router_lo_iso_address: str | None = None
     router_role: RouterRole | None = None
     router_site: SiteBlockInactive | None
-    vendor: RouterVendor | None = None
-
-
-def generate_fqdn(hostname: str, site_name: str, country_code: str) -> str:
-    """Generate an :term:`FQDN` from a hostname, site name, and a country code."""
-    oss = settings.load_oss_params()
-    return f"{hostname}.{site_name.lower()}.{country_code.lower()}{oss.IPAM.LO.domain_name}"
+    vendor: Vendor | None = None
 
 
 class RouterBlockProvisioning(RouterBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]):
@@ -74,7 +50,7 @@ class RouterBlockProvisioning(RouterBlockInactive, lifecycle=[SubscriptionLifecy
     router_lo_iso_address: str
     router_role: RouterRole
     router_site: SiteBlockProvisioning
-    vendor: RouterVendor
+    vendor: Vendor
 
 
 class RouterBlock(RouterBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
@@ -97,4 +73,4 @@ class RouterBlock(RouterBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTI
     #:  The :class:`Site` that this router resides in. Both physically and computationally.
     router_site: SiteBlock
     #:  The vendor of a router.
-    vendor: RouterVendor
+    vendor: Vendor
diff --git a/gso/products/product_blocks/super_pop_switch.py b/gso/products/product_blocks/super_pop_switch.py
new file mode 100644
index 0000000000000000000000000000000000000000..af2f2ba74c98cc41806842d9877e8b0168ec3748
--- /dev/null
+++ b/gso/products/product_blocks/super_pop_switch.py
@@ -0,0 +1,52 @@
+"""Product block for :class:`Super PoP Switch` products."""
+
+import ipaddress
+
+from orchestrator.domain.base import ProductBlockModel
+from orchestrator.types import SubscriptionLifecycle
+
+from gso.products.product_blocks.site import (
+    SiteBlock,
+    SiteBlockInactive,
+    SiteBlockProvisioning,
+)
+from gso.utils.shared_enums import PortNumber, Vendor
+
+
+class SuperPopSwitchBlockInactive(
+    ProductBlockModel,
+    lifecycle=[SubscriptionLifecycle.INITIAL],
+    product_block_name="SuperPopSwitchBlock",
+):
+    """A Super PoP switch that's being currently inactive. See :class:`SuperPopSwitchBlock`."""
+
+    super_pop_switch_fqdn: str | None = None
+    super_pop_switch_ts_port: PortNumber | None = None
+    super_pop_switch_mgmt_ipv4_address: ipaddress.IPv4Address | None = None
+    super_pop_switch_site: SiteBlockInactive | None
+    vendor: Vendor | None = None
+
+
+class SuperPopSwitchBlockProvisioning(SuperPopSwitchBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]):
+    """A Super PoP switch that's being provisioned. See :class:`SuperPopSwitchBlock`."""
+
+    super_pop_switch_fqdn: str | None = None
+    super_pop_switch_ts_port: PortNumber | None = None
+    super_pop_switch_mgmt_ipv4_address: ipaddress.IPv4Address | None = None
+    super_pop_switch_site: SiteBlockProvisioning | None
+    vendor: Vendor | None = None
+
+
+class SuperPopSwitchBlock(SuperPopSwitchBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
+    """A Super PoP switch that's currently deployed in the network."""
+
+    #:  Super PoP switch FQDN.
+    super_pop_switch_fqdn: str
+    #:  The port of the terminal server that this Super PoP switch is connected to. Used to offer out of band access.
+    super_pop_switch_ts_port: PortNumber
+    #:  The IPv4 management address of the Super PoP switch.
+    super_pop_switch_mgmt_ipv4_address: ipaddress.IPv4Address
+    #:  The :class:`Site` that this Super PoP switch resides in. Both physically and computationally.
+    super_pop_switch_site: SiteBlock
+    #:  The vendor of a Super PoP switch. Defaults to Juniper.
+    vendor: Vendor = Vendor.JUNIPER
diff --git a/gso/products/product_types/office_router.py b/gso/products/product_types/office_router.py
new file mode 100644
index 0000000000000000000000000000000000000000..6fff33e041edc35e1f333f66a01ac3d6f78fc286
--- /dev/null
+++ b/gso/products/product_types/office_router.py
@@ -0,0 +1,28 @@
+"""An office router product type."""
+
+from orchestrator.domain.base import SubscriptionModel
+from orchestrator.types import SubscriptionLifecycle
+
+from gso.products.product_blocks.office_router import (
+    OfficeRouterBlock,
+    OfficeRouterBlockInactive,
+    OfficeRouterBlockProvisioning,
+)
+
+
+class OfficeRouterInactive(SubscriptionModel, is_base=True):
+    """An inactive office router."""
+
+    office_router: OfficeRouterBlockInactive
+
+
+class OfficeRouterProvisioning(OfficeRouterInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]):
+    """An office router that is being provisioned."""
+
+    office_router: OfficeRouterBlockProvisioning
+
+
+class OfficeRouter(OfficeRouterProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
+    """An office router that is currently active."""
+
+    office_router: OfficeRouterBlock
diff --git a/gso/products/product_types/super_pop_switch.py b/gso/products/product_types/super_pop_switch.py
new file mode 100644
index 0000000000000000000000000000000000000000..e12b1a3d12793d372c47b5022a85307a7bf730ce
--- /dev/null
+++ b/gso/products/product_types/super_pop_switch.py
@@ -0,0 +1,28 @@
+"""A Super PoP switch product type."""
+
+from orchestrator.domain.base import SubscriptionModel
+from orchestrator.types import SubscriptionLifecycle
+
+from gso.products.product_blocks.super_pop_switch import (
+    SuperPopSwitchBlock,
+    SuperPopSwitchBlockInactive,
+    SuperPopSwitchBlockProvisioning,
+)
+
+
+class SuperPopSwitchInactive(SubscriptionModel, is_base=True):
+    """An inactive Super PoP switch."""
+
+    super_pop_switch: SuperPopSwitchBlockInactive
+
+
+class SuperPopSwitchProvisioning(SuperPopSwitchInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]):
+    """A Super PoP switch that is being provisioned."""
+
+    super_pop_switch: SuperPopSwitchBlockProvisioning
+
+
+class SuperPopSwitch(SuperPopSwitchProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
+    """A Super PoP switch that is currently active."""
+
+    super_pop_switch: SuperPopSwitchBlock
diff --git a/gso/services/infoblox.py b/gso/services/infoblox.py
index 6838ad9eada39ad5fe3999f0f28096e04216388a..8c23116a7ef8d4d3124cb89cb212e2a13dcfb9fc 100644
--- a/gso/services/infoblox.py
+++ b/gso/services/infoblox.py
@@ -65,7 +65,7 @@ def _allocate_network(
         for network in container.subnets(new_prefix=netmask):
             if objects.Network.search(conn, network=str(network)) is None:
                 created_net = objects.Network.create(
-                    conn, network=str(network), dns_view=dns_view, network_view=network_view, comment=comment
+                    conn, network=str(network), view=dns_view, network_view=network_view, comment=comment
                 )
                 if created_net.response != "Infoblox Object already Exists":
                     return ipaddress.ip_network(created_net.network)
@@ -202,7 +202,7 @@ def allocate_host(
                 name=hostname,
                 aliases=cname_aliases,
                 comment=comment,
-                dns_view=dns_view,
+                view=dns_view,
                 network_view=network_view,
             )
             created_v6 = ipaddress.IPv6Address(new_host.ipv6addr)
@@ -264,7 +264,7 @@ def create_host_by_ip(
 
     # This needs to be done in two steps, otherwise only one of the IP addresses is stored.
     objects.HostRecord.create(
-        conn, ip=ipv6_object, name=hostname, comment=comment, dns_view=dns_view, network_view=network_view
+        conn, ip=ipv6_object, name=hostname, comment=comment, view=dns_view, network_view=network_view
     )
     new_host = find_host_by_fqdn(hostname)
     new_host.ipv4addrs = [ipv4_object]
diff --git a/gso/services/subscriptions.py b/gso/services/subscriptions.py
index 9eb0583e6c74461b7155b7d94538ab1afad4a0e1..e89d9c25ad1cc52f68ae6ac3aea75a0542b92559 100644
--- a/gso/services/subscriptions.py
+++ b/gso/services/subscriptions.py
@@ -20,23 +20,23 @@ from orchestrator.types import SubscriptionLifecycle
 from pydantic_forms.types import UUIDstr
 
 from gso.products import ProductType
+from gso.products.product_types.site import Site
 
 SubscriptionType = dict[str, Any]
 
 
-def get_active_subscriptions(
+def get_subscriptions(
     product_type: str,
+    lifecycle: SubscriptionLifecycle,
     includes: list[str] | None = None,
     excludes: list[str] | None = None,
 ) -> list[SubscriptionType]:
     """Retrieve active subscriptions for a specific product type.
 
-    :param product_type: The type of the product for which to retrieve subscriptions.
-    :type product_type: str
-    :param includes: List of fields to be included in the returned Subscription objects.
-    :type includes: list[str]
-    :param excludes: List of fields to be excluded from the returned Subscription objects.
-    :type excludes: list[str]
+    :param str product_type: The type of the product for which to retrieve subscriptions.
+    :param SubscriptionLifecycle lifecycle: The lifecycle that the products must be in.
+    :param list[str] includes: List of fields to be included in the returned Subscription objects.
+    :param list[str] excludes: List of fields to be excluded from the returned Subscription objects.
 
     :return: A list of Subscription objects that match the query.
     :rtype: list[Subscription]
@@ -51,7 +51,7 @@ def get_active_subscriptions(
 
     query = SubscriptionTable.query.join(ProductTable).filter(
         ProductTable.product_type == product_type,
-        SubscriptionTable.status == SubscriptionLifecycle.ACTIVE,
+        SubscriptionTable.status == lifecycle,
     )
 
     results = query.with_entities(*dynamic_fields).all()
@@ -59,9 +59,7 @@ def get_active_subscriptions(
     return [dict(zip(includes, result, strict=True)) for result in results]
 
 
-def get_active_site_subscriptions(
-    includes: list[str] | None = None,
-) -> list[SubscriptionType]:
+def get_active_site_subscriptions(includes: list[str] | None = None) -> list[SubscriptionType]:
     """Retrieve active subscriptions specifically for sites.
 
     :param includes: The fields to be included in the returned Subscription objects.
@@ -70,12 +68,10 @@ def get_active_site_subscriptions(
     :return: A list of Subscription objects for sites.
     :rtype: list[Subscription]
     """
-    return get_active_subscriptions(product_type=ProductType.SITE, includes=includes)
+    return get_subscriptions(product_type=ProductType.SITE, lifecycle=SubscriptionLifecycle.ACTIVE, includes=includes)
 
 
-def get_active_router_subscriptions(
-    includes: list[str] | None = None,
-) -> list[SubscriptionType]:
+def get_active_router_subscriptions(includes: list[str] | None = None) -> list[SubscriptionType]:
     """Retrieve active subscriptions specifically for routers.
 
     :param includes: The fields to be included in the returned Subscription objects.
@@ -84,12 +80,19 @@ def get_active_router_subscriptions(
     :return: A list of Subscription objects for routers.
     :rtype: list[Subscription]
     """
-    return get_active_subscriptions(product_type="Router", includes=includes)
+    return get_subscriptions(product_type="Router", lifecycle=SubscriptionLifecycle.ACTIVE, includes=includes)
 
 
-def get_active_iptrunk_subscriptions(
-    includes: list[str] | None = None,
-) -> list[SubscriptionType]:
+def get_provisioning_router_subscriptions(includes: list[str] | None = None) -> list[SubscriptionType]:
+    """Retrieve provisioning subscriptions specifically for routers.
+
+    :param list[str] includes: The fields to be included in the returned Subscription objects.
+    :return list[Subscription]: A list of router Subscription objects.
+    """
+    return get_subscriptions(product_type="Router", lifecycle=SubscriptionLifecycle.PROVISIONING, includes=includes)
+
+
+def get_active_iptrunk_subscriptions(includes: list[str] | None = None) -> list[SubscriptionType]:
     """Retrieve active subscriptions specifically for IP trunks.
 
     :param includes: The fields to be included in the returned Subscription objects.
@@ -98,7 +101,7 @@ def get_active_iptrunk_subscriptions(
     :return: A list of Subscription objects for IP trunks.
     :rtype: list[Subscription]
     """
-    return get_active_subscriptions(product_type="Iptrunk", includes=includes)
+    return get_subscriptions(product_type="Iptrunk", lifecycle=SubscriptionLifecycle.ACTIVE, includes=includes)
 
 
 def get_active_trunks_that_terminate_on_router(subscription_id: UUIDstr) -> list[SubscriptionTable]:
@@ -175,3 +178,17 @@ def count_incomplete_validate_products() -> int:
 def get_insync_subscriptions() -> list[SubscriptionTable]:
     """Retrieve all subscriptions that are currently in sync."""
     return SubscriptionTable.query.join(ProductTable).filter(SubscriptionTable.insync.is_(True)).all()
+
+
+def get_site_by_name(site_name: str) -> Site:
+    """Get a site by its name.
+
+    :param site_name: The name of the site.
+    :type site_name: str
+    """
+    subscription = get_active_subscriptions_by_field_and_value("site_name", site_name)
+    if not subscription:
+        msg = f"Site with name {site_name} not found."
+        raise ValueError(msg)
+
+    return Site.from_subscription(subscription[0].subscription_id)
diff --git a/gso/translations/en-GB.json b/gso/translations/en-GB.json
index c8f8d2410f4b94719e127739b2a6e711e01e2efe..c62a6f69aa064bcefe19428e8179ffdbcd24bf2a 100644
--- a/gso/translations/en-GB.json
+++ b/gso/translations/en-GB.json
@@ -36,6 +36,7 @@
         }
     },
     "workflow": {
+        "activate_router": "Activate router",
         "confirm_info": "Please verify this form looks correct.",
         "deploy_twamp": "Deploy TWAMP",
         "migrate_iptrunk": "Migrate IP Trunk",
diff --git a/gso/utils/helpers.py b/gso/utils/helpers.py
index c43a80737f56e37e3288e78d89a0ab38dced94e4..6c30324ed0b81064bdc4c84e862f1a0ff671b9da 100644
--- a/gso/utils/helpers.py
+++ b/gso/utils/helpers.py
@@ -12,12 +12,13 @@ from pydantic import BaseModel, validator
 from pydantic.fields import ModelField
 from pydantic_forms.validators import Choice
 
+from gso import settings
 from gso.products.product_blocks.iptrunk import IptrunkInterfaceBlock
-from gso.products.product_blocks.router import RouterVendor
 from gso.products.product_blocks.site import SiteTier
 from gso.products.product_types.router import Router
 from gso.services.netbox_client import NetboxClient
 from gso.services.subscriptions import get_active_subscriptions_by_field_and_value
+from gso.utils.shared_enums import Vendor
 
 
 class LAGMember(BaseModel):
@@ -44,7 +45,7 @@ def available_interfaces_choices(router_id: UUID, speed: str) -> Choice | None:
     For Nokia routers, return a list of available interfaces.
     For Juniper routers, return a string.
     """
-    if get_router_vendor(router_id) != RouterVendor.NOKIA:
+    if get_router_vendor(router_id) != Vendor.NOKIA:
         return None
     interfaces = {
         interface["name"]: f"{interface['name']}  {interface['description']}"
@@ -63,7 +64,7 @@ def available_interfaces_choices_including_current_members(
     For Nokia routers, return a list of available interfaces.
     For Juniper routers, return a string.
     """
-    if get_router_vendor(router_id) != RouterVendor.NOKIA:
+    if get_router_vendor(router_id) != Vendor.NOKIA:
         return None
 
     available_interfaces = list(NetboxClient().get_available_interfaces(router_id, speed))
@@ -88,20 +89,20 @@ def available_lags_choices(router_id: UUID) -> Choice | None:
     For Nokia routers, return a list of available lags.
     For Juniper routers, return ``None``.
     """
-    if get_router_vendor(router_id) != RouterVendor.NOKIA:
+    if get_router_vendor(router_id) != Vendor.NOKIA:
         return None
     side_a_ae_iface_list = NetboxClient().get_available_lags(router_id)
     return Choice("ae iface", zip(side_a_ae_iface_list, side_a_ae_iface_list, strict=True))  # type: ignore[arg-type]
 
 
-def get_router_vendor(router_id: UUID) -> RouterVendor:
+def get_router_vendor(router_id: UUID) -> Vendor:
     """Retrieve the vendor of a router.
 
     :param router_id: The :term:`UUID` of the router.
     :type router_id: :class:`uuid.UUID`
 
     :return: The vendor of the router.
-    :rtype: RouterVendor:
+    :rtype: Vendor:
     """
     return Router.from_subscription(router_id).router.vendor
 
@@ -130,7 +131,7 @@ def validate_router_in_netbox(subscription_id: UUIDstr) -> UUIDstr:
     :rtype: :class:`UUIDstr`
     """
     router_type = Router.from_subscription(subscription_id)
-    if router_type.router.vendor == RouterVendor.NOKIA:
+    if router_type.router.vendor == Vendor.NOKIA:
         device = NetboxClient().get_device_by_name(router_type.router.router_fqdn)
         if not device:
             msg = "The selected router does not exist in Netbox."
@@ -259,7 +260,7 @@ def validate_interface_name_list(interface_name_list: list, vendor: str) -> list
                   exception.
     """
     # For Nokia nothing to do
-    if vendor == RouterVendor.NOKIA:
+    if vendor == Vendor.NOKIA:
         return interface_name_list
     pattern = re.compile(r"^(ge|et|xe)-[0-9]/[0-9]/[0-9]$")
     for interface in interface_name_list:
@@ -290,3 +291,9 @@ def validate_tt_number(tt_number: str) -> str:
         raise ValueError(err_msg)
 
     return tt_number
+
+
+def generate_fqdn(hostname: str, site_name: str, country_code: str) -> str:
+    """Generate an :term:`FQDN` from a hostname, site name, and a country code."""
+    oss = settings.load_oss_params()
+    return f"{hostname}.{site_name.lower()}.{country_code.lower()}{oss.IPAM.LO.domain_name}"
diff --git a/gso/utils/shared_enums.py b/gso/utils/shared_enums.py
new file mode 100644
index 0000000000000000000000000000000000000000..4861e9e134ecf113d74772a08282e7928829d19b
--- /dev/null
+++ b/gso/utils/shared_enums.py
@@ -0,0 +1,21 @@
+"""Shared choices for the different models."""
+
+from pydantic import ConstrainedInt
+from pydantic_forms.types import strEnum
+
+
+class Vendor(strEnum):
+    """Enumerator for the different product vendors that are supported."""
+
+    JUNIPER = "juniper"
+    NOKIA = "nokia"
+
+
+class PortNumber(ConstrainedInt):
+    """Constrained integer for valid port numbers.
+
+    The range from 49152 to 65535 is marked as ephemeral, and can therefore not be selected for permanent allocation.
+    """
+
+    gt = 0
+    le = 49151
diff --git a/gso/workflows/__init__.py b/gso/workflows/__init__.py
index 56f8a191946a79a786bc8764f70c03c77d9abd1b..d25088730e93388aadef15057e80d3eca8a93ce6 100644
--- a/gso/workflows/__init__.py
+++ b/gso/workflows/__init__.py
@@ -1,13 +1,27 @@
 """Initialisation class that imports all workflows into :term:`GSO`."""
 
+from orchestrator.services.subscriptions import WF_USABLE_MAP
 from orchestrator.workflows import LazyWorkflowInstance
 
+WF_USABLE_MAP.update(
+    {
+        "redeploy_base_config": ["provisioning", "active"],
+        "update_ibgp_mesh": ["provisioning", "active"],
+        "activate_router": ["provisioning"],
+        "deploy_twamp": ["provisioning", "active"],
+        "modify_trunk_interface": ["provisioning", "active"],
+        "activate_iptrunk": ["provisioning"],
+    }
+)
+
+LazyWorkflowInstance("gso.workflows.iptrunk.activate_iptrunk", "activate_iptrunk")
 LazyWorkflowInstance("gso.workflows.iptrunk.create_iptrunk", "create_iptrunk")
 LazyWorkflowInstance("gso.workflows.iptrunk.deploy_twamp", "deploy_twamp")
 LazyWorkflowInstance("gso.workflows.iptrunk.modify_isis_metric", "modify_isis_metric")
 LazyWorkflowInstance("gso.workflows.iptrunk.modify_trunk_interface", "modify_trunk_interface")
 LazyWorkflowInstance("gso.workflows.iptrunk.migrate_iptrunk", "migrate_iptrunk")
 LazyWorkflowInstance("gso.workflows.iptrunk.terminate_iptrunk", "terminate_iptrunk")
+LazyWorkflowInstance("gso.workflows.router.activate_router", "activate_router")
 LazyWorkflowInstance("gso.workflows.router.create_router", "create_router")
 LazyWorkflowInstance("gso.workflows.router.redeploy_base_config", "redeploy_base_config")
 LazyWorkflowInstance("gso.workflows.router.terminate_router", "terminate_router")
@@ -18,3 +32,5 @@ LazyWorkflowInstance("gso.workflows.site.terminate_site", "terminate_site")
 LazyWorkflowInstance("gso.workflows.tasks.import_site", "import_site")
 LazyWorkflowInstance("gso.workflows.tasks.import_router", "import_router")
 LazyWorkflowInstance("gso.workflows.tasks.import_iptrunk", "import_iptrunk")
+LazyWorkflowInstance("gso.workflows.tasks.import_super_pop_switch", "import_super_pop_switch")
+LazyWorkflowInstance("gso.workflows.tasks.import_office_router", "import_office_router")
diff --git a/gso/workflows/iptrunk/activate_iptrunk.py b/gso/workflows/iptrunk/activate_iptrunk.py
new file mode 100644
index 0000000000000000000000000000000000000000..f686a8cb7e3c825dceffeb876c644a37342ce3d8
--- /dev/null
+++ b/gso/workflows/iptrunk/activate_iptrunk.py
@@ -0,0 +1,59 @@
+"""Activate IP trunk takes a provisioning trunk to the active lifecycle state."""
+
+from orchestrator.config.assignee import Assignee
+from orchestrator.forms import FormPage
+from orchestrator.forms.validators import Label
+from orchestrator.targets import Target
+from orchestrator.types import FormGenerator, SubscriptionLifecycle, UUIDstr
+from orchestrator.workflow import StepList, done, init, inputstep, 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
+
+
+def _initial_input_form(subscription_id: UUIDstr) -> FormGenerator:
+    trunk = Iptrunk.from_subscription(subscription_id)
+
+    class ActivateTrunkForm(FormPage):
+        info_label: Label = "Start approval process for IP trunk activation."  # type:ignore[assignment]
+
+    user_input = yield ActivateTrunkForm
+
+    return user_input.dict() | {"subscription": trunk}
+
+
+@inputstep("Verify checklist completion", assignee=Assignee.SYSTEM)
+def verify_complete_checklist() -> FormGenerator:
+    """Show a form for the operator to input a link to the completed checklist."""
+
+    class VerifyCompleteForm(FormPage):
+        info_label: Label = "Verify that the checklist has been completed. Then continue this workflow."  # type: ignore[assignment]
+        checklist_url: str = ""
+
+    user_input = yield VerifyCompleteForm
+
+    return {"checklist_url": user_input.dict()["checklist_url"]}
+
+
+@workflow(
+    "Activate an IP Trunk",
+    initial_input_form=wrap_modify_initial_input_form(_initial_input_form),
+    target=Target.MODIFY,
+)
+def activate_iptrunk() -> StepList:
+    """Move an IP Trunk from a ``PROVISIONING`` state to an ``ACTIVE`` state.
+
+    * Send email notifications to different teams.
+    * Wait for approval to be given.
+    * Update the subscription lifecycle state to ``ACTIVE``.
+    """
+    return (
+        init
+        >> store_process_subscription(Target.MODIFY)
+        >> unsync
+        >> verify_complete_checklist
+        >> set_status(SubscriptionLifecycle.ACTIVE)
+        >> resync
+        >> done
+    )
diff --git a/gso/workflows/iptrunk/create_iptrunk.py b/gso/workflows/iptrunk/create_iptrunk.py
index db6f91ce99d706d1c97d59bbda15785532f36d50..e9e304d224c5a9c36314a21050bf878846c8ef70 100644
--- a/gso/workflows/iptrunk/create_iptrunk.py
+++ b/gso/workflows/iptrunk/create_iptrunk.py
@@ -21,7 +21,6 @@ from gso.products.product_blocks.iptrunk import (
     IptrunkType,
     PhyPortCapacity,
 )
-from gso.products.product_blocks.router import RouterVendor
 from gso.products.product_types.iptrunk import IptrunkInactive
 from gso.products.product_types.router import Router
 from gso.services import infoblox, subscriptions
@@ -38,12 +37,16 @@ from gso.utils.helpers import (
     validate_router_in_netbox,
     validate_tt_number,
 )
+from gso.utils.shared_enums import Vendor
 
 
 def initial_input_form_generator(product_name: str) -> FormGenerator:
     """Gather input from the user in three steps. General information, and information on both sides of the trunk."""
     routers = {}
-    for router in subscriptions.get_active_router_subscriptions(includes=["subscription_id", "description"]):
+    for router in subscriptions.get_active_router_subscriptions(
+        includes=["subscription_id", "description"]
+    ) + subscriptions.get_provisioning_router_subscriptions(includes=["subscription_id", "description"]):
+        #  Add both provisioning and active routers, since trunks are required for promoting a router to active.
         routers[str(router["subscription_id"])] = router["description"]
 
     class CreateIptrunkForm(FormPage):
@@ -82,7 +85,7 @@ def initial_input_form_generator(product_name: str) -> FormGenerator:
     class JuniperAeMembers(UniqueConstrainedList[LAGMember]):
         min_items = initial_user_input.iptrunk_minimum_links
 
-    if get_router_vendor(router_a) == RouterVendor.NOKIA:
+    if get_router_vendor(router_a) == Vendor.NOKIA:
 
         class NokiaLAGMemberA(LAGMember):
             interface_name: available_interfaces_choices(  # type: ignore[valid-type]
@@ -130,7 +133,7 @@ def initial_input_form_generator(product_name: str) -> FormGenerator:
     user_input_router_side_b = yield SelectRouterSideB
     router_b = user_input_router_side_b.side_b_node_id.name
 
-    if get_router_vendor(router_b) == RouterVendor.NOKIA:
+    if get_router_vendor(router_b) == Vendor.NOKIA:
 
         class NokiaLAGMemberB(LAGMember):
             interface_name: available_interfaces_choices(  # type: ignore[valid-type]
@@ -400,7 +403,7 @@ def reserve_interfaces_in_netbox(subscription: IptrunkInactive) -> State:
     """Create the :term:`LAG` interfaces in NetBox and attach the lag interfaces to the physical interfaces."""
     nbclient = NetboxClient()
     for trunk_side in subscription.iptrunk.iptrunk_sides:
-        if get_router_vendor(trunk_side.iptrunk_side_node.owner_subscription_id) == RouterVendor.NOKIA:
+        if get_router_vendor(trunk_side.iptrunk_side_node.owner_subscription_id) == Vendor.NOKIA:
             # Create :term:`LAG` interfaces
             lag_interface: Interfaces = nbclient.create_interface(
                 iface_name=trunk_side.iptrunk_side_ae_iface,
@@ -469,8 +472,8 @@ def create_iptrunk() -> StepList:
     * Allocate the interfaces in Netbox
     * Set the subscription to active in the database
     """
-    side_a_is_nokia = conditional(lambda state: get_router_vendor(state["side_a_node_id"]) == RouterVendor.NOKIA)
-    side_b_is_nokia = conditional(lambda state: get_router_vendor(state["side_b_node_id"]) == RouterVendor.NOKIA)
+    side_a_is_nokia = conditional(lambda state: get_router_vendor(state["side_a_node_id"]) == Vendor.NOKIA)
+    side_b_is_nokia = conditional(lambda state: get_router_vendor(state["side_b_node_id"]) == Vendor.NOKIA)
 
     return (
         init
diff --git a/gso/workflows/iptrunk/migrate_iptrunk.py b/gso/workflows/iptrunk/migrate_iptrunk.py
index 71729149bf86db868b0bfb477565120ff8e4d98d..5f1240c56e74c0b16fe76bec4a72b962fbbec7c3 100644
--- a/gso/workflows/iptrunk/migrate_iptrunk.py
+++ b/gso/workflows/iptrunk/migrate_iptrunk.py
@@ -25,7 +25,6 @@ from pydantic_forms.core import ReadOnlyField
 from pynetbox.models.dcim import Interfaces
 
 from gso.products.product_blocks.iptrunk import IptrunkInterfaceBlock
-from gso.products.product_blocks.router import RouterVendor
 from gso.products.product_types.iptrunk import Iptrunk
 from gso.products.product_types.router import Router
 from gso.services import infoblox
@@ -41,6 +40,7 @@ from gso.utils.helpers import (
     validate_interface_name_list,
     validate_tt_number,
 )
+from gso.utils.shared_enums import Vendor
 from gso.utils.workflow_steps import set_isis_to_90000
 
 
@@ -109,7 +109,7 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
     new_router = new_side_iptrunk_router_input.new_node
     side_a_ae_iface = available_lags_choices(new_router) or str
 
-    new_side_is_nokia = get_router_vendor(new_router) == RouterVendor.NOKIA
+    new_side_is_nokia = get_router_vendor(new_router) == Vendor.NOKIA
     if new_side_is_nokia:
 
         class NokiaLAGMember(LAGMember):
@@ -155,7 +155,7 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
 
         @validator("new_lag_interface", allow_reuse=True, pre=True, always=True)
         def lag_interface_proper_name(cls, new_lag_interface: str) -> str:
-            if get_router_vendor(new_router) == RouterVendor.JUNIPER:
+            if get_router_vendor(new_router) == Vendor.JUNIPER:
                 juniper_lag_re = re.compile("^ae\\d{1,2}$")
                 if not juniper_lag_re.match(new_lag_interface):
                     msg = "Invalid LAG name, please try again."
@@ -643,10 +643,10 @@ def migrate_iptrunk() -> StepList:
     * Update the subscription model in the database
     * Update the reserved interfaces in Netbox
     """
-    new_side_is_nokia = conditional(lambda state: get_router_vendor(state["new_node"]) == RouterVendor.NOKIA)
+    new_side_is_nokia = conditional(lambda state: get_router_vendor(state["new_node"]) == Vendor.NOKIA)
     old_side_is_nokia = conditional(
         lambda state: get_router_vendor(state["old_side_data"]["iptrunk_side_node"]["owner_subscription_id"])
-        == RouterVendor.NOKIA
+        == Vendor.NOKIA
     )
     should_restore_isis_metric = conditional(lambda state: state["restore_isis_metric"])
 
diff --git a/gso/workflows/iptrunk/modify_trunk_interface.py b/gso/workflows/iptrunk/modify_trunk_interface.py
index 1dcda4fd5ceb2f3b2a4114533ff313a76823ac53..d4e6edb19160c77a429f5a7f307b5897ec5df055 100644
--- a/gso/workflows/iptrunk/modify_trunk_interface.py
+++ b/gso/workflows/iptrunk/modify_trunk_interface.py
@@ -21,7 +21,6 @@ from gso.products.product_blocks.iptrunk import (
     IptrunkType,
     PhyPortCapacity,
 )
-from gso.products.product_blocks.router import RouterVendor
 from gso.products.product_types.iptrunk import Iptrunk
 from gso.services.netbox_client import NetboxClient
 from gso.services.provisioning_proxy import execute_playbook, pp_interaction
@@ -34,6 +33,7 @@ from gso.utils.helpers import (
     validate_iptrunk_unique_interface,
     validate_tt_number,
 )
+from gso.utils.shared_enums import Vendor
 
 
 def initialize_ae_members(subscription: Iptrunk, initial_user_input: dict, side_index: int) -> type[LAGMember]:
@@ -41,7 +41,7 @@ def initialize_ae_members(subscription: Iptrunk, initial_user_input: dict, side_
     router = subscription.iptrunk.iptrunk_sides[side_index].iptrunk_side_node
     router_vendor = get_router_vendor(router.owner_subscription_id)
     iptrunk_minimum_link = initial_user_input["iptrunk_minimum_links"]
-    if router_vendor == RouterVendor.NOKIA:
+    if router_vendor == Vendor.NOKIA:
         iptrunk_speed = initial_user_input["iptrunk_speed"]
 
         class NokiaLAGMember(LAGMember):
@@ -386,13 +386,13 @@ def modify_trunk_interface() -> StepList:
         lambda state: get_router_vendor(
             state["subscription"]["iptrunk"]["iptrunk_sides"][0]["iptrunk_side_node"]["owner_subscription_id"]
         )
-        == RouterVendor.NOKIA
+        == Vendor.NOKIA
     )
     side_b_is_nokia = conditional(
         lambda state: get_router_vendor(
             state["subscription"]["iptrunk"]["iptrunk_sides"][1]["iptrunk_side_node"]["owner_subscription_id"]
         )
-        == RouterVendor.NOKIA
+        == Vendor.NOKIA
     )
     return (
         init
diff --git a/gso/workflows/iptrunk/terminate_iptrunk.py b/gso/workflows/iptrunk/terminate_iptrunk.py
index 33ee86fde6129a05278d8985a3959e2ef945c067..330c90c3fddb35253180b8668d61947f484ce9b7 100644
--- a/gso/workflows/iptrunk/terminate_iptrunk.py
+++ b/gso/workflows/iptrunk/terminate_iptrunk.py
@@ -19,12 +19,12 @@ from orchestrator.workflows.utils import wrap_modify_initial_input_form
 from pydantic import validator
 
 from gso.products.product_blocks.iptrunk import IptrunkSideBlock
-from gso.products.product_blocks.router import RouterVendor
 from gso.products.product_types.iptrunk import Iptrunk
 from gso.services import infoblox
 from gso.services.netbox_client import NetboxClient
 from gso.services.provisioning_proxy import execute_playbook, pp_interaction
 from gso.utils.helpers import get_router_vendor, validate_tt_number
+from gso.utils.shared_enums import Vendor
 from gso.utils.workflow_steps import set_isis_to_90000
 
 
@@ -159,14 +159,14 @@ def terminate_iptrunk() -> StepList:
         and get_router_vendor(
             state["subscription"]["iptrunk"]["iptrunk_sides"][0]["iptrunk_side_node"]["owner_subscription_id"]
         )
-        == RouterVendor.NOKIA
+        == Vendor.NOKIA
     )
     side_b_is_nokia = conditional(
         lambda state: state["clean_up_netbox"]
         and get_router_vendor(
             state["subscription"]["iptrunk"]["iptrunk_sides"][1]["iptrunk_side_node"]["owner_subscription_id"]
         )
-        == RouterVendor.NOKIA
+        == Vendor.NOKIA
     )
 
     config_steps = (
diff --git a/gso/workflows/router/activate_router.py b/gso/workflows/router/activate_router.py
new file mode 100644
index 0000000000000000000000000000000000000000..4de880f4e2b8a9cc13b7c1c80315fef634e577c9
--- /dev/null
+++ b/gso/workflows/router/activate_router.py
@@ -0,0 +1,59 @@
+"""Activate router takes a provisioning router to the active lifecycle state."""
+
+from orchestrator.config.assignee import Assignee
+from orchestrator.forms import FormPage
+from orchestrator.forms.validators import Label
+from orchestrator.targets import Target
+from orchestrator.types import FormGenerator, SubscriptionLifecycle, UUIDstr
+from orchestrator.workflow import StepList, done, init, inputstep, 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.router import Router
+
+
+def _initial_input_form(subscription_id: UUIDstr) -> FormGenerator:
+    router = Router.from_subscription(subscription_id)
+
+    class ActivateRouterForm(FormPage):
+        info_label: Label = "Start approval process for router activation."  # type:ignore[assignment]
+
+    user_input = yield ActivateRouterForm
+
+    return user_input.dict() | {"subscription": router}
+
+
+@inputstep("Verify checklist completion", assignee=Assignee.SYSTEM)
+def verify_complete_checklist() -> FormGenerator:
+    """Show a form for the operator to input a link to the completed checklist."""
+
+    class VerifyCompleteForm(FormPage):
+        info_label: Label = "Verify that the checklist has been completed. Then continue this workflow."  # type: ignore[assignment]
+        checklist_url: str = ""
+
+    user_input = yield VerifyCompleteForm
+
+    return {"checklist_url": user_input.dict()["checklist_url"]}
+
+
+@workflow(
+    "Activate a router",
+    initial_input_form=wrap_modify_initial_input_form(_initial_input_form),
+    target=Target.MODIFY,
+)
+def activate_router() -> StepList:
+    """Move a router from a ``PROVISIONING`` state to an ``ACTIVE`` state.
+
+    * Send email notifications to different teams.
+    * Wait for approval to be given.
+    * Update the subscription lifecycle state to ``ACTIVE``.
+    """
+    return (
+        init
+        >> store_process_subscription(Target.MODIFY)
+        >> unsync
+        >> verify_complete_checklist
+        >> set_status(SubscriptionLifecycle.ACTIVE)
+        >> resync
+        >> done
+    )
diff --git a/gso/workflows/router/create_router.py b/gso/workflows/router/create_router.py
index 29a60dbb87d73f68f695710d3b746a8e8fa4c198..f33dcec56f8314c2ebd188f7e5d9df6865caa032 100644
--- a/gso/workflows/router/create_router.py
+++ b/gso/workflows/router/create_router.py
@@ -13,19 +13,15 @@ from orchestrator.workflows.utils import wrap_create_initial_input_form
 from pydantic import validator
 from pydantic_forms.core import ReadOnlyField
 
-from gso.products.product_blocks.router import (
-    PortNumber,
-    RouterRole,
-    RouterVendor,
-    generate_fqdn,
-)
+from gso.products.product_blocks.router import RouterRole
 from gso.products.product_types.router import RouterInactive
 from gso.products.product_types.site import Site
 from gso.services import infoblox, subscriptions
 from gso.services.crm import get_customer_by_name
 from gso.services.netbox_client import NetboxClient
 from gso.services.provisioning_proxy import pp_interaction
-from gso.utils.helpers import iso_from_ipv4
+from gso.utils.helpers import generate_fqdn, iso_from_ipv4
+from gso.utils.shared_enums import PortNumber, Vendor
 from gso.utils.workflow_steps import deploy_base_config_dry, deploy_base_config_real, run_checks_after_base_config
 
 
@@ -47,7 +43,7 @@ def initial_input_form_generator(product_name: str) -> FormGenerator:
 
         tt_number: str
         customer: str = ReadOnlyField("GÉANT")
-        vendor: RouterVendor
+        vendor: Vendor
         router_site: _site_selector()  # type: ignore[valid-type]
         hostname: str
         ts_port: PortNumber
@@ -91,7 +87,7 @@ def initialize_subscription(
     ts_port: PortNumber,
     router_site: str,
     router_role: RouterRole,
-    vendor: RouterVendor,
+    vendor: Vendor,
 ) -> State:
     """Initialise the subscription object in the service database."""
     subscription.router.router_ts_port = ts_port
@@ -221,7 +217,7 @@ def create_router() -> StepList:
     * Validate :term:`IPAM` resources
     * Create a new device in Netbox
     """
-    router_is_nokia = conditional(lambda state: state["vendor"] == RouterVendor.NOKIA)
+    router_is_nokia = conditional(lambda state: state["vendor"] == Vendor.NOKIA)
 
     return (
         init
diff --git a/gso/workflows/router/terminate_router.py b/gso/workflows/router/terminate_router.py
index 1a83943a76852689ed931da4f22ea7f172022cfd..20ac88dd053c7d231cdf8445d186f02ee81fa2f8 100644
--- a/gso/workflows/router/terminate_router.py
+++ b/gso/workflows/router/terminate_router.py
@@ -18,11 +18,11 @@ from orchestrator.workflows.steps import (
 )
 from orchestrator.workflows.utils import wrap_modify_initial_input_form
 
-from gso.products.product_blocks.router import RouterVendor
 from gso.products.product_types.router import Router
 from gso.services import infoblox
 from gso.services.netbox_client import NetboxClient
 from gso.services.provisioning_proxy import execute_playbook, pp_interaction
+from gso.utils.shared_enums import Vendor
 
 logger = logging.getLogger(__name__)
 
@@ -41,7 +41,7 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
         clean_up_ipam: bool = True
 
     user_input = yield TerminateForm
-    return user_input.dict() | {"router_is_nokia": router.router.vendor == RouterVendor.NOKIA}
+    return user_input.dict() | {"router_is_nokia": router.router.vendor == Vendor.NOKIA}
 
 
 @step("Deprovision loopback IPs from IPAM")
diff --git a/gso/workflows/tasks/import_office_router.py b/gso/workflows/tasks/import_office_router.py
new file mode 100644
index 0000000000000000000000000000000000000000..9b8de86fa099e328971b2d052098839f6f66ff1b
--- /dev/null
+++ b/gso/workflows/tasks/import_office_router.py
@@ -0,0 +1,94 @@
+"""A creation workflow that adds existing office routers to the coreDB."""
+
+import ipaddress
+
+from orchestrator import workflow
+from orchestrator.forms import FormPage
+from orchestrator.targets import Target
+from orchestrator.types import FormGenerator, State, SubscriptionLifecycle
+from orchestrator.workflow import StepList, done, init, step
+from orchestrator.workflows.steps import resync, set_status, store_process_subscription
+
+from gso.products import ProductType
+from gso.products.product_types import office_router
+from gso.products.product_types.office_router import OfficeRouterInactive
+from gso.services import subscriptions
+from gso.services.crm import get_customer_by_name
+from gso.services.subscriptions import get_site_by_name
+from gso.utils.shared_enums import PortNumber, Vendor
+
+
+@step("Create subscription")
+def create_subscription(customer: str) -> State:
+    """Create a new subscription object."""
+    customer_id = get_customer_by_name(customer)["id"]
+    product_id = subscriptions.get_product_id_by_name(ProductType.OFFICE_ROUTER)
+    subscription = OfficeRouterInactive.from_product_id(product_id, customer_id)
+
+    return {
+        "subscription": subscription,
+        "subscription_id": subscription.subscription_id,
+    }
+
+
+def initial_input_form_generator() -> FormGenerator:
+    """Generate a form that is filled in using information passed through the :term:`API` endpoint."""
+
+    class ImportOfficeRouter(FormPage):
+        class Config:
+            title = "Import an office router"
+
+        customer: str
+        office_router_site: str
+        office_router_fqdn: str
+        office_router_ts_port: PortNumber
+        office_router_lo_ipv4_address: ipaddress.IPv4Address
+        office_router_lo_ipv6_address: ipaddress.IPv6Address
+
+    user_input = yield ImportOfficeRouter
+
+    return user_input.dict()
+
+
+@step("Initialize subscription")
+def initialize_subscription(
+    subscription: OfficeRouterInactive,
+    office_router_fqdn: str,
+    office_router_ts_port: PortNumber,
+    office_router_site: str,
+    office_router_lo_ipv4_address: ipaddress.IPv4Address | None = None,
+    office_router_lo_ipv6_address: ipaddress.IPv6Address | None = None,
+) -> State:
+    """Initialise the office router subscription using input data."""
+    subscription.office_router.office_router_ts_port = office_router_ts_port
+    site_obj = get_site_by_name(office_router_site).site
+    subscription.office_router.office_router_site = site_obj
+    subscription.office_router.office_router_fqdn = office_router_fqdn
+    subscription.description = f"Office router {office_router_fqdn}"
+    subscription.office_router.office_router_lo_ipv4_address = office_router_lo_ipv4_address
+    subscription.office_router.office_router_lo_ipv6_address = office_router_lo_ipv6_address
+    subscription.office_router.vendor = Vendor.JUNIPER
+
+    subscription = office_router.OfficeRouterProvisioning.from_other_lifecycle(
+        subscription, SubscriptionLifecycle.PROVISIONING
+    )
+
+    return {"subscription": subscription}
+
+
+@workflow(
+    "Import office router",
+    initial_input_form=initial_input_form_generator,
+    target=Target.CREATE,
+)
+def import_office_router() -> StepList:
+    """Import an office router without provisioning it."""
+    return (
+        init
+        >> create_subscription
+        >> store_process_subscription(Target.CREATE)
+        >> initialize_subscription
+        >> set_status(SubscriptionLifecycle.ACTIVE)
+        >> resync
+        >> done
+    )
diff --git a/gso/workflows/tasks/import_router.py b/gso/workflows/tasks/import_router.py
index 878b538a40b0cf326380863d7008ef44b8acaab3..8346b973d51bf959c3397f5df73e0285a9474d4d 100644
--- a/gso/workflows/tasks/import_router.py
+++ b/gso/workflows/tasks/import_router.py
@@ -11,26 +11,14 @@ from orchestrator.workflows.steps import resync, set_status, store_process_subsc
 
 from gso.products import ProductType
 from gso.products.product_blocks import router as router_pb
-from gso.products.product_blocks.router import PortNumber, RouterRole, RouterVendor, generate_fqdn
+from gso.products.product_blocks.router import RouterRole
 from gso.products.product_types import router
 from gso.products.product_types.router import RouterInactive
-from gso.products.product_types.site import Site
 from gso.services import subscriptions
 from gso.services.crm import get_customer_by_name
-
-
-def _get_site_by_name(site_name: str) -> Site:
-    """Get a site by its name.
-
-    :param site_name: The name of the site.
-    :type site_name: str
-    """
-    subscription = subscriptions.get_active_subscriptions_by_field_and_value("site_name", site_name)
-    if not subscription:
-        msg = f"Site with name {site_name} not found."
-        raise ValueError(msg)
-
-    return Site.from_subscription(subscription[0].subscription_id)
+from gso.services.subscriptions import get_site_by_name
+from gso.utils.helpers import generate_fqdn
+from gso.utils.shared_enums import PortNumber, Vendor
 
 
 @step("Create subscription")
@@ -57,7 +45,7 @@ def initial_input_form_generator() -> FormGenerator:
         router_site: str
         hostname: str
         ts_port: int
-        router_vendor: RouterVendor
+        router_vendor: Vendor
         router_role: RouterRole
         router_lo_ipv4_address: ipaddress.IPv4Address
         router_lo_ipv6_address: ipaddress.IPv6Address
@@ -75,14 +63,14 @@ def initialize_subscription(
     ts_port: PortNumber,
     router_site: str,
     router_role: router_pb.RouterRole,
-    router_vendor: RouterVendor,
+    router_vendor: Vendor,
     router_lo_ipv4_address: ipaddress.IPv4Address | None = None,
     router_lo_ipv6_address: ipaddress.IPv6Address | None = None,
     router_lo_iso_address: str | None = None,
 ) -> State:
     """Initialise the router subscription using input data."""
     subscription.router.router_ts_port = ts_port
-    router_site_obj = _get_site_by_name(router_site).site
+    router_site_obj = get_site_by_name(router_site).site
     subscription.router.router_site = router_site_obj
     fqdn = generate_fqdn(hostname, router_site_obj.site_name, router_site_obj.site_country_code)
     subscription.router.router_fqdn = fqdn
diff --git a/gso/workflows/tasks/import_super_pop_switch.py b/gso/workflows/tasks/import_super_pop_switch.py
new file mode 100644
index 0000000000000000000000000000000000000000..5d7aa4ab214f630e4db5fa206fc8650681016df1
--- /dev/null
+++ b/gso/workflows/tasks/import_super_pop_switch.py
@@ -0,0 +1,93 @@
+"""A creation workflow that adds existing Super PoP switches to the coreDB."""
+
+import ipaddress
+
+from orchestrator import workflow
+from orchestrator.forms import FormPage
+from orchestrator.targets import Target
+from orchestrator.types import FormGenerator, State, SubscriptionLifecycle
+from orchestrator.workflow import StepList, done, init, step
+from orchestrator.workflows.steps import resync, set_status, store_process_subscription
+
+from gso.products import ProductType
+from gso.products.product_types import super_pop_switch
+from gso.products.product_types.super_pop_switch import SuperPopSwitchInactive
+from gso.services import subscriptions
+from gso.services.crm import get_customer_by_name
+from gso.services.subscriptions import get_site_by_name
+from gso.utils.helpers import generate_fqdn
+from gso.utils.shared_enums import PortNumber, Vendor
+
+
+@step("Create subscription")
+def create_subscription(customer: str) -> State:
+    """Create a new subscription object."""
+    customer_id = get_customer_by_name(customer)["id"]
+    product_id = subscriptions.get_product_id_by_name(ProductType.SUPER_POP_SWITCH)
+    subscription = SuperPopSwitchInactive.from_product_id(product_id, customer_id)
+
+    return {
+        "subscription": subscription,
+        "subscription_id": subscription.subscription_id,
+    }
+
+
+def initial_input_form_generator() -> FormGenerator:
+    """Generate a form that is filled in using information passed through the :term:`API` endpoint."""
+
+    class ImportSuperPopSwitch(FormPage):
+        class Config:
+            title = "Import a Super PoP switch"
+
+        customer: str
+        super_pop_switch_site: str
+        hostname: str
+        super_pop_switch_ts_port: PortNumber
+        super_pop_switch_mgmt_ipv4_address: ipaddress.IPv4Address
+
+    user_input = yield ImportSuperPopSwitch
+
+    return user_input.dict()
+
+
+@step("Initialize subscription")
+def initialize_subscription(
+    subscription: SuperPopSwitchInactive,
+    hostname: str,
+    super_pop_switch_ts_port: PortNumber,
+    super_pop_switch_site: str,
+    super_pop_switch_mgmt_ipv4_address: ipaddress.IPv4Address | None = None,
+) -> State:
+    """Initialise the Super PoP switch subscription using input data."""
+    subscription.super_pop_switch.super_pop_switch_ts_port = super_pop_switch_ts_port
+    site_obj = get_site_by_name(super_pop_switch_site).site
+    subscription.super_pop_switch.super_pop_switch_site = site_obj
+    fqdn = generate_fqdn(hostname, site_obj.site_name, site_obj.site_country_code)
+    subscription.super_pop_switch.super_pop_switch_fqdn = fqdn
+    subscription.description = f"Super PoP switch {fqdn}"
+    subscription.super_pop_switch.super_pop_switch_mgmt_ipv4_address = super_pop_switch_mgmt_ipv4_address
+    subscription.super_pop_switch.vendor = Vendor.JUNIPER
+
+    subscription = super_pop_switch.SuperPopSwitchProvisioning.from_other_lifecycle(
+        subscription, SubscriptionLifecycle.PROVISIONING
+    )
+
+    return {"subscription": subscription}
+
+
+@workflow(
+    "Import Super PoP switch",
+    initial_input_form=initial_input_form_generator,
+    target=Target.CREATE,
+)
+def import_super_pop_switch() -> StepList:
+    """Import a Super PoP switch without provisioning it."""
+    return (
+        init
+        >> create_subscription
+        >> store_process_subscription(Target.CREATE)
+        >> initialize_subscription
+        >> set_status(SubscriptionLifecycle.ACTIVE)
+        >> resync
+        >> done
+    )
diff --git a/setup.py b/setup.py
index cf34f158746312524a27348396cf48a46812080d..5be922344ae8716f0bbbfcd91f73e60b3912fbee 100644
--- a/setup.py
+++ b/setup.py
@@ -2,7 +2,7 @@ from setuptools import find_packages, setup
 
 setup(
     name="geant-service-orchestrator",
-    version="0.7",
+    version="0.8",
     author="GÉANT",
     author_email="swd@geant.org",
     description="GÉANT Service Orchestrator",
diff --git a/test/api/test_imports.py b/test/api/test_imports.py
index d823bc367d39c3072faf1c307d4c8d796151de22..b4d58f86cef94b2992973cc487fcede995819f14 100644
--- a/test/api/test_imports.py
+++ b/test/api/test_imports.py
@@ -6,13 +6,16 @@ from orchestrator.db import SubscriptionTable
 from orchestrator.services import subscriptions
 
 from gso.products.product_blocks.iptrunk import IptrunkType, PhyPortCapacity
-from gso.products.product_blocks.router import RouterRole, RouterVendor
+from gso.products.product_blocks.router import RouterRole
 from gso.products.product_blocks.site import SiteTier
 from gso.utils.helpers import iso_from_ipv4
+from gso.utils.shared_enums import Vendor
 
 SITE_IMPORT_ENDPOINT = "/api/v1/imports/sites"
 ROUTER_IMPORT_ENDPOINT = "/api/v1/imports/routers"
 IPTRUNK_IMPORT_API_URL = "/api/v1/imports/iptrunks"
+SUPER_POP_SWITCH_IMPORT_API_URL = "/api/v1/imports/super-pop-switches"
+OFFICE_ROUTER_IMPORT_API_URL = "/api/v1/imports/office-routers"
 
 
 @pytest.fixture()
@@ -114,7 +117,7 @@ def router_data(faker, site_data):
     return {
         "hostname": "127.0.0.1",
         "router_role": RouterRole.PE,
-        "router_vendor": RouterVendor.JUNIPER,
+        "router_vendor": Vendor.JUNIPER,
         "router_site": site_data["site_name"],
         "ts_port": 1234,
         "customer": "GÉANT",
@@ -124,6 +127,30 @@ def router_data(faker, site_data):
     }
 
 
+@pytest.fixture()
+def super_pop_switch_data(faker, site_data):
+    mock_ipv4 = faker.ipv4()
+    return {
+        "hostname": "127.0.0.1",
+        "super_pop_switch_site": site_data["site_name"],
+        "super_pop_switch_ts_port": 1234,
+        "customer": "GÉANT",
+        "super_pop_switch_mgmt_ipv4_address": mock_ipv4,
+    }
+
+
+@pytest.fixture()
+def office_router_data(faker, site_data):
+    return {
+        "office_router_fqdn": "127.0.0.1",
+        "office_router_site": site_data["site_name"],
+        "office_router_ts_port": 1234,
+        "customer": "GÉANT",
+        "office_router_lo_ipv4_address": faker.ipv4(),
+        "office_router_lo_ipv6_address": faker.ipv6(),
+    }
+
+
 def test_import_site_endpoint(test_client, site_data):
     assert SubscriptionTable.query.all() == []
     # Post data to the endpoint
@@ -337,3 +364,59 @@ def test_import_iptrunk_fails_on_side_a_and_b_members_mismatch(
             },
         ],
     }
+
+
+def test_import_super_pop_switch_endpoint(test_client, site_data, super_pop_switch_data):
+    response = test_client.post(SITE_IMPORT_ENDPOINT, json=site_data)
+    assert response.status_code == 201
+    assert SubscriptionTable.query.count() == 1
+
+    response = test_client.post(SUPER_POP_SWITCH_IMPORT_API_URL, json=super_pop_switch_data)
+    assert response.status_code == 201
+    assert SubscriptionTable.query.count() == 2
+
+
+def test_import_super_pop_switch_endpoint_with_invalid_data(test_client, site_data, super_pop_switch_data):
+    response = test_client.post(SITE_IMPORT_ENDPOINT, json=site_data)
+    assert response.status_code == 201
+    assert SubscriptionTable.query.count() == 1
+
+    # invalid data, missing hostname and invalid mgmt_ipv4_address
+    super_pop_switch_data.pop("hostname")
+    super_pop_switch_data["super_pop_switch_mgmt_ipv4_address"] = "invalid"
+    response = test_client.post(SUPER_POP_SWITCH_IMPORT_API_URL, json=super_pop_switch_data)
+    assert response.status_code == 422
+    assert SubscriptionTable.query.count() == 1
+    response = response.json()
+    assert response["detail"][0]["loc"] == ["body", "hostname"]
+    assert response["detail"][0]["msg"] == "field required"
+    assert response["detail"][1]["loc"] == ["body", "super_pop_switch_mgmt_ipv4_address"]
+    assert response["detail"][1]["msg"] == "value is not a valid IPv4 address"
+
+
+def test_import_office_router_endpoint(test_client, site_data, office_router_data):
+    response = test_client.post(SITE_IMPORT_ENDPOINT, json=site_data)
+    assert response.status_code == 201
+    assert SubscriptionTable.query.count() == 1
+
+    response = test_client.post(OFFICE_ROUTER_IMPORT_API_URL, json=office_router_data)
+    assert response.status_code == 201
+    assert SubscriptionTable.query.count() == 2
+
+
+def test_import_office_router_endpoint_with_invalid_data(test_client, site_data, office_router_data):
+    response = test_client.post(SITE_IMPORT_ENDPOINT, json=site_data)
+    assert response.status_code == 201
+    assert SubscriptionTable.query.count() == 1
+
+    # invalid data, missing FQDN and invalid lo_ipv6_address
+    office_router_data.pop("office_router_fqdn")
+    office_router_data["office_router_lo_ipv6_address"] = "invalid"
+    response = test_client.post(OFFICE_ROUTER_IMPORT_API_URL, json=office_router_data)
+    assert response.status_code == 422
+    assert SubscriptionTable.query.count() == 1
+    response = response.json()
+    assert response["detail"][0]["loc"] == ["body", "office_router_fqdn"]
+    assert response["detail"][0]["msg"] == "field required"
+    assert response["detail"][1]["loc"] == ["body", "office_router_lo_ipv6_address"]
+    assert response["detail"][1]["msg"] == "value is not a valid IPv6 address"
diff --git a/test/fixtures.py b/test/fixtures.py
index bc96381bfa605c3934610eac8490fa275e6051de..732439527e0b34f346488fae687dfff98a80a577 100644
--- a/test/fixtures.py
+++ b/test/fixtures.py
@@ -12,12 +12,13 @@ from gso.products.product_blocks.iptrunk import (
     IptrunkType,
     PhyPortCapacity,
 )
-from gso.products.product_blocks.router import RouterRole, RouterVendor
+from gso.products.product_blocks.router import RouterRole
 from gso.products.product_blocks.site import SiteTier
 from gso.products.product_types.iptrunk import IptrunkInactive
 from gso.products.product_types.router import Router, RouterInactive
 from gso.products.product_types.site import Site, SiteInactive
 from gso.services import subscriptions
+from gso.utils.shared_enums import Vendor
 
 CUSTOMER_ID: UUIDstr = "2f47f65a-0911-e511-80d0-005056956c1a"
 
@@ -107,7 +108,7 @@ def nokia_router_subscription_factory(site_subscription_factory, faker):
         router_subscription.router.router_lo_iso_address = router_lo_iso_address
         router_subscription.router.router_role = router_role
         router_subscription.router.router_site = Site.from_subscription(router_site).site
-        router_subscription.router.vendor = RouterVendor.NOKIA
+        router_subscription.router.vendor = Vendor.NOKIA
 
         router_subscription = SubscriptionModel.from_other_lifecycle(router_subscription, SubscriptionLifecycle.ACTIVE)
         router_subscription.description = description
@@ -158,7 +159,7 @@ def juniper_router_subscription_factory(site_subscription_factory, faker):
         router_subscription.router.router_lo_iso_address = router_lo_iso_address
         router_subscription.router.router_role = router_role
         router_subscription.router.router_site = Site.from_subscription(router_site).site
-        router_subscription.router.vendor = RouterVendor.JUNIPER
+        router_subscription.router.vendor = Vendor.JUNIPER
 
         router_subscription = SubscriptionModel.from_other_lifecycle(router_subscription, SubscriptionLifecycle.ACTIVE)
         router_subscription.description = description
@@ -226,6 +227,7 @@ def iptrunk_subscription_factory(iptrunk_side_subscription_factory, faker):
         iptrunk_ipv4_network=None,
         iptrunk_ipv6_network=None,
         iptrunk_sides=None,
+        status: SubscriptionLifecycle | None = None,
     ) -> UUIDstr:
         product_id = subscriptions.get_product_id_by_name(ProductType.IP_TRUNK)
         description = description or faker.sentence()
@@ -255,6 +257,10 @@ def iptrunk_subscription_factory(iptrunk_side_subscription_factory, faker):
             iptrunk_subscription,
             SubscriptionLifecycle.ACTIVE,
         )
+
+        if status:
+            iptrunk_subscription.status = status
+
         iptrunk_subscription.description = description
         iptrunk_subscription.start_date = start_date
         iptrunk_subscription.save()
diff --git a/test/services/test_infoblox.py b/test/services/test_infoblox.py
index 64bf43fdb5917d53a1e06cc95405ce34b0f0bdd0..bebcdae602f852f4fb92fab4bfbeab85e7f12d26 100644
--- a/test/services/test_infoblox.py
+++ b/test/services/test_infoblox.py
@@ -53,8 +53,8 @@ def _set_up_host_responses():
 
     responses.add(
         method=responses.GET,
-        url="https://10.0.0.1/wapi/v2.12/record%3Ahost?name=test.lo.geant.net&ipv6addr=func%3Anextavailableip%3Adead%3A"
-        "beef%3A%3A%2F80%2Cdefault",
+        url="https://10.0.0.1/wapi/v2.12/record%3Ahost?name=test.lo.geant.net&view=default&ipv6addr=func%3Anextavailabl"
+        "eip%3Adead%3Abeef%3A%3A%2F80%2Cdefault",
         json=[],
     )
 
diff --git a/test/utils/test_helpers.py b/test/utils/test_helpers.py
index 4006c4ad50101e93b10ca16c802761612a244ea7..5dee0aa8f7a5cb981771a63cdfec933986423991 100644
--- a/test/utils/test_helpers.py
+++ b/test/utils/test_helpers.py
@@ -3,8 +3,8 @@ from unittest.mock import patch
 import pytest
 
 from gso.products.product_blocks.iptrunk import IptrunkInterfaceBlock
-from gso.products.product_blocks.router import RouterVendor
 from gso.utils.helpers import available_interfaces_choices_including_current_members, validate_tt_number
+from gso.utils.shared_enums import Vendor
 
 
 @pytest.fixture()
@@ -34,20 +34,20 @@ def generate_tt_numbers(faker, request):
 
 
 def test_non_nokia_router_returns_none(mock_router, faker):
-    mock_router.from_subscription.return_value.router.vendor = RouterVendor.JUNIPER
+    mock_router.from_subscription.return_value.router.vendor = Vendor.JUNIPER
     result = available_interfaces_choices_including_current_members(faker.uuid4(), "10G", [])
     assert result is None
 
 
 def test_nokia_router_with_no_interfaces_returns_empty_choice(mock_router, mock_netbox_client, faker):
-    mock_router.from_subscription.return_value.router.vendor = RouterVendor.NOKIA
+    mock_router.from_subscription.return_value.router.vendor = Vendor.NOKIA
     mock_netbox_client().get_available_interfaces.return_value = iter([])
     result = available_interfaces_choices_including_current_members(faker.uuid4(), "10G", [])
     assert len(result) == 0
 
 
 def test_nokia_router_with_interfaces_returns_choice(mock_router, mock_netbox_client, faker):
-    mock_router.from_subscription.return_value.router.vendor = RouterVendor.NOKIA
+    mock_router.from_subscription.return_value.router.vendor = Vendor.NOKIA
     mock_netbox_client().get_available_interfaces.return_value = iter(
         [
             {"name": "interface1", "module": {"display": "module1"}, "description": "desc1"},
diff --git a/test/workflows/iptrunk/test_activate_iptrunk.py b/test/workflows/iptrunk/test_activate_iptrunk.py
new file mode 100644
index 0000000000000000000000000000000000000000..837f340c6adc77e1aa148760e425429288e4d01f
--- /dev/null
+++ b/test/workflows/iptrunk/test_activate_iptrunk.py
@@ -0,0 +1,36 @@
+import pytest
+
+from gso.products import Iptrunk
+from test.workflows import (
+    assert_complete,
+    assert_suspended,
+    extract_state,
+    resume_workflow,
+    run_workflow,
+)
+
+
+@pytest.mark.workflow()
+def test_activate_router_success(
+    iptrunk_subscription_factory,
+    faker,
+):
+    #  Set up mock return values
+    product_id = iptrunk_subscription_factory(status="provisioning")
+    #  Sanity check
+    assert Iptrunk.from_subscription(product_id).status == "provisioning"
+
+    #  Run workflow
+    initial_input_data = [{"subscription_id": product_id}, {}]
+    result, process_stat, step_log = run_workflow("activate_iptrunk", initial_input_data)
+
+    assert_suspended(result)
+    result, step_log = resume_workflow(process_stat, step_log, input_data=[{"checklist_url": "http://localhost"}])
+
+    assert_complete(result)
+
+    state = extract_state(result)
+    subscription_id = state["subscription_id"]
+    subscription = Iptrunk.from_subscription(subscription_id)
+
+    assert subscription.status == "active"
diff --git a/test/workflows/iptrunk/test_create_iptrunk.py b/test/workflows/iptrunk/test_create_iptrunk.py
index c069618476f87b4beafcf4fbcb89f259fc043071..b85f6d3fc586949dd07d53f5694e23d251eb69f5 100644
--- a/test/workflows/iptrunk/test_create_iptrunk.py
+++ b/test/workflows/iptrunk/test_create_iptrunk.py
@@ -5,9 +5,9 @@ import pytest
 
 from gso.products import Iptrunk, ProductType
 from gso.products.product_blocks.iptrunk import IptrunkType, PhyPortCapacity
-from gso.products.product_blocks.router import RouterVendor
 from gso.services.subscriptions import get_product_id_by_name
 from gso.utils.helpers import LAGMember
+from gso.utils.shared_enums import Vendor
 from test.services.conftest import MockedNetboxClient
 from test.workflows import (
     assert_complete,
@@ -43,11 +43,11 @@ def _netbox_client_mock():
 
 @pytest.fixture()
 def input_form_wizard_data(request, juniper_router_subscription_factory, nokia_router_subscription_factory, faker):
-    vendor = getattr(request, "param", RouterVendor.NOKIA)
+    vendor = getattr(request, "param", Vendor.NOKIA)
     router_side_a = nokia_router_subscription_factory()
 
     # Set side b router to Juniper
-    if vendor == RouterVendor.JUNIPER:
+    if vendor == Vendor.JUNIPER:
         router_side_b = juniper_router_subscription_factory()
         side_b_members = faker.link_members_juniper()
     else:
@@ -165,7 +165,7 @@ def test_iptrunk_creation_fails_when_lso_return_code_is_one(
     assert mock_execute_playbook.call_count == 2
 
 
-@pytest.mark.parametrize("input_form_wizard_data", [RouterVendor.JUNIPER], indirect=True)
+@pytest.mark.parametrize("input_form_wizard_data", [Vendor.JUNIPER], indirect=True)
 @pytest.mark.workflow()
 @patch("gso.workflows.iptrunk.create_iptrunk.execute_playbook")
 @patch("gso.workflows.iptrunk.create_iptrunk.infoblox.allocate_v6_network")
diff --git a/test/workflows/iptrunk/test_migrate_iptrunk.py b/test/workflows/iptrunk/test_migrate_iptrunk.py
index c7ca62bf11a89dbde7aa9d3bf2297eef2c5651cd..825b9e7c779287e3b42ff11d01413562ff66d44b 100644
--- a/test/workflows/iptrunk/test_migrate_iptrunk.py
+++ b/test/workflows/iptrunk/test_migrate_iptrunk.py
@@ -4,8 +4,8 @@ from unittest.mock import patch
 import pytest
 
 from gso.products import Iptrunk
-from gso.products.product_blocks.router import RouterVendor
 from gso.products.product_types.router import Router
+from gso.utils.shared_enums import Vendor
 from test import USER_CONFIRM_EMPTY_FORM
 from test.conftest import UseJuniperSide
 from test.workflows import (
@@ -189,15 +189,15 @@ def test_migrate_iptrunk_success(
     vendor_new = Router.from_subscription(new_router).router.vendor
 
     # Only Nokia will be checked on netbox
-    num_nokia_lags = 1 if vendor_new == RouterVendor.NOKIA else 0
-    num_nokia_reserved = 2 * (vendor_new == RouterVendor.NOKIA)
-    num_nokia_attached = 2 * (vendor_new == RouterVendor.NOKIA)
+    num_nokia_lags = 1 if vendor_new == Vendor.NOKIA else 0
+    num_nokia_reserved = 2 * (vendor_new == Vendor.NOKIA)
+    num_nokia_attached = 2 * (vendor_new == Vendor.NOKIA)
 
     # Only interfaces lag delete for nokia node is tested
-    num_nokia_lag_del = 1 * (vendor_old == RouterVendor.NOKIA)
+    num_nokia_lag_del = 1 * (vendor_old == Vendor.NOKIA)
 
     # Only free interfaces when node was nokia
-    num_nokia_free = 2 * (vendor_old == RouterVendor.NOKIA)
+    num_nokia_free = 2 * (vendor_old == Vendor.NOKIA)
 
     # Assert all Netbox calls have been made
     assert mocked_create_interface.call_count == num_nokia_lags  # once for creating the LAG on the newly replaced side:
diff --git a/test/workflows/iptrunk/test_modify_trunk_interface.py b/test/workflows/iptrunk/test_modify_trunk_interface.py
index b3b15a75e8904eab51b767fb6ef3d527169efff1..78bedca5562302e2b6e6c12e3e71bbbb93bf7758 100644
--- a/test/workflows/iptrunk/test_modify_trunk_interface.py
+++ b/test/workflows/iptrunk/test_modify_trunk_interface.py
@@ -4,7 +4,7 @@ import pytest
 
 from gso.products import Iptrunk
 from gso.products.product_blocks.iptrunk import IptrunkType, PhyPortCapacity
-from gso.products.product_blocks.router import RouterVendor
+from gso.utils.shared_enums import Vendor
 from test.conftest import UseJuniperSide
 from test.workflows import (
     assert_complete,
@@ -141,15 +141,15 @@ def test_iptrunk_modify_trunk_interface_success(
     # Only Nokia interfaces are checked
     vendor_side_a = subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.vendor
     vendor_side_b = subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.vendor
-    num_ifaces = (len(new_side_a_ae_members) if vendor_side_a == RouterVendor.NOKIA else 0) + (
-        len(new_side_b_ae_members) if vendor_side_b == RouterVendor.NOKIA else 0
+    num_ifaces = (len(new_side_a_ae_members) if vendor_side_a == Vendor.NOKIA else 0) + (
+        len(new_side_b_ae_members) if vendor_side_b == Vendor.NOKIA else 0
     )
 
     # Define free interfaces for only nokia sides
-    num_free_ifaces = 2 * (vendor_side_a == RouterVendor.NOKIA) + 2 * (vendor_side_b == RouterVendor.NOKIA)
+    num_free_ifaces = 2 * (vendor_side_a == Vendor.NOKIA) + 2 * (vendor_side_b == Vendor.NOKIA)
 
     # lag interface for nokia sides
-    num_lag_ifaces = int(vendor_side_a == RouterVendor.NOKIA) + int(vendor_side_b == RouterVendor.NOKIA)
+    num_lag_ifaces = int(vendor_side_a == Vendor.NOKIA) + int(vendor_side_b == Vendor.NOKIA)
 
     assert mocked_reserve_interface.call_count == num_ifaces  # Only nokia interfaces per side num is randomly generated
     assert mocked_attach_interface_to_lag.call_count == num_ifaces
diff --git a/test/workflows/router/test_activate_router.py b/test/workflows/router/test_activate_router.py
new file mode 100644
index 0000000000000000000000000000000000000000..2e60fd7ce60e11bca0f4ae2e4c47d49e244a38e0
--- /dev/null
+++ b/test/workflows/router/test_activate_router.py
@@ -0,0 +1,36 @@
+import pytest
+
+from gso.products import Router
+from test.workflows import (
+    assert_complete,
+    assert_suspended,
+    extract_state,
+    resume_workflow,
+    run_workflow,
+)
+
+
+@pytest.mark.workflow()
+def test_activate_router_success(
+    nokia_router_subscription_factory,
+    faker,
+):
+    #  Set up mock return values
+    product_id = nokia_router_subscription_factory(status="provisioning")
+    #  Sanity check
+    assert Router.from_subscription(product_id).status == "provisioning"
+
+    #  Run workflow
+    initial_input_data = [{"subscription_id": product_id}, {}]
+    result, process_stat, step_log = run_workflow("activate_router", initial_input_data)
+
+    assert_suspended(result)
+    result, step_log = resume_workflow(process_stat, step_log, input_data=[{"checklist_url": "http://localhost"}])
+
+    assert_complete(result)
+
+    state = extract_state(result)
+    subscription_id = state["subscription_id"]
+    subscription = Router.from_subscription(subscription_id)
+
+    assert subscription.status == "active"
diff --git a/test/workflows/router/test_create_router.py b/test/workflows/router/test_create_router.py
index ca012757153478de3ca4e50fa37b44daa9696595..efa7e6732c524cbf1d6edf0584fe207b070efcdf 100644
--- a/test/workflows/router/test_create_router.py
+++ b/test/workflows/router/test_create_router.py
@@ -4,9 +4,10 @@ import pytest
 from infoblox_client import objects
 
 from gso.products import ProductType, Site
-from gso.products.product_blocks.router import RouterRole, RouterVendor
+from gso.products.product_blocks.router import RouterRole
 from gso.products.product_types.router import Router
 from gso.services.subscriptions import get_product_id_by_name
+from gso.utils.shared_enums import Vendor
 from test import USER_CONFIRM_EMPTY_FORM
 from test.workflows import (
     assert_complete,
@@ -29,7 +30,7 @@ def router_creation_input_form_data(site_subscription_factory, faker):
         "hostname": faker.pystr(),
         "ts_port": faker.pyint(),
         "router_role": faker.random_choices(elements=(RouterRole.P, RouterRole.PE, RouterRole.AMT), length=1)[0],
-        "vendor": RouterVendor.NOKIA,
+        "vendor": Vendor.NOKIA,
     }