Skip to content

Router configuration

This section covers all options that can be set for each router using the router-config.json file. These options can be applied by the OTP server without rebuilding the graph.

Configure using command-line arguments

Certain settings can be provided on the command line, when starting OpenTripPlanner. See the CommandLineParameters class for a full list of arguments .

Routing defaults

There are many trip planning options used in the OTP web API, and more exist internally that are not exposed via the API. You may want to change the default value for some of these parameters, i.e. the value which will be applied unless it is overridden in a web API request.

A full list of them can be found in the RouteRequest.

Parameter Summary

Config Parameter Type Summary Req./Opt. Default Value Since
configVersion string Deployment version of the router-config.json. Optional 2.1
flex object Configuration for flex routing. Optional 2.1
rideHailingServices object[] Configuration for interfaces to external ride hailing services like Uber. Optional 2.3
routingDefaults object The default parameters for the routing query. Optional 2.0
server object Configuration for router server. Optional 2.4
   apiProcessingTimeout duration Maximum processing time for an API request Optional "PT-1S" 2.4
   traceParameters object[] Trace OTP request using HTTP request/response parameter(s) combined with logging. Optional 2.4
         generateIdIfMissing boolean If true a unique value is generated if no http request header is provided, or the value is missing. Optional false 2.4
         httpRequestHeader string The header-key to use when fetching the trace parameter value Optional 2.4
         httpResponseHeader string The header-key to use when saving the value back into the http response Optional 2.4
         logKey string The log event key used. Optional 2.4
timetableUpdates object Global configuration for timetable updaters. Optional 2.2
   maxSnapshotFrequency duration How long a snapshot should be cached. Optional "PT1S" 2.2
   purgeExpiredData boolean Should expired realtime data be purged from the graph. Apply to GTFS-RT and Siri updates. Optional true 2.2
transit object Configuration for transit searches with RAPTOR. Optional na
   iterationDepartureStepInSeconds integer Step for departure times between each RangeRaptor iterations. Optional 60 na
   maxNumberOfTransfers integer This parameter is used to allocate enough memory space for Raptor. Optional 12 na
   scheduledTripBinarySearchThreshold integer This threshold is used to determine when to perform a binary trip schedule search. Optional 50 na
   searchThreadPoolSize integer Split a travel search in smaller jobs and run them in parallel to improve performance. Optional 0 na
   transferCacheMaxSize integer The maximum number of distinct transfers parameters to cache pre-calculated transfers for. Optional 25 na
   dynamicSearchWindow object The dynamic search window coefficients used to calculate the EDT, LAT and SW. Optional 2.1
      maxWindow duration Upper limit for the search-window calculation. Optional "PT3H" 2.2
      minTransitTimeCoefficient double The coefficient to multiply with minTransitTime. Optional 0.5 2.1
      minWaitTimeCoefficient double The coefficient to multiply with minWaitTime. Optional 0.5 2.1
      minWindow duration The constant minimum duration for a raptor-search-window. Optional "PT40M" 2.2
      stepMinutes integer Used to set the steps the search-window is rounded to. Optional 10 2.1
   pagingSearchWindowAdjustments duration[] The provided array of durations is used to increase the search-window for the next/previous page. Optional na
   stopTransferCost enum map of integer Use this to set a stop transfer cost for the given transfer priority Optional 2.0
   transferCacheRequests object[] Routing requests to use for pre-filling the stop-to-stop transfer cache. Optional 2.3
transmodelApi object Configuration for the Transmodel GraphQL API. Optional 2.1
   hideFeedId boolean Hide the FeedId in all API output, and add it to input. Optional false na
   tracingHeaderTags string[] Used to group requests when monitoring OTP. Optional na
updaters object[] Configuration for the updaters that import various types of data into OTP. Optional 1.5
vectorTileLayers object[] Configuration of the individual layers for the Mapbox vector tiles. Optional 2.0
vehicleRentalServiceDirectory object Configuration for the vehicle rental service directory. Optional 2.0
   language string Language code. Optional na
   sourcesName string Json tag name for updater sources. Optional "systems" na
   updaterNetworkName string Json tag name for the network name for each source. Optional "id" na
   updaterUrlName string Json tag name for endpoint urls for each source. Optional "url" na
   url uri Endpoint for the VehicleRentalServiceDirectory Required na
   headers map of string HTTP headers to add to the request. Any header key, value can be inserted. Optional na

