diff --git a/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/TestSitesService.java b/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/FeatureCollectionService.java
similarity index 69%
rename from src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/TestSitesService.java
rename to src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/FeatureCollectionService.java
index 771ef45aa50137c8343bd1d72250827eba955df7..e5cb9d5dbedf3396db5b4ae75964df59acdf480c 100644
--- a/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/TestSitesService.java
+++ b/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/FeatureCollectionService.java
@@ -3,7 +3,6 @@ package gov.usgs.earthquake.nshmp.netcdf.www;
 import com.google.gson.JsonElement;
 
 import gov.usgs.earthquake.nshmp.geo.json.FeatureCollection;
-import gov.usgs.earthquake.nshmp.netcdf.NetcdfHazardCurves;
 import gov.usgs.earthquake.nshmp.netcdf.NetcdfVersion;
 import gov.usgs.earthquake.nshmp.www.ResponseBody;
 import gov.usgs.earthquake.nshmp.www.ResponseMetadata;
@@ -12,27 +11,29 @@ import io.micronaut.http.HttpRequest;
 import jakarta.inject.Singleton;
 
 /**
- * Test sites handler for {@link TestSitesController}.
+ * Handle feature collection services.
  *
  * @author U.S. Geological Survey
  */
 @Singleton
-public class TestSitesService {
-  static final String NAME = "Test Sites";
+public class FeatureCollectionService {
 
-  static String handleSites(HttpRequest<?> http, NetcdfHazardCurves netcdf, Boolean raw) {
+  static String handleFeatureCollection(
+      HttpRequest<?> http,
+      String name,
+      FeatureCollection fc,
+      Boolean raw) {
     RequestData requestData = new RequestData(raw);
-    FeatureCollection sites = netcdf.netcdfData().sites();
 
     if (requestData.raw) {
-      return sites.toJson();
+      return fc.toJson();
     } else {
       var response = ResponseBody.<RequestData, JsonElement> success()
-          .name(NAME)
+          .name(name)
           .url(http.getUri().toString())
           .metadata(new ResponseMetadata(NetcdfVersion.appVersions()))
           .request(requestData)
-          .response(sites.toJsonTree())
+          .response(fc.toJsonTree())
           .build();
 
       return NetcdfWsUtils.GSON.toJson(response);
diff --git a/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/MapController.java b/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/MapController.java
new file mode 100644
index 0000000000000000000000000000000000000000..ef89ab0ba4fe7a5ea9e4fe0bab3f69fad34b9e6e
--- /dev/null
+++ b/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/MapController.java
@@ -0,0 +1,94 @@
+package gov.usgs.earthquake.nshmp.netcdf.www;
+
+import java.nio.file.Path;
+
+import gov.usgs.earthquake.nshmp.geo.json.FeatureCollection;
+import gov.usgs.earthquake.nshmp.netcdf.NetcdfDataFilesHazardCurves;
+import gov.usgs.earthquake.nshmp.netcdf.www.FeatureCollectionService.RequestData;
+import gov.usgs.earthquake.nshmp.www.NshmpMicronautServlet;
+import gov.usgs.earthquake.nshmp.www.ResponseBody;
+
+import io.micronaut.context.annotation.Value;
+import io.micronaut.context.event.StartupEvent;
+import io.micronaut.core.annotation.Nullable;
+import io.micronaut.http.HttpRequest;
+import io.micronaut.http.HttpResponse;
+import io.micronaut.http.MediaType;
+import io.micronaut.http.annotation.Controller;
+import io.micronaut.http.annotation.Get;
+import io.micronaut.http.annotation.PathVariable;
+import io.micronaut.http.annotation.QueryValue;
+import io.micronaut.runtime.event.annotation.EventListener;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.inject.Inject;
+
+@Tag(name = MapController.NAME)
+@Controller("/map")
+public class MapController {
+  static final String NAME = "Map Boundary";
+
+  @Inject
+  private NshmpMicronautServlet servlet;
+
+  /**
+   * The path to a hazard NetCDF file
+   */
+  @Value("${nshmp-ws-static.netcdf-path}")
+  Path netcdfPath;
+
+  NetcdfServiceHazardCurves service;
+
+  /**
+   * Read in data type and return the appropriate service to use.
+   */
+  @EventListener
+  void startup(StartupEvent event) {
+    var netcdfDataFiles = new NetcdfDataFilesHazardCurves(netcdfPath);
+    service = new NetcdfServiceHazardCurves(netcdfDataFiles);
+  }
+
+  @Operation(
+      summary = "Get the GeoJSON map boundary",
+      description = "Returns the feature collection of NSHM map boundary",
+      operationId = "map")
+  @ApiResponse(
+      description = "NSHM map boundary",
+      responseCode = "200",
+      content = @Content(
+          schema = @Schema(implementation = Response.class)))
+  @Get(uri = "{?raw}", produces = MediaType.APPLICATION_JSON)
+  public HttpResponse<String> doGet(
+      HttpRequest<?> http,
+      @QueryValue(defaultValue = "false") @Nullable Boolean raw) {
+    try {
+      FeatureCollection map = service.netcdf().netcdfData().map();
+      return HttpResponse
+          .ok(FeatureCollectionService.handleFeatureCollection(http, NAME, map, raw));
+    } catch (Exception e) {
+      return NetcdfWsUtils.handleError(e, NAME, http.getUri().toString());
+    }
+  }
+
+  @Operation(
+      summary = "Get the GeoJSON map boundary",
+      description = "Returns the feature collection of NSHM map boundary",
+      operationId = "map-slash")
+  @ApiResponse(
+      description = "NSHM map boundary",
+      responseCode = "200",
+      content = @Content(
+          schema = @Schema(implementation = Response.class)))
+  @Get(uri = "/{raw}", produces = MediaType.APPLICATION_JSON)
+  public HttpResponse<String> doGetSlash(
+      HttpRequest<?> http,
+      @PathVariable(defaultValue = "false") @Nullable Boolean raw) {
+    return doGet(http, raw);
+  }
+
+  // Swagger schema
+  private static class Response extends ResponseBody<RequestData, FeatureCollection> {};
+}
diff --git a/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/NetcdfServiceHazardCurves.java b/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/NetcdfServiceHazardCurves.java
index b308f719370ced23737609fef91f8b5900ca2733..6984e675bb8bf2b8232500c46e977c2a94c723e7 100644
--- a/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/NetcdfServiceHazardCurves.java
+++ b/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/NetcdfServiceHazardCurves.java
@@ -3,8 +3,6 @@ package gov.usgs.earthquake.nshmp.netcdf.www;
 import java.util.List;
 import java.util.stream.Collectors;
 
-import com.google.gson.JsonElement;
-
 import gov.usgs.earthquake.nshmp.data.XySequence;
 import gov.usgs.earthquake.nshmp.geo.Location;
 import gov.usgs.earthquake.nshmp.gmm.Imt;
@@ -220,12 +218,10 @@ public class NetcdfServiceHazardCurves extends
 
   static class HazardSourceModel extends SourceModel {
     public final List<Imt> imts;
-    public final JsonElement map;
 
     HazardSourceModel(NetcdfHazardCurves netcdf) {
       super(netcdf);
       imts = netcdf.netcdfData().imts().stream().sorted().collect(Collectors.toList());
-      map = netcdf.netcdfData().map().toJsonTree();
     }
   }
 }
diff --git a/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/TestSitesController.java b/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/TestSitesController.java
index 4a1b436e5f6f33e564ff57ff32d23b7c43a545ba..751e8e5319ba3270ff7e886917e5e968ea4aad3f 100644
--- a/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/TestSitesController.java
+++ b/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/TestSitesController.java
@@ -4,7 +4,7 @@ import java.nio.file.Path;
 
 import gov.usgs.earthquake.nshmp.geo.json.FeatureCollection;
 import gov.usgs.earthquake.nshmp.netcdf.NetcdfDataFilesHazardCurves;
-import gov.usgs.earthquake.nshmp.netcdf.www.TestSitesService.RequestData;
+import gov.usgs.earthquake.nshmp.netcdf.www.FeatureCollectionService.RequestData;
 import gov.usgs.earthquake.nshmp.www.NshmpMicronautServlet;
 import gov.usgs.earthquake.nshmp.www.ResponseBody;
 
@@ -26,9 +26,10 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.inject.Inject;
 
-@Tag(name = TestSitesService.NAME)
+@Tag(name = TestSitesController.NAME)
 @Controller("/sites")
 public class TestSitesController {
+  static final String NAME = "Test Sites";
 
   @Inject
   private NshmpMicronautServlet servlet;
@@ -64,9 +65,11 @@ public class TestSitesController {
       HttpRequest<?> http,
       @QueryValue(defaultValue = "false") @Nullable Boolean raw) {
     try {
-      return HttpResponse.ok(TestSitesService.handleSites(http, service.netcdf(), raw));
+      FeatureCollection sites = service.netcdf().netcdfData().sites();
+      return HttpResponse
+          .ok(FeatureCollectionService.handleFeatureCollection(http, NAME, sites, raw));
     } catch (Exception e) {
-      return NetcdfWsUtils.handleError(e, TestSitesService.NAME, http.getUri().toString());
+      return NetcdfWsUtils.handleError(e, NAME, http.getUri().toString());
     }
   }