# Google Transit ticketing extensions

This page defines a subset of the GTFS-Ticketing specification, which is in the proposal stage
during the creation of this document. This page also includes several additional restrictions from
Google.

## Requirements

The field `departure_time` in `stop_times.txt` is necessary.

## Additional field types

This section outlines the additional field types and those with extensions from Google. Some
fields require values in the form of a URI.

- **URI** : A fully qualified Uniform Resource Identifier (URI) that includes the scheme. Any special characters in the URI must use the correct escapes. For a description of how to create fully qualified URI values, see [RFC 3986 Uniform Resource Identifier (URI): Generic Syntax](https://tools.ietf.org/html/rfc3986).

## File extensions or additions

The text files with the state **Extension** are files that already exist and that you need to
extend. You must add the new fields that we define in the following table.

The text files with the state **Addition** are new files introduced by the Google Transit
Ticketing Extensions. You need to create these files in accordance with the
[Field definitions](https://developers.google.com/transit/gtfs/reference/google-transit-ticketing-extension#field-definitions) given after the following table. Be sure to
include these files in your feed.

| File name | State | Defines |
|---|---|---|
| `agency.txt` | Extension | Adds `agency.ticketing_deep_link_id`. |
| `routes.txt` | Extension | Adds `routes.ticketing_deep_link_id`. |
| `trips.txt` | Extension | Adds `trips.ticketing_trip_id` and `trips.ticketing_type`. |
| `stop_times.txt` | Extension | Adds `stop_times.ticketing_type`. |
| `ticketing_identifiers.txt` | Addition | New file. For more details, see [Field definitions](https://developers.google.com/transit/gtfs/reference/google-transit-ticketing-extension#field-definitions). |
| `ticketing_deep_links.txt` | Addition | New file. For more details, see [Field definitions](https://developers.google.com/transit/gtfs/reference/google-transit-ticketing-extension#field-definitions). |

## Field definitions

`agency.txt` (file extended)

| Field name | Presence | Details |
|---|---|---|
| `ticketing_deep_link_id` | Optional | (ID from `ticketing_deep_links.txt`) Defines the deep link to use for this agency. Can be overwritten in `routes.txt`. > [!NOTE] > **Note:** To apply ticketing to the whole static feed, set the same value in the `ticketing_deep_link_id` field for all agencies in the `agency.txt` file. |

`routes.txt` (file extended)

| Field name | Presence | Details |
|---|---|---|
| `ticketing_deep_link_id` | Optional | (ID from `ticketing_deep_links.txt`) Defines the deep link to use for this route. If present, overwrites the field in `agency.txt`. |

`trips.txt` (file extended)

| Field name | Presence | Details |
|---|---|---|
| `ticketing_trip_id` | Optional | (ID) Identifier to pass in the deep link. Doesn't have to be unique. If `ticketing_trip_id` is empty, the system uses the value of `trip_id` instead. |
| `ticketing_type` | Optional | (Enum) Define whether ticketing through a deep link is available for this trip: - `0` or empty: - If you define a `ticketing_deep_link_id` for the route or agency, this is available. - Unavailable otherwise. - `1`: Unavailable. |

`stop_times.txt` (file extended)

| Field name | Presence | Details |
|---|---|---|
| `ticketing_type` | Optional | (Enum) Defines whether ticketing through a deep link is available for this stop time. If you define `stop_times.ticketing_type`, this overrides the value set in `trip.ticketing_type`. - Empty: Uses the value from `trip.ticketing_type`. - `0`: - If you define a `ticketing_deep_link_id` for the route or agency, this is available. - Unavailable otherwise. - `1`: Unavailable. > [!IMPORTANT] > **Important** : For a particular `stop_id`, if any of its `stop_times` has `ticketing_type=1`, then all of its other `stop_times` must also have `ticketing_type=1`. |

`ticketing_identifiers.txt` (file added)

The most common case for ticketing is that every stop has an identifier to process billing.
Different agencies might use the same identifier for any given stop. When multiple agencies serve
the same stop, repeat this mapping for each agency.

| Field name | Presence | Details |
|---|---|---|
| `ticketing_stop_id` | **Required** | (ID) Defines a default ticketing ID for this stop for this agency. |
| `stop_id` | **Required** | (ID from `stops.txt`) The stop for which the default `ticketing_stop_id` is defined. |
| `agency_id` | **Required** | (ID from `agency.txt`) The agency of the stop for which the default `ticketing_stop_id` is defined. |

`ticketing_deep_links.txt` (file added)

| Field name | Presence | Details |
|---|---|---|
| `ticketing_deep_link_id` | **Required** | (ID) Defines an ID for the deep link. |
| `web_url` | Optional | (URL) The URL to visit for deep linking. This field isn't translatable through `translations.txt`. > [!IMPORTANT] > **Important:** There's no support for URL translation for this field, so language detection must be done by the receiving website or app. |
| `android_intent_uri` | Optional | (URI) The URI to pass to a native Android app with an `android.intent.action.VIEW` intent. If empty, it means you can't deep link to a native Android app. > [!IMPORTANT] > **Important:** There's no support for URL translation for this field, so language detection must be done by the receiving website or app. For more information about deep links on Android, see [Create deep links to app content](https://developer.android.com/training/app-links/deep-linking). > [!NOTE] > **Recommendation:** Use Android App Links, which are both more secure and have a better fallback behavior when the passenger doesn't have the native Android app on their device. This field isn't translatable through `translations.txt`. |
| `ios_universal_link_url` | Optional | (URL) The universal link URL to invoke on iOS. > [!IMPORTANT] > **Important:** There's no support for URL translation for this field, so language detection must be done by the receiving website or app. If empty, deep linking doesn't display on iOS. For more information, see [Universal Links on iOS](https://developer.apple.com/ios/universal-links/). This field isn't translatable through `translations.txt`. |

> [!NOTE]
> **Note:** When multiple agencies or routes share the same deep link URLs in the feed, *we strongly recommend* that you define only one deep link and link all those agencies or routes to the same deep link. For more information, see the [Recommended practices](https://developers.google.com/transit/gtfs/reference/google-transit-ticketing-extension#recommended-practices) section.

## Field placeholders in the API call

Google calls the URLs defined in `ticketing_deep_links.txt` with the following
parameters:

| Field name | Details |
|---|---|
| `service_date` | (Date, repeatable) Service day of the trip. Use this field for the date when the trip departs from its first stop. This field formats as a JSON array. |
| `ticketing_trip_id` | (ID from `trips.txt`, repeatable) Identifier of the trip. This field formats as a JSON array. |
| `from_ticketing_stop_time_id` | (Repeatable) Identifier of the `stop_time` at which the leg starts. For a particular stop time, the value is the following: - The `ticketing_stop_id` defined in `ticketing_identifiers.txt` for the `agency_id` and the `stop_id` of the `stop_time`. > [!NOTE] > **Important:** This only uses the `ticketing_identifiers` for the stop itself. Even if the stop has a parent stop, we ignore the `ticketing_identifiers` of the parent stop. - Else, it falls back on the `stop_sequence`. This field formats as a JSON array. |
| `` `to_ticketing_stop_time_id` `` | (Repeatable) Identifier of the `stop_time` at which the leg ends. For an explanation on the derivation of the value, see `from_ticketing_stop_time_id`. This field formats as a JSON array. |
| `boarding_time` | (ISO 8601, repeatable) Time of departure, `departure_time`, of the `stop_time` at which the leg starts. Use this field for the actual date and time when the passenger boards the vehicle. The time value of this field conforms to [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) with the following string format: `YYYY-MM-DDThh:mm:ss±hh:mm` All the times below are the same, in different time zones: - In London, UK: `2019-07-29T18:26:00+01:00` - In NYC, US-NY: `2019-07-29T13:26:00-04:00` - In Tokyo, JP: `2019-07-30T02:26:00+09:00` > [!IMPORTANT] > **Important:** The time zone isn't necessarily the same time zone for the agency in the feed. The time zone is in UTC. This field formats as a JSON array. |
| `arrival_time` | (ISO 8601, repeatable) Time of arrival, arrival_time at the stop_time at which the leg ends. The time value of this field conforms to [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) with the following string format: `YYYY-MM-DDThh:mm:ss±,hh:mm` All the times below are the same, in different time zones: - In London, UK: 2019-07-29T18:46:00+01:00 - In NYC, US-NY: 2019-07-29T13:46:00-04:00 - In Tokyo, JP: 2019-07-30T02:46:00+09:00 **Important:** The time zone isn't necessarily the same time zone for the agency in the feed. The time zone is in UTC. This field formats as a JSON array. |

## Examples

In this example, the following are the legs of a passenger's journey:

- On service date `20190716`, trip ticketing ID `ti1` begins from stop time ticketing ID `11` to stop time ticketing ID `12`. The passenger boards at 2:00 PM UTC and arrives at 2:50 PM UTC.
- On service date `20190716`, trip ticketing ID `ti2` begins from stop time ticketing ID `21` to stop time ticketing ID `22`. The passenger boards at 3:00 PM UTC and arrives at 3:50 PM UTC.

For this example, the `web_url` is `https://examplepetstore.com`.

Given all of this information, use the following values for the parameters of the feed for this
trip:

| Field name | Details |
|---|---|
| `service_date` | `["20190716"`, `"20190716"]` |
| `ticketing_trip_id` | `["ti1"`, `"ti2"]` |
| `from_ticketing_stop_time_id` | `["11"`, `"21"]` |
| `to_ticketing_stop_time_id` | `["12"`, `"22"]` |
| `boarding_time` | `["2019-07-16T14:00:00+00:00"`, `"2019-07-16T15:00:00+00:00"]` |
| `arrival_time` | `["2019-07-16T14:50:00+00:00"`, `"2019-07-16T15:50:00+00:00"]` |

The following is the final URI after encoding:
`
https://examplepetstore.com?service_date=%5B%2220190716%22,%2220190716%22%5D
&ticketing_trip_id=%5B%22ti1%22,%22ti2%22%5D&from_ticketing_stop_time_id=%5B%2211%22,%2221%22%5D
&to_ticketing_stop_time_id=%5B%2212%22,%2222%22%5D&boarding_time=%5B%222019-07-16T14:00:00%2B00:00
%22,%222019-07-16T15:00:00%2B00:00%22%5D&arrival_time=%5B%222019-07-16T14:50:00%2B00:00 %22,%222019-07-16T15:50:00%2B00:00%22%5D`

## Examples of different
`ticketing_stop_id` values

Stops can have different IDs for the purposes of ticketing with the
`ticketing_identifiers.txt` field. Values in bold are those found in the web call
described after the files.

| `stop.txt` |
|---|
| `stop_id`,`stop_name` `si1`,"Paris Gare-de-Lyon" `si2`,"Lyon Part-Dieu" |

| `routes.txt` |
|---|
| `route_id`,`route_long_name`,`ticketing_deep_link_id` `ri1`,"TGV inOui Paris-Lyon",`tdl1` |

| `trips.txt` |
|---|
| `trip_id`,`service_id`,`route_id`,`trip_short_name`, `ticketing_trip_id` ti1,everyday,ri1,"TGV INOUI 6603",**FR_SNCF_6603** ti2,everyday,ri1,"TGV INOUI 6681",FR_SNCF_6681 ti3,everyday,ri1,"TGV INOUI 6607",FR_SNCF_6607 |

| `stop_times.txt` |
|---|
| `trip_id`,`stop_sequence`,`stop_id`,`arrival_time`, `departure_time` ti1,1,si1,06:59:00,**06:59:00** ti1,2,si2,08:56:00,**08:56:00** ti2,1,si1,07:53:00,07:53:00 ti2,2,si2,10:00:00,10:00:00 ti3,1,si1,08:59:00,08:59:00 ti3,2,si2,10:56:00,10:56:00 |

| `ticketing_identifiers.txt` |
|---|
| `stop_id`,`agency_id`,`ticketing_stop_id` si1,agency1,**4924** si2,agency1,**4676** |

| `ticketing_deep_links.txt` |
|---|
| `ticketing_deep_link_id`, `web_url`, `android_intent_uri`, `ios_universal_link_url` tdl1, https://examplepetstore.com/api/gtfs/web, https://examplepetstore.com/api/gtfs/android, https://examplepetstore.com/api/gtfs/ios |

If the request is made on 2019-07-19 and the GTFS times are in time zone UTC+1, then the
following is the call for the web:
`https://examplepetstore.com/api/gtfs/web?service_date=%5B%2220190719%22%5D&ticketing_trip_id=
%5B%22FR_SNCF_6603%22%5D&from_ticketing_stop_time_id=%5B%224924%22%5D&to_ticketing_stop_time_id=
%5B%224676%22%5D&boarding_time=%5B%222019-07-19T05:59:00%2B00:00%22%5&arrival_time=%5B%222019-07-19T07:56:00%2B00:00%22%5D`

> [!IMPORTANT]
> **Important:** Note that the values inside `boarding_time` and `arrival_time`, which are in UTC, don't necessarily follow the time zone in GTFS (UTC+1), which causes a shift in the `HH:MM:SS` value.

## Recommended practices

Follow these recommendations to implement Google Transit ticketing extensions:

| Guidelines ||
|---|---|
| Share deep links between agencies or routes whenever possible. | In the static feed, whenever multiple agencies or routes have the same deep link URLs, they must share a single value for the `ticketing_deep_link_id` field. This enables transfers between agencies or routes. > [!NOTE] > **Note:** If not, passengers must buy two separate tickets due to different values in `ticketing_deep_link_id`. |
| Set consistent values for `ticketing_type`. | The `ticketing_type` value for a particular stop must be consistent across all the rows in the `stop_times.txt` file. > [!NOTE] > **Note:** A `ticketing_type` value of `1` disables ticketing for all trips that use that given stop. |
| Map `ticketing_stop_id` for both parent and child stops. | Perform the mapping for all the parent and child stops that require ticketing. In the `ticketing_identifiers.txt` file, the `ticketing_stop_id` values don't propagate between a parent stop and its children. |
| Map `ticketing_stop_id` for each agency that uses the same stop in the feed. | In the static feed, if multiple agencies that enable ticketing share the same stop, the mapping must be present for each of the agencies. For more details, refer to the [Field definitions](https://developers.google.com/transit/gtfs/reference/google-transit-ticketing-extension#field-definitions) section for `ticketing_identifiers.txt`. |
| Use Android App Links when a deep link to an Android app is required. | If the partner wants to open up an Android app from a deep link, set up the deep link as an Android App Link. > [!NOTE] > **Note:** If the partner only wants to open a mobile-enabled website, use the website URL directly. |
| Use iOS Universal Links when a deep link to an iOS app is required. | If the partner wants to open up an iOS app from a deep link, set up the deep link as an iOS Universal Link. |