Docs · Editor panels · Flex zones & booking rules

Authoring GTFS-Flex with GTFS·X

GTFS-Flex is an optional extension to the baseline GTFS specification that makes demand-responsive service — dial-a-ride, microtransit, deviated fixed-route — discoverable in trip planners. If you're new to the extension, start with the What is GTFS-Flex? primer. This page is the authoring guide: the Flex Zones panel lets you draw polygon zones, configure booking rules in a form, and validate before export.

GTFS·X editor with the Flex Zones panel selected.
The Flex Zones panel. Three zone-creation methods are available — draw a polygon, group existing stops, or buffer existing fixed routes.

When to use it

Flex applies any time service isn't a fixed route running on a fixed schedule:

Three ways to create a zone

Flex Zones panel showing the three zone-creation options: Draw Zone on Map, Create Stop Group, and Auto-generate from fixed routes with a buffer distance input.
The three zone-creation methods. Auto-generate has a configurable buffer (defaults to 0.75 mi); each method then routes you into the same Service Details configuration.

Click + Add flex zone, then pick one of the three paths under Create new flex zone:

Creating a zone also creates its paired route (route_type 715). You never add the route yourself.

Configuring a zone

Click Details on a zone to open its configuration. The fields, by section:

Continuous pickup / drop-off is not a zone field: it is set on the route (Routes panel) or per stop_time, and applies to deviated fixed-route hybrids where riders flag the bus down along a corridor.

Microtransit

Microtransit (an on-demand zone a rider books into, with no fixed stops and no fixed times) is the simplest thing Flex models. In GTFS·X it is one flex zone, one trip carrying a pickup/drop-off window, and one booking rule. Because a rider gets on and off anywhere inside the same area, the trip's stop_times reference that one zone twice: the spec requires two records for travel within a single location, so a one-row flex trip is invalid.

Each thing you fill in maps to one file in the exported feed:

In the editorWhat it writes
Draw the zone (and name it) locations.geojson: one Feature for the zone, with the zone id at the top level of the Feature and only stop_name / stop_desc in properties. Several polygons in one zone merge into a single MultiPolygon Feature.
Pickup start / Pickup end stop_times.txt: start_pickup_drop_off_window and end_pickup_drop_off_window on both rows of the flex trip.
Service pattern trips.txt: the flex trip's service_id.
Booking fields booking_rules.txt: one row for the zone, referenced from the stop_times rows via pickup_booking_rule_id / drop_off_booking_rule_id.
Travel-time estimation (advanced) trips.txt: safe_duration_factor / safe_duration_offset (these are trips.txt fields, not stop_times fields).
The paired route (created for you) routes.txt: route_type 715, Demand and Response Bus Service.

Adding a microtransit zone to an existing feed

  1. Confirm demand response is enabled. Settings in the left rail opens Feature settings; the Demand response / paratransit card is on by default. If someone turned it off, turn it back on: while it's off, the Flex Zones panel is hidden.
  2. Open Flex Zones in the left rail. The panel is Flex Zones & Rules, and existing zones are listed under Service areas.
  3. Click + Add flex zone, then Draw Zone on Map. The map banner reads "Drawing Flex Zone — Click to add vertices, double-click to close polygon." Click once per corner of the service area and double-click to place the last vertex and close the polygon. The zone appears in the list, and its paired route is created at the same time.
  4. Name the zone. New zones arrive as "Zone 2", "Zone 3", and so on. Click the name, type the rider-facing name (for example "East Side Microtransit"), and press Enter. The paired route's short name follows the zone name, so renaming here renames both.
  5. Click Details and set the service window. Under Service schedule, enter Pickup start (say 08:00:00) and Pickup end (say 18:00:00). These are free-text HH:MM:SS fields, and they are what makes the zone exportable.
  6. Pick the service pattern. Choose the calendar the zone runs on (weekdays, weekends, a dates-only service). The flex trip is written against that service_id.
  7. Fill in the booking rule. Under Booking, pick Real-time, Same-day, or Prior day. Same-day exposes Min minutes ahead (60, for "call an hour ahead"); Prior day exposes a day/time cutoff. Then set the booking phone, booking URL, info URL, and the rider message riders will actually read ("Call at least 1 hour before your pickup"). The phone and info URL are pre-filled from your agency record the first time you touch this section.
  8. Check the paired route. Open the Routes panel: the zone's route is in the Flex section, and its Route type reads Demand and Response Bus Service (route_type 715). Nothing to change, but the route color you set here is the color the zone is drawn in on the map.
  9. Validate. Open Validation in the bottom bar and clear anything flagged. Errors block export.
  10. Export GTFS. The export dialog's file list should now include locations.geojson (with your zone counted) and booking_rules.txt.