Parameter Details

configVersion

Since version: 2.1Type: stringCardinality: Optional
Path: /

Deployment version of the router-config.json.

The config-version is a parameter which each OTP deployment may set to be able to query the OTP server and verify that it uses the correct version of the config. The version should be injected into the config in the (continuous) deployment pipeline. How this is done, is up to the deployment.

The config-version has no effect on OTP, and is provided as is on the API. There is no syntax or format check on the version and it can be any string.

Be aware that OTP uses the config embedded in the loaded graph if no new config is provided.

server

Since version: 2.4Type: objectCardinality: Optional
Path: /

Configuration for router server.

These parameters are used to configure the router server. Many parameters are specific to a domain, these are set in the routing request.

apiProcessingTimeout

Since version: 2.4Type: durationCardinality: OptionalDefault value: "PT-1S"
Path: /server

Maximum processing time for an API request

This timeout limits the server-side processing time for a given API request. This does not include network latency nor waiting time in the HTTP server thread pool. The default value is -1s(no timeout). The timeout is applied to all APIs (REST, Transmodel & GTFS GraphQL). The timeout is not enforced when the parallel routing OTP feature is in use.

traceParameters

Since version: 2.4Type: object[]Cardinality: Optional
Path: /server

Trace OTP request using HTTP request/response parameter(s) combined with logging.

OTP supports tracing user requests across log events and "outside" services. OTP can insert http-request-header parameters into all associated log events and into the http response. If the value is not present in the request, a unique value can be generated. The OTP generated value is a 6 characters long base 36[0-9a-z] character string.

Use-case Correlation-ID

A common use-case in a service oriented environment is to use a correlation-id to identify all log messages across multiple (micro-)services from the same user. This is done by setting the "X-Correlation-ID" http header in the http facade/gateway. Use the "traceParameters" to configure OTP to pick up the correlation id, insert it into the logs and return it. See the example below on how-to configure the "server.traceParameters" instance.

logKey

Since version: 2.4Type: stringCardinality: Optional
Path: /server/traceParameters/[0]

The log event key used.

OTP stores the key/value pair in the log MDC(Mapped Diagnostic Context). To use it you normally include the key in the log pattern like this: %X{LOG-KEY}. See your log framework for details. Only log4j and logback support this.

maxSnapshotFrequency

Since version: 2.2Type: durationCardinality: OptionalDefault value: "PT1S"
Path: /timetableUpdates

How long a snapshot should be cached.

If a timetable snapshot is requested less than this number of milliseconds after the previous snapshot, then return the same instance. Throttles the potentially resource-consuming task of duplicating a TripPattern → Timetable map and indexing the new Timetables. Applies to GTFS-RT and Siri updates.

transit

Since version: naType: objectCardinality: Optional
Path: /

Configuration for transit searches with RAPTOR.

Some of these parameters for tuning transit routing are only available through configuration and cannot be set in the routing request. These parameters work together with the default routing request and the actual routing request.

iterationDepartureStepInSeconds

Since version: naType: integerCardinality: OptionalDefault value: 60
Path: /transit

Step for departure times between each RangeRaptor iterations.

This is a performance optimization parameter. A transit network usually uses minute resolution for the timetables, so to match that, set this variable to 60 seconds. Setting it to less than 60 will not give better result, but degrade performance. Setting it to 120 seconds will improve performance, but you might get a slack of 60 seconds somewhere in the result.

maxNumberOfTransfers

Since version: naType: integerCardinality: OptionalDefault value: 12
Path: /transit

This parameter is used to allocate enough memory space for Raptor.

Set it to the maximum number of transfers for any given itinerary expected to be found within the entire transit network. The memory overhead of setting this higher than the maximum number of transfers is very little so it is better to set it too high than to low.

scheduledTripBinarySearchThreshold

Since version: naType: integerCardinality: OptionalDefault value: 50
Path: /transit

This threshold is used to determine when to perform a binary trip schedule search.

This reduce the number of trips departure time lookups and comparisons. When testing with data from Entur and all of Norway as a Graph, the optimal value was about 50. If you calculate the departure time every time or want to fine tune the performance, changing this may improve the performance a few percents.

searchThreadPoolSize

Since version: naType: integerCardinality: OptionalDefault value: 0
Path: /transit

