diff --git a/gso/api/v1/imports.py b/gso/api/v1/imports.py
index 684d7b7eb43bd878b5d3919a768f7a70ab33172c..8920765a83efa12cc50f2bf56ae9465c9e746c6e 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_choices 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..e4ad3711bdc02a03b2e021d591ae5e6b8235133f
--- /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 = 'bacd55c26106'
+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/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..83471d8f66f903f8d7c4f2b7129e197cd650c2a7
--- /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_choices 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..1905b8652f3ad18c51a0433502ee11f7d10eef3f 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_choices 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,8 +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]):
     """A router that's currently deployed in the network."""
@@ -97,4 +72,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..2e2d5b7c1aa548ce5bbbf9d586370edfdb6153ee
--- /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_choices 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/subscriptions.py b/gso/services/subscriptions.py
index e77b8d7ba60bfa869ac2a4b4f6638c8eaa55e8ab..e89d9c25ad1cc52f68ae6ac3aea75a0542b92559 100644
--- a/gso/services/subscriptions.py
+++ b/gso/services/subscriptions.py
@@ -20,6 +20,7 @@ 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]
 
@@ -177,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/utils/helpers.py b/gso/utils/helpers.py
index c43a80737f56e37e3288e78d89a0ab38dced94e4..74050ef545b768042115093cb19263dbb7831dd7 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_choices 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']}"
@@ -54,16 +55,16 @@ def available_interfaces_choices(router_id: UUID, speed: str) -> Choice | None:
 
 
 def available_interfaces_choices_including_current_members(
-    router_id: UUID,
-    speed: str,
-    interfaces: list[IptrunkInterfaceBlock],
+        router_id: UUID,
+        speed: str,
+        interfaces: list[IptrunkInterfaceBlock],
 ) -> Choice | None:
     """Return a list of available interfaces for a given router and speed including the 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_choices.py b/gso/utils/shared_choices.py
new file mode 100644
index 0000000000000000000000000000000000000000..4861e9e134ecf113d74772a08282e7928829d19b
--- /dev/null
+++ b/gso/utils/shared_choices.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 3566d7fc4fcf997d286223edf0dc097fa2aa789e..d25088730e93388aadef15057e80d3eca8a93ce6 100644
--- a/gso/workflows/__init__.py
+++ b/gso/workflows/__init__.py
@@ -32,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/create_iptrunk.py b/gso/workflows/iptrunk/create_iptrunk.py
index 904803ca9d0c59e3dd974d9e134cd906f86b84cd..ea2aef09b013a4ed3148818584488b8d56b3d2bf 100644
--- a/gso/workflows/iptrunk/create_iptrunk.py
+++ b/gso/workflows/iptrunk/create_iptrunk.py
@@ -21,7 +21,7 @@ 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,6 +38,7 @@ from gso.utils.helpers import (
     validate_router_in_netbox,
     validate_tt_number,
 )
+from gso.utils.shared_choices import Vendor
 
 
 def initial_input_form_generator(product_name: str) -> FormGenerator:
@@ -85,7 +86,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]
@@ -133,7 +134,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]
@@ -403,7 +404,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,
@@ -472,8 +473,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..edcc87dff51864dab5ae3e66d9e6c8bb18e82cce 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_choices 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..0e0aa5254bceb4c70bf55835a9427b5070aa66b1 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_choices 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..aa5a91eaae3dd0526dbce2effdafb44492ae9ce2 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_choices 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/create_router.py b/gso/workflows/router/create_router.py
index 29a60dbb87d73f68f695710d3b746a8e8fa4c198..bdf829b7b9ad1857b05d36e5e95d3fe72cbb26ba 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_choices 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..1c5052b55f7f6d07179e43135e51a5cd1106cb4b 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_choices 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..ce7b3664c9cdf93735d519d92ead73c9fdcd9530
--- /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_choices 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..e7e332ff6fc9522434d875760376534cdb3732e9 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_choices 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..e13896f20f31e2f499e62425eb6c26c745137673
--- /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_choices 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/test/api/test_imports.py b/test/api/test_imports.py
index d823bc367d39c3072faf1c307d4c8d796151de22..0737860921c930bb3928efcf859ed67c6a0ac676 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_choices 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 32bbfdca03b7218df8e6ce36b7f184f1390668c5..933fcfc14738460aea302adba8d5af2c080be08f 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_choices 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
diff --git a/test/utils/test_helpers.py b/test/utils/test_helpers.py
index 4006c4ad50101e93b10ca16c802761612a244ea7..a7a09ade563cbea460f2884ae718e1545b6975c2 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_choices 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_create_iptrunk.py b/test/workflows/iptrunk/test_create_iptrunk.py
index c069618476f87b4beafcf4fbcb89f259fc043071..d8ca8c09c719a5bf27f666074d66f6bb2a21cf0e 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_choices 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..634bd52774a45370594b4413d94db40ea59db6bd 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_choices 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..5a7d1596c4fb0c53a3ddb840bfa89f1d96bbb233 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_choices 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_create_router.py b/test/workflows/router/test_create_router.py
index ca012757153478de3ca4e50fa37b44daa9696595..898823b79e84bb34200225c132f4319515999cf5 100644
--- a/test/workflows/router/test_create_router.py
+++ b/test/workflows/router/test_create_router.py
@@ -4,10 +4,11 @@ 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 test import USER_CONFIRM_EMPTY_FORM
+from gso.utils.shared_choices import Vendor
 from test.workflows import (
     assert_complete,
     assert_pp_interaction_failure,
@@ -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,
     }