The Flex Zones panel with a zone's Details open: pickup start and end times, a service pattern dropdown, and the booking section with booking type, minutes of notice, phone, URL and rider message.
A microtransit zone's Details: the pickup window, the service pattern, and the booking rule. Booking phone and info URL are seeded from your agency record.
The Routes panel showing the flex zone's paired route with route type set to Demand and Response Bus Service, and the zone drawn on the map.
The paired route, created with the zone. Route type is Demand and Response Bus Service (route_type 715).

What to look for in the zip

Open the exported zip and check four things:

trip_id,stop_sequence,location_id,start_pickup_drop_off_window,end_pickup_drop_off_window,pickup_type,drop_off_type,pickup_booking_rule_id,drop_off_booking_rule_id
flex-zone-1752345678901-trip,1,flex-zone-1752345678901,08:00:00,18:00:00,2,2,flex-zone-1752345678901-booking,flex-zone-1752345678901-booking
flex-zone-1752345678901-trip,2,flex-zone-1752345678901,08:00:00,18:00:00,2,2,flex-zone-1752345678901-booking,flex-zone-1752345678901-booking

What Flex does not cover

A flex feed is a static description of where the service runs, when it runs, and how to book it. It is not a booking API. Publishing one does not make your service bookable from a trip planner, and it says nothing about what is happening right now:

Live availability and in-app booking are the domain of GOFS and GTFS-OnDemand, which are separate specifications from GTFS-Flex. Set expectations accordingly when you tell riders the service is "in the app": what they will see is the zone, the hours, the fare, and how to call.

Paratransit and microtransit

GTFS does not distinguish between them. The words "paratransit" and "microtransit" appear nowhere in the spec, whose only vocabulary for this is "demand-responsive". Both are encoded exactly the same way: a locations.geojson zone, stop_times rows carrying a pickup/drop-off window instead of times, a booking_rules.txt entry, and route_type 715. There is no paratransit-specific route type, and no flag anywhere that marks a zone as one or the other. That is why the feature setting is called Demand response / paratransit and this page calls it microtransit: they are the same machinery.

What differs is convention, not fields:

ParatransitMicrotransit
Booking type Usually Prior day (book by a cutoff the day before) Usually Real-time or Same-day
Zone shape Usually the ADA corridor around the fixed routes Usually a free-standing service area
Fare Often a separate, higher fare Usually the standard fare

If you are modeling ADA complementary paratransit, Auto-generate from fixed routes is the path you want: its default buffer of 0.75 miles is the three-quarter-mile corridor the ADA requires around each fixed route, so the generated zone is the corridor.

The one thing to get right: rider eligibility is not machine-readable. Paratransit is restricted to certified riders; microtransit is open to everyone. GTFS has no field for that distinction anywhere, so a trip planner consuming your feed cannot tell that a paratransit zone is closed to the general public, and may route an ordinary rider into it. (rider_categories.txt and its eligibility_url are a Fares v2 concept about who qualifies for a fare, such as a senior discount. They do not say who is permitted to board.)

The only place to state the restriction is prose that riders read and machines ignore. Put it in the booking rule's Rider message (or the pickup / drop-off message), and in the route description, in plain terms: who is eligible, and how to get certified. Assume nothing downstream enforces it.

Map popups and editing

Click a zone on the map to get a popup with quick links to Edit Route (every flex zone has an automatically-paired route that appears in trip planners) and Edit Service Details. Zone fill color tracks the linked route's color, so styling a zone consistently is a matter of styling its route.

What gets exported

Flex export produces multiple files depending on the zone types:

Zones missing a pickup window are skipped from the output entirely: no geometry, no booking rule, no trip. Give every zone a window before publishing, since a flex zone without one won't function in trip planners.

Edge cases and gotchas

See also