Split a travel search in smaller jobs and run them in parallel to improve performance.

Use this parameter to set the total number of executable threads available across all searches. Multiple searches can run in parallel - this parameter have no effect with regard to that. If 0, no extra threads are started and the search is done in one thread.

transferCacheMaxSize

Since version: naType: integerCardinality: OptionalDefault value: 25
Path: /transit

The maximum number of distinct transfers parameters to cache pre-calculated transfers for.

If too low, requests may be slower. If too high, more memory may be used then required.

dynamicSearchWindow

Since version: 2.1Type: objectCardinality: Optional
Path: /transit

The dynamic search window coefficients used to calculate the EDT, LAT and SW.

The dynamic search window coefficients is used to calculate EDT(earliest-departure-time), LAT(latest-arrival-time) and SW(raptor-search-window) request parameters using heuristics. The heuristics perform a Raptor search (one-iteration) to find a trip which we use to find a lower bound for the travel duration time - the "minTransitTime". The heuristic search is used for other purposes too, and is very fast.

At least the EDT or the LAT must be passed into Raptor to perform a Range Raptor search. If unknown/missing the parameters(EDT, LAT, DW) are dynamically calculated. The dynamic coefficients affect the performance and should be tuned to match the deployment.

The request parameters are calculated like this:

    DW  = round_N(C + T * minTransitTime + W * minWaitTime)
    LAT = EDT + DW + minTransitTime
    EDT = LAT - (DW + minTransitTime)

The round_N(...) method rounds the input to the closest multiplication of N.

The 3 coefficients above are:

  • C is parameter: minWindow
  • T is parameter: minTransitTimeCoefficient
  • W is parameter: minWaitTimeCoefficient
  • N is parameter: stepMinutes

In addition there is an upper bound on the calculation of the search window: maxWindow.

maxWindow

Since version: 2.2Type: durationCardinality: OptionalDefault value: "PT3H"
Path: /transit/dynamicSearchWindow

Upper limit for the search-window calculation.

Long search windows consumes a lot of resources and may take a long time. Use this parameter to tune the desired maximum search time.

This is the parameter that affects the response time most, the downside is that a search is only guaranteed to be pareto-optimal within a search-window.

minTransitTimeCoefficient

Since version: 2.1Type: doubleCardinality: OptionalDefault value: 0.5
Path: /transit/dynamicSearchWindow

The coefficient to multiply with minTransitTime.

Use a value between 0.0 and 3.0. Using 0.0 will eliminate the minTransitTime from the dynamic raptor-search-window calculation.

minWaitTimeCoefficient

Since version: 2.1Type: doubleCardinality: OptionalDefault value: 0.5
Path: /transit/dynamicSearchWindow

The coefficient to multiply with minWaitTime.

Use a value between 0.0 and 1.0. Using 0.0 will eliminate the minWaitTime from the dynamic raptor-search-window calculation.

minWindow

Since version: 2.2Type: durationCardinality: OptionalDefault value: "PT40M"
Path: /transit/dynamicSearchWindow

The constant minimum duration for a raptor-search-window.

Use a value between 20 and 180 minutes in a normal deployment.

stepMinutes

Since version: 2.1Type: integerCardinality: OptionalDefault value: 10
Path: /transit/dynamicSearchWindow

Used to set the steps the search-window is rounded to.

The search window is rounded off to the closest multiplication of stepMinutes. If stepMinutes = 10 minutes, the search-window can be 10, 20, 30 ... minutes. It the computed search-window is 5 minutes and 17 seconds it will be rounded up to 10 minutes.

Use a value between 1 and 60. This should be less than the min-raptor-search-window coefficient.

pagingSearchWindowAdjustments

Since version: naType: duration[]Cardinality: Optional
Path: /transit

The provided array of durations is used to increase the search-window for the next/previous page.

The search window is expanded when the current page return few options. If ZERO result is returned the first duration in the list is used, if ONE result is returned then the second duration is used and so on. The duration is added to the existing search-window and inserted into the next and previous page cursor. See JavaDoc for TransitTuningParameters#pagingSearchWindowAdjustments" + for more info."

stopTransferCost

Since version: 2.0Type: enum map of integerCardinality: Optional
Path: /transit
Enum keys: discouraged | allowed | recommended | preferred

Use this to set a stop transfer cost for the given transfer priority

