diff --git a/mapping_provider/api/map.py b/mapping_provider/api/map.py
index 54fdba9b8b3f03bee869be61c44731966415784c..b8bc36b921069f51f3ca75e7bc954f13336d8fd9 100644
--- a/mapping_provider/api/map.py
+++ b/mapping_provider/api/map.py
@@ -146,3 +146,17 @@ def get_trunks() -> services.ServiceList:
     handler for /trunks, same as /services/IP TRUNK
     """
     return get_services(service_type='IP TRUNK')
+
+
+@router.get("/map-info")
+@router.get("/map-info/{service_type}")
+def get_map_info(service_type: str | None = None) -> services.MapInfo:
+    """
+    handler for /map-info
+    """
+    pop_list = get_pops()
+    pops_dict = {_p.name: _p.abbreviation for _p in pop_list.pops}
+    return_value = services.build_map_info(pop_abbrevs=pops_dict, service_type=service_type)
+    if not return_value.nodes:
+        raise HTTPException(status_code=404, detail=f'unrecognized service type: {service_type}')
+    return return_value
diff --git a/mapping_provider/backends/services.py b/mapping_provider/backends/services.py
index 7406a0ebb29dbc150a2fd2297eb978aa22cd1a22..a19bddc5e477ffaf217806ab4a4c002644bfb509 100644
--- a/mapping_provider/backends/services.py
+++ b/mapping_provider/backends/services.py
@@ -38,6 +38,11 @@ class ServiceList(BaseModel):
     services: list[Service]
 
 
+class MapInfo(BaseModel):
+    nodes: list[str]
+    edges: list[str]
+
+
 def endpoint_equipment(endpoint: dict[str, Any]) -> str:
     """
     convert the correlator router hostname or optical equipment name
@@ -145,3 +150,45 @@ def build_service_info_list(service_type: str | None = None) -> ServiceList:
     return a list of mappable info about all operational services
     """  
     return ServiceList(services=list(_services(service_type)))
+
+
+def _service_label_in_map(service: Service) -> str | None:
+    """
+    return a label for the service in the map
+    """
+    if not service.pops:
+        return None
+
+    pops = sorted(service.pops)
+    label_components = pops + [service.scid[:8].upper()]
+    return '-'.join(label_components)
+
+
+def build_map_info(pop_abbrevs: dict[str, str], service_type: str | None = None) -> MapInfo:
+    """
+    return a list of mappable info about operational services
+    """
+    nodes = set()
+    edges = set()
+
+    def _get_service_pops(svc: Service) -> Generator[str]:
+        for _p in svc.pops:
+            abbrev = pop_abbrevs.get(_p)
+            if abbrev:
+                yield abbrev
+
+    for _s in _services(service_type):
+
+        _pops = set(_get_service_pops(_s))
+        if not _pops:
+            logger.warning(f'service {_s.sid} has no POPs')
+            continue
+
+        nodes |= set(_pops)
+
+        _lc = list(_pops) + [_s.scid[:8].upper()]
+        edges.add('-'.join(_lc))
+
+    return MapInfo(
+        nodes=sorted(nodes),
+        edges=sorted(edges))
diff --git a/test/test_map_endpoints.py b/test/test_map_endpoints.py
index c3dbaba94c42eed1fdaf0c348f7aa657bda64b87..457eb3b7ad605ab959d1155c21c6f38c1bc3cc95 100644
--- a/test/test_map_endpoints.py
+++ b/test/test_map_endpoints.py
@@ -4,20 +4,22 @@ import pytest
 import responses
 
 from mapping_provider.api.map import EquipmentList, PopList
-from mapping_provider.backends.services import ServiceList
+from mapping_provider.backends.services import MapInfo, ServiceList
 
 from .common import load_test_data
 
 
-@responses.activate
-def test_get_pops(client):
-
+def _add_pops_response():
     responses.add(
         method=responses.GET,
         url=re.compile(r'.*/map/pops$'),
         json=load_test_data('inprov-pops.json')
     )
 
+@responses.activate
+def test_get_pops(client):
+    _add_pops_response()
+
     rv = client.get("/map/pops")
     assert rv.status_code == 200
     pop_list = PopList.model_validate(rv.json())
@@ -86,6 +88,7 @@ def test_get_all_services(client):
     service_list = ServiceList.model_validate(rv.json())
     assert service_list.services, 'test data should not be empty'
 
+
 @responses.activate
 def test_get_trunks(client):
     rv = client.get("/map/trunks")
@@ -95,3 +98,20 @@ def test_get_trunks(client):
     assert all(s.type == 'IP TRUNK' for s in service_list.services)
 
 
+@responses.activate
+def test_get_trunk_map_info(client):
+    _add_pops_response()
+
+    rv = client.get("/map/map-info/IP TRUNK")
+    assert rv.status_code == 200
+    map_info = MapInfo.model_validate(rv.json())
+    assert map_info.nodes, 'test data should not be empty'
+    assert map_info.edges, 'test data should not be empty'
+
+
+@responses.activate
+def test_get_unknown_service_type_map_info(client):
+    _add_pops_response()
+    rv = client.get("/map/map-info/BOGUS_SERVICE_TYPE")
+    assert rv.status_code == 404
+