The cost is applied to boarding and alighting at all stops. All stops have a transfer cost priority set, the default is allowed. The stopTransferCost parameter is optional, but if listed all values must be set.

If not set the stopTransferCost is ignored. This is only available for NeTEx imported Stops.

The cost is a scalar, but is equivalent to the felt cost of riding a transit trip for 1 second.

Config key Description Type
discouraged Use a very high cost like 72 000 to eliminate transfers at the stop if not the only option. int
allowed Allowed, but not recommended. Use something like 150. int
recommended Use a small cost penalty like 60. int
preferred The best place to do transfers. Should be set to 0(zero). int

Use values in a range from 0 to 100 000. All key/value pairs are required if the stopTransferCost is listed.

transferCacheRequests

Since version: 2.3Type: object[]Cardinality: Optional
Path: /transit ∙ See: RouteRequest.md

Routing requests to use for pre-filling the stop-to-stop transfer cache.

If not set, the default behavior is to cache stop-to-stop transfers using the default route request (routingDefaults). Use this to change the default or specify more than one RouteRequest.

Example

// router-config.json
{
  "transit": {
    "transferCacheRequests": [
      { "modes": "WALK"                                                     },
      { "modes": "WALK",    "wheelchairAccessibility": { "enabled": true  } }
    ]
  }
}

hideFeedId

Since version: naType: booleanCardinality: OptionalDefault value: false
Path: /transmodelApi

Hide the FeedId in all API output, and add it to input.

Only turn this feature on if you have unique ids across all feeds, without the feedId prefix.

tracingHeaderTags

Since version: naType: string[]Cardinality: Optional
Path: /transmodelApi

Used to group requests when monitoring OTP.

headers

Since version: naType: map of stringCardinality: Optional
Path: /vehicleRentalServiceDirectory

HTTP headers to add to the request. Any header key, value can be inserted.

Router Config Example

// router-config.json
{
  "configVersion" : "v2.4.0-EN000121",
  "server" : {
    "apiProcessingTimeout" : "7s",
    "traceParameters" : [
      {
        "httpRequestHeader" : "X-Correlation-ID",
        "httpResponseHeader" : "X-Correlation-ID",
        "logKey" : "correlationId",
        "generateIdIfMissing" : true
      }
    ]
  },
  "routingDefaults" : {
    "walkSpeed" : 1.3,
    "bikeSpeed" : 5,
    "carSpeed" : 40,
    "numItineraries" : 12,
    "transferPenalty" : 0,
    "walkReluctance" : 4.0,
    "bikeReluctance" : 5.0,
    "bikeWalkingReluctance" : 10.0,
    "bikeStairsReluctance" : 150.0,
    "carReluctance" : 10.0,
    "stairsReluctance" : 1.65,
    "turnReluctance" : 1.0,
    "elevatorBoardTime" : 90,
    "elevatorBoardCost" : 90,
    "elevatorHopTime" : 20,
    "elevatorHopCost" : 20,
    "escalatorReluctance" : 1.5,
    "vehicleRental" : {
      "pickupCost" : 120,
      "dropOffTime" : 30,
      "dropOffCost" : 30
    },
    "bikeParkTime" : 60,
    "bikeParkCost" : 120,
    "carDropoffTime" : 120,
    "waitReluctance" : 1.0,
    "walkBoardCost" : 600,
    "bikeBoardCost" : 600,
    "otherThanPreferredRoutesPenalty" : 300,
    "transferSlack" : 120,
    "boardSlackForMode" : {
      "AIRPLANE" : "35m"
    },
    "alightSlackForMode" : {
      "AIRPLANE" : "15m"
    },
    "transitReluctanceForMode" : {
      "RAIL" : 0.85
    },
    "accessEgress" : {
      "maxDuration" : "45m",
      "maxDurationForMode" : {
        "BIKE_RENTAL" : "20m"
      },
      "maxStopCount" : 500,
      "penalty" : {
        "FLEXIBLE" : {
          "timePenalty" : "2m + 1.1t",
          "costFactor" : 1.7
        }
      }
    },
    "itineraryFilters" : {
      "transitGeneralizedCostLimit" : {
        "costLimitFunction" : "15m + 1.5 x",
        "intervalRelaxFactor" : 0.4
      },
      "bikeRentalDistanceRatio" : 0.3,
      "accessibilityScore" : true,
      "minBikeParkingDistance" : 300,
      "debug" : "limit-to-search-window"
    },
    "carDecelerationSpeed" : 2.9,
    "carAccelerationSpeed" : 2.9,
    "ignoreRealtimeUpdates" : false,
    "geoidElevation" : false,
    "maxJourneyDuration" : "36h",
    "unpreferred" : {
      "agencies" : [
        "HSL:123"
      ],
      "routes" : [
        "HSL:456"
      ]
    },
    "unpreferredCost" : "10m + 2.0 x",
    "streetRoutingTimeout" : "5s",
    "transferOptimization" : {
      "optimizeTransferWaitTime" : true,
      "minSafeWaitTimeFactor" : 5.0,
      "backTravelWaitTimeFactor" : 1.0,
      "extraStopBoardAlightCostsFactor" : 8.0
    },
    "wheelchairAccessibility" : {
      "trip" : {
        "onlyConsiderAccessible" : false,
        "unknownCost" : 600,
        "inaccessibleCost" : 3600
      },
      "stop" : {
        "onlyConsiderAccessible" : false,
        "unknownCost" : 600,
        "inaccessibleCost" : 3600
      },
      "elevator" : {
        "onlyConsiderAccessible" : false,
        "unknownCost" : 20,
        "inaccessibleCost" : 3600
      },
      "inaccessibleStreetReluctance" : 25,
      "maxSlope" : 0.083,
      "slopeExceededReluctance" : 1,
      "stairsReluctance" : 100
    }
  },
  "flex" : {
    "maxTransferDuration" : "5m",
    "maxFlexTripDuration" : "45m",
    "maxAccessWalkDuration" : "15m",
    "maxEgressWalkDuration" : "15m"
  },
  "transit" : {
    "maxNumberOfTransfers" : 12,
    "dynamicSearchWindow" : {
      "minTransitTimeCoefficient" : 0.5,
      "minWaitTimeCoefficient" : 0.5,
      "minWindow" : "1h",
      "maxWindow" : "5h"
    },
    "stopTransferCost" : {
      "DISCOURAGED" : 1500,
      "ALLOWED" : 75,
      "RECOMMENDED" : 30,
      "PREFERRED" : 0
    },
    "transferCacheRequests" : [
      {
        "modes" : "WALK"
      },
      {
        "modes" : "WALK",
        "wheelchairAccessibility" : {
          "enabled" : true
        }
      }
    ]
  },
  "vehicleRentalServiceDirectory" : {
    "url" : "https://entur.no/bikeRentalServiceDirectory",
    "sourcesName" : "systems",
    "updaterUrlName" : "url",
    "updaterNetworkName" : "id",
    "headers" : {
      "ET-Client-Name" : "MY_ORG_CLIENT_NAME"
    }
  },
  "transmodelApi" : {
    "hideFeedId" : true
  },
  "vectorTileLayers" : [
    {
      "name" : "stops",
      "type" : "Stop",
      "mapper" : "Digitransit",
      "maxZoom" : 20,
      "minZoom" : 14,
      "cacheMaxSeconds" : 600
    },
    {
      "name" : "stations",
      "type" : "Station",
      "mapper" : "Digitransit",
      "maxZoom" : 20,
      "minZoom" : 12,
      "cacheMaxSeconds" : 600
    },
    {
      "name" : "rentalPlaces",
      "type" : "VehicleRental",
      "mapper" : "Digitransit",
      "maxZoom" : 20,
      "minZoom" : 14,
      "cacheMaxSeconds" : 60,
      "expansionFactor" : 0.25
    },
    {
      "name" : "rentalVehicle",
      "type" : "VehicleRentalVehicle",
      "mapper" : "Digitransit",
      "maxZoom" : 20,
      "minZoom" : 14,
      "cacheMaxSeconds" : 60
    },
    {
      "name" : "rentalStation",
      "type" : "VehicleRentalStation",
      "mapper" : "Digitransit",
      "maxZoom" : 20,
      "minZoom" : 14,
      "cacheMaxSeconds" : 600
    },
    {
      "name" : "vehicleParking",
      "type" : "VehicleParking",
      "mapper" : "Digitransit",
      "maxZoom" : 20,
      "minZoom" : 14,
      "cacheMaxSeconds" : 60,
      "expansionFactor" : 0.25
    }
  ],
  "timetableUpdates" : {
    "purgeExpiredData" : false,
    "maxSnapshotFrequency" : "2s"
  },
  "updaters" : [
    {
      "type" : "real-time-alerts",
      "frequency" : "30s",
      "url" : "http://developer.trimet.org/ws/V1/FeedSpecAlerts/appID/0123456789ABCDEF",
      "feedId" : "TriMet",
      "headers" : {
        "Some-Header" : "A-Value"
      }
    },
    {
      "type" : "vehicle-rental",
      "network" : "socialbicycles_coast",
      "sourceType" : "gbfs",
      "language" : "en",
      "frequency" : "1m",
      "allowKeepingRentedVehicleAtDestination" : false,
      "geofencingZones" : false,
      "url" : "http://coast.socialbicycles.com/opendata/gbfs.json",
      "headers" : {
        "Auth" : "<any-token>",
        "<key>" : "<value>"
      }
    },
    {
      "type" : "vehicle-parking",
      "sourceType" : "hsl-park",
      "feedId" : "hslpark",
      "timeZone" : "Europe/Helsinki",
      "facilitiesFrequencySec" : 3600,
      "facilitiesUrl" : "https://p.hsl.fi/api/v1/facilities.json?limit=-1",
      "utilizationsFrequencySec" : 600,
      "utilizationsUrl" : "https://p.hsl.fi/api/v1/utilizations.json?limit=-1",
      "hubsUrl" : "https://p.hsl.fi/api/v1/hubs.json?limit=-1"
    },
    {
      "type" : "vehicle-parking",
      "sourceType" : "park-api",
      "feedId" : "parkapi",
      "timeZone" : "Europe/Berlin",
      "frequency" : "10m",
      "url" : "https://foo.bar",
      "headers" : {
        "Cache-Control" : "max-age=604800"
      },
      "tags" : [
        "source:parkapi"
      ]
    },
    {
      "type" : "vehicle-parking",
      "feedId" : "bikely",
      "sourceType" : "bikely",
      "url" : "https://api.safebikely.com/api/v1/s/locations",
      "headers" : {
        "X-Bikely-Token" : "${BIKELY_TOKEN}",
        "Authorization" : "${BIKELY_AUTHORIZATION}"
      }
    },
    {
      "type" : "stop-time-updater",
      "frequency" : "1m",
      "backwardsDelayPropagationType" : "REQUIRED_NO_DATA",
      "url" : "http://developer.trimet.org/ws/V1/TripUpdate/appID/0123456789ABCDEF",
      "feedId" : "TriMet",
      "headers" : {
        "Authorization" : "A-Token"
      }
    },
    {
      "type" : "vehicle-positions",
      "url" : "https://s3.amazonaws.com/kcm-alerts-realtime-prod/vehiclepositions.pb",
      "feedId" : "1",
      "frequency" : "1m",
      "headers" : {
        "Header-Name" : "Header-Value"
      }
    },
    {
      "type" : "websocket-gtfs-rt-updater"
    },
    {
      "type" : "siri-et-updater",
      "url" : "https://example.com/some/path",
      "feedId" : "feed_id",
      "timeout" : "30s",
      "headers" : {
        "Authorization" : "Some-Token"
      }
    },
    {
      "type" : "siri-sx-updater",
      "url" : "https://example.com/some/path",
      "feedId" : "feed_id",
      "timeout" : "30s",
      "headers" : {
        "Key" : "Value"
      }
    },
    {
      "type" : "siri-azure-sx-updater",
      "topic" : "some_topic",
      "servicebus-url" : "service_bus_url",
      "feedId" : "feed_id",
      "customMidnight" : 4,
      "history" : {
        "url" : "endpoint_url",
        "fromDateTime" : "-P1D",
        "toDateTime" : "P1D",
        "timeout" : 300000
      }
    }
  ],
  "rideHailingServices" : [
    {
      "type" : "uber-car-hailing",
      "clientId" : "secret-id",
      "clientSecret" : "very-secret",
      "wheelchairAccessibleProductId" : "545de0c4-659f-49c6-be65-0d5e448dffd5",
      "bannedProductIds" : [
        "1196d0dd-423b-4a81-a1d8-615367d3a365",
        "f58761e5-8dd5-4940-a472-872f1236c596"
      ]
    }
  ]
}