NAV
shell

Introduction

Welcome to the Token Transit Ticketing API.

The Token Transit Ticketing API is a RESTful service that can be used to purchase transit passes on behalf of an authenticated users in third-party applications and display the resulting tickets for transit agencies that Token Transit works with. Where possible the API uses understandable resource identifiers, JSON-encoded responses, and well-known HTML response and error codes.

In addition to the live API, you may also access our test API by using an test API key.

Versions

The current version of the Token Transit API is 2020-05-21. The API version is updated when there are breaking changes to the API. These changes are recorded below.

Changes in Token Transit API versions:

Version Changes
2020-05-21 Fare Free improvements to the API. The Agency object now includes service_alerts and the /agency/{agency_id}/fare endpoint will return an empty list of fares if the agency is currently fare free and fares cannot be purchased.
2019-05-01 Initial release of the Token Transit API.

Headers

The Token Transit Ticketing API uses a number of common headers across the API. Some requests also require specific headers that will be mentioned separately. These should be included in each request.

Cookies used are described in the Cookies section.

List

This is a list of all headers and the endpoints for which they are useful.

Request Headers

These are all of the headers that you may need to include with requests

Header Endpoints Default Description
Token-Transit-Api-Key all N/A (Required) Authentication that also identifies your service and access
Token-Transit-Api-Version all N/A (Required) Describes which version of the API you wish to use. This document describes version
Token-Transit-Passes-State Purchase and Activate (but safe to add to any call) N/A (Required on Purchase or Activate) Tells the server only to perform the action if the client account state is up to date. This is required for purchase endpoint /purchase and always required for the activate endpoint /pass/{pass_id}/activate. You should read this header from calls to purchase, activate, or list passes and send it back in the purchase and activate calls.
Accept-Language Fares, Agencies en-US User language for requesting results in a different language
Authentication All Pass Enpoints, Login N/A (Ignored/optional if user_session_id cookie is provided) A JWT signed by your service authenticating the user
Idempotency-Key Purchase N/A (Highly Recommended!) A key to indicate a request is a duplicate (retry) of a previous request to prevent duplicate transactions

Response headers

This is a list of important headers that might be sent back across multiple API requests.

Header Endpoints Description
Content-Type all The encoding of the response (usually application/json)
Retry-After stateful endpoints upon a 503 failure How many seconds to wait until retrying a request. A value of 0 means that the request may be retried immediately.
Access-Control-Allow-Origin all CORS is enabled on a per-developer basis. Contact Token Transit for information on enabling CORS for your platform.
Token-Transit-Passes-State Purchase, Activate, List Passes endpoints The latest version of user pass state. This should always be sent back to the server using the same Token-Transit-Passes-State header on purchase and activate endpoints. It can always be fetched by calling the list passes endpoint.

Cookies

These are all of the cookies that are used in the API.

Cookie Name Endpoints Description
user_session_id Purchase, Pass Retrieval, Login, Logout A session id that can be used instead of a JWT to authenticate a user.

API Authentication

Token Transit Ticketing API uses API keys for authentication of your service. If you are integrating with us, your publishable and secret test and live API keys will be sent to you during the integration process.

To authenticate with Token Transit, you must send your API key as the Token-Transit-API-Key HTTP header like the following example:

Token-Transit-API-Key: sk_fake_1234567890abcdef

API keys have two components: key type and environment.

Key Types

There are two types of keys: publishable (publishable) and secret (secret). Publishable keys may be freely used in your app. Publishable API keys are indicated with a pk prefix to all keys. An API key has

Key Type Key Prefix Access Level Allowed Usage
Publishable (publishable) pk Agency information App or web clients
Secret (secret) sk All Backend servers only

If an endpoint restricts only certain information or is only available for use with a secret key, it will be marked as "(Requires Secret Key)".

Environments

There are three environments available as part of the Token Transit Ticketing API: fake, test, and live.

Environment Key Infix Description
fake _fake_ Fast, deterministic, stateless, fake API useful for exploring the API or automating integration tests.
test _test_ Test servers that use fake credit cards, user accounts, and test payment provider environment. Useful for testing before launch.
live _live_ Live service that should only use real accounts.

Code examples in this doc exclusively use the fake environment.

Distribution

During your integration, we will ask you to provide us with a public RSA key. We we sign your API keys and distribute them to you over e-mail or your preferred method of contact.

Retrieval and Invalidation

Once we send you a copy of your API key, it is impossible for us to re-send you your key. If you suspect a key has been compromised or you lose your API key, we will have to send you new key(s) and invalidate the old one(s).

Protocol

All calls must be made over HTTPS and include API key authentication or they will fail.

User Credentials

In addition to API keys, some endpoints also require you also create a user session. User sessions are long-lasting sessions created using the Token Transit API. User sessions are returned as cookies and are created by sending a signed JWT that we can use to verify account credentials. You must have a user id (unique per user) for each of your users and that you guarantee you can map back to a user for the duration of our integration with you to used as the sub claim in your JWT.

Token Transit uses standardized JWK tools to accept and verify your JWT. The standards for this interaction are:

Token Transit requires that you send a signed JWT to create Token Transit account credentials. We will use a URL that you provide us separately to fetch your JWKs and verify the signature on each JWT we are provided.

We expect your JWTs to be short-lived (less than 10 minutes will work well with Token Transit infrastructure). We expect your JWKs to be publicly accessible at a URL that you provide us, served over HTTPS, as a JSON Web Key Set (JWKS).

Here is a list of useful Links if you are not experienced with JWTs:

Setting up your own internal tooling to support JWT and JWKS is not difficult. There are also many third party services that will assist with this such as Auth0 and AWS Cognito.

Authorization Header

User credentials should be sent using the Authorization header specifying the Bearer type. Example:

Authorization: Bearer JWTHeader.JWTClaims.JWTSignature

JWT Specification

We use the following standard claims:

Claim Required Description
iat Required Creation time of the JWT. Must be in the past.
exp Required Expiration time of the JWT (Recommended no longer than 10 minutes past creation time)
sub Required User ID of your user that must be unique and that you can use to look up the user.
aud Optional If specified, please set to "tokentransit.com". We will reject any requests that include this claim but specify something else. This may be helpful to specify if you are generating JWTs for another API and don't want to mix up the JWTs.
iss Optional If specified, it must be a prefix of the HTTPS address of the JWKS server you have on file. This may be helpful to specify if you have a test and live server and you want an appropriate error message indicating that the issuer is incorrect.
jti Optional Can be used to force generation of a new session. We will never duplicate a session across JTIs even if the device ID is the same.

We also use the following custom claims:

Claim Required Description
email Required Specifies a contact method for the user. We require an email contact method for customer service purposes and to send users receipts.
device_id Conditionally Required If you will be generating and showing our visual tickets in your app, then you must provide a unique ID that represents a user's device. Some contracts with agencies require us to prevent users from sharing tickets across devices.
phone_number Optional Specifies a sms contact method for the user. We may use phone number to communicate notifications to a user. (We also accept phone as an alias for this claim name.)

JWKS Server

API implementors must provide a JWKS server endpoint that is publicly accessible. We will occasionally fetch your JWKS when neccessary or when signature verification fails. Token Transit provides an example endpoint that meets our specification and can be used for verification.

curl "https://api.tokentransit.com/testing/jwks.json"

The above command returns the following valid JWKS:

{
  "keys": [
    {
      "kty": "RSA",
      "alg": "RS256",
      "kid": "0c2c5382-cef8-4c60-bda8-16bb0771c3bc",
      "use": "sig",
      "e": "AQAB",
      "n": "odw6amrOS58r3APO4V90Rv_qS4FKn1Ri9MD6vjpFNJcQKfLeJLIdLhBPr_rUY0O5dyOd2xvv8Zc4yd4G8iWpDtO-1ShwigMARg6mlY0iHZH9l-hUKsm1x_cg3L4Tovp2SIEidhJp5MvpTodKJAtBADs6-s-YVokS3R5dQLnUy9MWpB9o_YvexR8slMzyb9wwcIJaLcFOFiJ59yRxGRfuRDn4yOttdqaKU4c8yjqbknwPPrkHGvsJHTrTgFBy2kU_vilWLKdizhfJc3xxj7GibryNclrGW7TiLy_DoRuwPf4mtfsN6RH6zZdmqISkps--j6T7lByl2thm2iw2J05Epw"
    }
  ]
}

JWT Generation

API implementors must generate signed JWTs for each user authentication. We will verify the signature of the JWT is valid and matches a key in you JWKS. Token Transit provides an example endpoint that meets our specification that can be used for verification. Note that this endpoint requires a valid public testing key to function, and will not accept live or fake keys.

curl "https://api.tokentransit.com/auth/testing/jwt" \
  -H "Token-Transit-Api-Key: sk_fake_1234567890abcXYZ" \
  -H "Token-Transit-Api-Version: 2020-05-21" \
  --data-urlencode "sub=bcfad41f" \
  --data-urlencode "email=user@example.com" \
  --data-urlencode "phone_number=+14155550123" \
  --data-urlencode "device_id=d8a942c5-a700-450d-977d-b3935ab0ea87"

The above command returns the following valid JWT:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsidG9rZW50cmFuc2l0LmNvbSJdLCJleHAiOjE1NzY3MjM4ODAsImlhdCI6MTU3NjcyMzU4MCwiaXNzIjoiaHR0cHM6Ly9hcGkudGVzdC50b2tlbnBsZXguY29tL3Rlc3RpbmcvIiwic3ViIjoiYmNmYWQ0MWYiLCJkZXZpY2VfaWQiOiJkOGE5NDJjNS1hNzAwLTQ1MGQtOTc3ZC1iMzkzNWFiMGVhODciLCJlbWFpbCI6InVzZXJAZXhhbXBsZS5jb20ifQ.SMt7c2eWSmeeWBZ1yQWoCb2noDKT9vFXuJe4e5ZnKcpyDd0Cu1jSDOLk37vnhKoBrTHOlW5FHJokKX6FpHOq8MREZQw0O1lRwC7px6nqHqDgv3nDgY68kEGnLDSwUoBNwv81_qIGFhJTasFKLzVYNGeZnp4SWN6Z4SnF6rkDeidAQSHIgOSEQ78OPM_5t-0L1DXaemaaj1e7VjSCpilLP-a62sBH-soXHQQVjx6y54ew0e1HFq2I4wSNc2fAXibIrfSL3FE4zXwcXZaUmoWk08-MqnhyExjKPKvmbFdC6oEsKIWE1an7D5p4USCUUXT0QFohEHQbr87ymY1WYkSKVg

Sessions

Requests to Token Transit API endpoints that require a user session will return an error with a 401 Authorization Error status code if no session cookie or valid authorization bearer token is provided. If an authorization bearer token is provided, a user_session_id cookie will be set in the response.

You may wish to use the endpoint /user/login to obtain a session id, as this endpoint has no further side effects.

Any successful request sent with a JWT (except those sent to the /user/logout endpoint) will result in the user_session_id cookie being set. The cookie may be used until the session expires. The session may expire at any time, so you should always first try a call with the user_session_id and fall back to using a JWT if the request fails with a 401 Authorization Error status code.

If you would like to forcibly destroy a session, please call /user/logout. This will block all further calls to the Token Transit Ticketing API using that session id to fail with an authorization error.

Versioning

The Token Transit Ticketing API uses a versioning system to separate breaking API changes. The Token-Transit-Api-Version header should always be set to ensure that the correct version of the API is used.

All version ids look like date strings in the format YYYY-MM-DD.

This version of the API is 2020-05-21.

Idempotency

All requests that have the ability to modify state will be PUT, POST, DELETE, or PATCH requests. Any of these requests can include an Idempotency-Key header. The idempotency key allows for requests to be retried without duplication of side-effects. All results whether successful or not will be saved server-side allowing a later request including an Idempotency-Key to be retried. These requests will always return the same result.

If an idempotency key is included in a request then the parameters of the request must match all previous requests with that key.

You may use any UUID suitable to yourself, but we strongly recommend generating UUIDs using UUID version 4 as specified in by IETF RFC 4122. Your UUID must be no longer than 128 bytes long and must be encoded in UTF8 format. We recommend either encoding the bytes using URL-safe base 64 encoding or the standard hexadecimal format specified in IETF RFC 4122.

Because GET requests are inherently idempotent (they do not modify state), the Idempotency-Key will be ignored on all GET requests.

Errors

Token Transit uses standard HTTP codes to indicate success or failure as well as a JSON-encoded response for each error as well as a JSON response with more details.

Status Codes

Token Transit Ticketing API uses a set of standard error codes.

Status Code Name Meaning
200 Success Request was successful.
400 Bad Request Requested parameters or format were invalid.
401 Unauthorized API key, user JWT, or session ID was missing or invalid.
403 Forbidden Requested resource cannot be accesssed by API key and/or user JWT or session ID.
404 Not Found Requested resource (e.g. Path, pass ID, etc) does not exist.
408 Request Timeout A retryable error occurred. Resend request without changing Idempotency-Key header.
409 Conflict Idempotency key was already used in a previous request with different parameters.
412 Precondition Failed Account state is out of date. You must refresh passes on the account and send back Token-Transit-Passes-State header in the new request.
428 Precondition Required Request won't work given current state of user account but could work if state changes.
429 Too Many Requests Your request has been rate-limited and must be retried.
500 Server Error An internal error in the Token Transit service occurred.
503 Service Unavailable An interanl error occurred that is retriable. Use the Retry-After header for guidance for when to retry.

JSON Error Response

In addition to the status code, we also return a response with the following fields:

Field Name Type Description
code string A code identifying a specific error.
message string A message to help debug the error suitable for displaying to the end user (localized).
param string The name of the param that is missing or invalid.
retryable boolean A boolean indicating whether the request should be retried.
type string The type of error returned.

Error Codes

Most errors have a specific error code (set in the code field) that can be used to identify the error. Below is a table of all error codes and their meanings:

Error Code Description Resolution Retryable (using same idempotency key)
account_state_out_of_sync Changes to user's account have occurred since pass list was last fetched Fetch user's passes with /pass and show them to user. Send back Token-Transit-Passes-State header in next request. false
activated_pass_already_on_device User tries to purchase 2nd pass while 1st pass is currently active /agency/{agency_id}/fare/{fare_id}/purchase only) Display error false
agency_free_fare_day Agency is preventing passes from being purchased or activated at this time because fares are free or not required Notify user that fares are free or not required false
appengine_unexpected_error Internal retryable error code Retry request with same idempotency key true
card_declined_expired The user's card is expired Display error false
card_declined_generic The user's card was declined with no extra explanation Display error false
card_declined_insufficient_funds The user's card has insufficient funds Display error false
card_declined_radar The user's card was declined for suspicious activity detected by Token Transit's payment processor Display error. Do not inform user why card was declined. Make sure explanation is identical to card_declined_generic above. false
card_declined_retryable An retryable error occurred while processing payment Retry request with same idempotency key true
card_declined_stolen The user tried to use a card reported as stolen Display error. Do not inform user why card was declined. Make sure explanation is identical to card_declined_generic above. false
card_ineligible A card with limits on the types of purchases it can be used (e.g. commuter benefits) for was ineligible to make this purchase Display error and request user use a different card false
datastore_failure Internal non-retryable error code Abort flow, display generic error, and refresh user passes false
datastore_retryable_failure Internal retryable error code Retry request with same idempotency key true
existing_purchase_in_progess Another purchase is already in progress on this account. Purchase will be blocked for at most 2 minutes Display error and prompt user to retry in at most 2 minutes false
fares_cannot_be_purchased User not allowed to purchase fare on this platform Send back cart to refresh fares and repopulate list of fares false
fares_out_of_date Agency fares have been updated Re-fetch agency fares and begin purchase flow again false
idempotency_key_in_use A request with different parameters already used this idempotency key. Likely indicates a bug in your implementation. Make sure to send a new idempotency key if user modifies request before retrying false
internal_checks_failed Internal non-retryable error code Abort flow, display generic error, and refresh user passes. Please notify us if you encounter this error. false
invalid_email_address User entered an e-mail address that was not parseable Indicates that not enough validation was done for user email addresses. This error should not occur for properly formatted e-mail addresses false
invalid_cart The requested collection of passes is not valid (for example, because a quantity limit is exceeded) Display the error and continue from the last valid cart. false
invalid_id An endpoint was called with an incompatible ID (e.g. /pass/{pass_id} was called with a pass book id) Fix internal code to only pass in proper IDs to endpoints. false
order_expired An incomplete order was retried (with same idempotency key) after timeout limit Stop retrying order and rotate the idempotency key before trying again false
pass_active_on_another_device User tried to activate a pass that is already in use on another device Display error. User may use pass again on the next day if it is still valid false
pass_cannot_be_activated_at_this_time User tried to activate a pass outside of its activation window Display error to user. You may want to check activation windows and display information to user instead of hitting the activation endpoint. false
pass_expired User tried to activate a pass that is expired Error should not be encountered if properly monitoring pass expiration. If encountered, display error and refresh user passes false
pass_invalid User tried to activate a pass that was invalidated by Token Transit Display error and refresh user passes false
pass_refunded User tried to activate a pass that has been refunded Display error and refresh user passes false
passes_state_header_invalid_or_missing An endpoint was called that requires the Token-Transit-Passes-State header Fetch user's passes with /pass and show them to user. Send back Token-Transit-Passes-State header in next request. false
pass_transferred User tried to activate a pass that was transferred Optionally show user the error. This indicates that you have a bug in your pass listing logic. Passes that are transferred should not be displayed false
pass_transfer_in_progress User tried to activate a pass that is in the process of being transferred Show user the error. This may indicate that you have a bug in your pass listing logic as you shouldn't normally be able to hit this endpoint without an account sync error. Passes that are awaiting_transfer should not be displayed false
pass_transfer_into_source_account User is attempting to redeem a pass or passes that are coming from their own account Show user the error. false
pass_unactivated Attempted to make visual validation call with an unactivated pass Do not show error to user. This indicates a bug in your visual validation logic. false
payment_method_not_found Purchase attempted without payment method Indicates a bug in your purchase logic. Probably show user this message, and ensure purchase never happens without a payment method. Rotate idempotency key before retrying false
redemption_already_used User attempted to redeem something that was already redeemed by the same account Show the error to the user and then refresh passes. false
redemption_invalid Either a user specified an invalid redemption code, the redemption link is expired, or there's a bug in the redemption logic Show the error to the user. false
redemption_ineligible User tried to a redeem something that their account is not eligible to redeem Show the error to the user. false
redemption_limits_exceeded User attempted to redeem something that has per-account limits and they already redeemed too many similar items Show the error to the user false
redemption_used_by_another_account User tried to redeem something but it had already been claimed by another account Show the error to the user. false
restricted_purchase User cannot purchase pass due to purchase restrictions Display error and information about purchase restrictions. Users should be barred from attempting purchase of restricted passes. false
resource_not_found Invalid id (e.g. agency, fare, pass) sent in URL Likely indicates a bug in your implementation false
serious_internal_error Major internal error occurred Display error and contact Token Transit as soon as possible if hit false
single_order_pass_limit_exceeded Too many passes added to the cart Show the user the error message and then show the cart contents false
stripe_idempotency_error User changed order but same idempotency key was used Likely indicates bug in your implementation. Make sure you rotate idempotency keys after receiving a non-retryable error false
stripe_idempotency_key_in_use Rare issue that might happen if the same request is sent twice in rapid succession Wait a few seconds and then retry request with identical parameters true
stripe_invalid_request An invalid Stripe parameter was sent or an internal error occurred Ensure that you are sending correct payment credentials that are associated with this account false
stripe_retryable_error A temporary error was passed through by our payment processor Retry request with same idempotency key true
stripe_unknown_error An unknown error was passed through by our payment processor Display error and/or retry with a different idempotency key false
unknown Internal non-retryable error code Display error, and notify us if you encounter this error false
url_error Internal retryable error code Retry request with same idempotency key true

Developers

A developer is an entity that uses the Token Transit Ticketing API. Generally your integration will have two developers: test and live. We provide a few endpoints to allow you to verify that your API keys work properly.

Retrieving Developer Info

Retrieves key type and developer id to verify that your key is working. Your API key uniquely identifies what partner is making the request, the environment, and what permissions may be accessed.

HTTP Request

curl "https://api.tokentransit.com/developer" \
  -H "Token-Transit-Api-Key: sk_fake_1234567890abcXYZ" \
  -H "Token-Transit-Api-Version: 2020-05-21"

The above command returns JSON structured like this

{
  "platform_id": "plat_1234567890abcXYZ",
  "platform_name": "Fake Platform",
  "owner_entity_kind": "Platform",
  "developer_id": "dev_1234567890abcXYZ",
  "api_key_type": "secret",
  "stripe_publishable_api_key": "pk_test_1234567890abcXYZ",
  "env": "fake"
}

GET /developer

HTTP Response

Field Name Type Description
platform_id string ID of your platform. A platform is the entity associated with a developer.
platform_name string The name of your platform.
developer_id string A unique id for your developer. This developer is what your API keys are associated with
api_key_type enum (as string) Either publishable or secret indicating the key type
stripe_publishable_api_key string A Stripe publishable API key to use with Stripe API requests
env enum (as string) Either fake, test or live indicating the environment that is being accessed

Testing a Secret Key (Requires Secret Key)

This endpoint will return success if you are using a secret key and fail if you are using a publishable key.

HTTP Requst

curl "https://api.tokentransit.com/developer/private" \
  -H "Token-Transit-Api-Key: sk_fake_1234567890abcXYZ" \
  -H "Token-Transit-Api-Version: 2020-05-21"

The above command returns JSON structured like this

{
  "platform_id": "plat_1234567890abcXYZ",
  "platform_name": "Fake Platform",
  "owner_entity_kind": "Platform",
  "developer_id": "dev_1234567890abcXYZ",
  "api_key_type": "secret",
  "env": "fake"
}

GET /developer/private

HTTP Response

Field Name Type Description
api_key_type enum (as string) Either publishable or secret indicating the key type
developer_id string A unique id for your developer. This developer is what your API keys are associated with
env enum (as string) Either fake, test or live indicating the environment that is being accessed
platform_id string ID of your platform. A platform is the entity associated with a developer.
platform_name string The name of your platform.

Users

A user is the Token Transit representation of an account on your system. Users must be authenticated using JWTs that are signed by your service and returned to our service. We provide endpoints to fetch information about a user, generate a user session, and destroy a user session.

Generating a New User Session (Logging in) (User Authentication Required)

To obtain a user session, make a request to the login endpoint for the user and specify a signed JWT. The session ID returned in the cookie can be used in place of a JWT in any requests requiring User Authentication.

HTTP Request

curl -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InVzZXItY29udGFjdC1lbWFpbEB0aGlyZC1wYXJ0eS1zaXRlLmNvbSIsImF1ZCI6IlRva2VuVHJhbnNpdCIsImlzcyI6Imh0dHBzOi8vdGhpcmQtcGFydHktc2l0ZS5jb20vcHVibGljLWtleS1zaWduYXR1cmUiLCJzdWIiOiJ1bmlxdWVVc2VySUQxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE1NTk0MzAyMjR9.WRYRX2PYdUmouiQgG3r_KOTB7FRaW51YotR_YYuvnTCbymmlCP_I4-Di0QzvH096H7Mjh332JUp_Zx6P-TQt-iriEjCqKkZ8_WS9lc0TYClT5jL1cviS7yaZJgJ" \
  -H "Token-Transit-Api-Key: sk_fake_1234567890abcXYZ" \
  -H "Token-Transit-Api-Version: 2020-05-21" \
  -d include_stripe_customer_id=true \
  -b /tmp/tt_fake_api_cookies \
  -c /tmp/tt_fake_api_cookies \
  -X POST \
  "https://api.tokentransit.com/user/login"

The above command returns JSON structured like this

{
  "stripe_customer_id": "cus_1234567890abdXYZ"
}

POST https://api.tokentransit.com/user/login

Query Parameters

Parameter Required/Default Description
include_stripe_customer_id (Secret Key Only) false If true, return the Stripe customer id, but only if using a secret API key.
accepted_latest_terms false If true, acknowledges that the user was shown the Token Transit terms of service and privacy policy and has accepted the latest terms.

HTTP Response

Field Name Type Description
stripe_customer_id string The Stripe customer id of the user (for use with attaching a source to a customer. This will only be returned if include_stripe_customer_id is set to true

Destroying an Existing User Session (Logging out) (User Authentication Required)

To destroy an existing user session, make a request to the logout endpoint and specify the session id.

HTTP Request

The following command assumes you have already sent a login request and saved cookies in

/tmp/tt_fake_api_cookies

curl -H "Token-Transit-Api-Key: sk_fake_1234567890abcXYZ" \
  -H "Token-Transit-Api-Version: 2020-05-21" \
  -b /tmp/tt_fake_api_cookies \
  -c /tmp/tt_fake_api_cookies \
  -X POST \
  "https://api.tokentransit.com/user/logout"

The above command returns JSON structured like this

{}

POST https://api.tokentransit.com/user/logout

Cookies

Name Required Description
user_session_id Required The session id to destroy. This will prevent any future requests from using this session id.

HTTP Response

The response will always be an empty JSON response upon success with a cookie set to delete the user_session_id cookie.

Retrieve a Stripe ephemeral key (User Authentication Required)

To retrieve a Stripe ephemeral key for the current user, make a POST request to the stripe_ephemeral_key user endpoint. See the client-side Stripe documentation for iOS and Android on how to consume this ephemeral key in a client-side mobile application.

HTTP Request

The following command assumes you have already sent a login request and saved cookies in

/tmp/tt_fake_api_cookies

curl -H "Token-Transit-Api-Key: sk_fake_1234567890abcXYZ" \
  -H "Token-Transit-Api-Version: 2020-05-21" \
  -d stripe_api_version=2019-12-03 \
  -b /tmp/tt_fake_api_cookies \
  -c /tmp/tt_fake_api_cookies \
  -X POST \
  "https://api.tokentransit.com/user/stripe_ephemeral_key"

The above command returns JSON with unspecified internal structure that should be returns as is to Stripe for use with Stripe endpoints that require an ephemeral key.

{
  "internal": "fields"
}

POST https://api.tokentransit.com/user/stripe_ephemeral_key

Parameters

Name Required Description
stripe_api_version true The Stripe API version used by the client-side mobile application.

Cookies

Name Required Description
user_session_id Required The session id to destroy. This will prevent any future requests from using this session id.

HTTP Response

The response will be JSON with unspecified internal structure that should be returns as is to Stripe for use with Stripe endpoints that require an ephemeral key. Do not depend on the internal fields specified in the JSON response. Ensure that client-side code passes this JSON response unmodified to Stripe APIs.

Agencies

An agency is the issuer of a pass. Fetching an agency will return the location, name, and logo of the agency.

Agency object

All endpoints in this section either return an Agency object or a list of Agency objects.

The Agency object response is defined as follows:

Field Type Description
agency_id string A unique id for the agency.
agency_name string The name of the agency that should be displayed to the user.
logo_url string A cropped and resized logo that may be used when displaying the agency.
country string An ISO 3166-1 alpha-2 2-letter country code of the country or region where this agency operates.
timezone string A string containing the name of the time zone from the time zone database, such as "America/Los_Angeles".
region string A region name to display to the user.
website string The URL of the agency's website.
phone_number string A phone number for the agency for customer support or information purposes.
email string An email address for the agency for customer support or information purposes.
location object A location object composed of latitude and longitude.
location.latitude number The latitude coordinate for the center of the agency.
location.longitude number The longitude coordinate for the center of the agency.
service_alerts array An array of ServiceAlert objects.

ServiceAlert object

A Service Alert is an alert that should be displayed to the user when that tells them timely alerts about the agency.

Field Type Description
header string Header for the alert in an Array object.
description string Description for the alert in an Array object.
effect ServiceAlertEffect enum Effect for the alert in an Array object.

ServiceAlert effects

A service alert may have one of a number of effects. The effect type may be used to change app behaviour. New effects may be added over time; you should treat unknown effect types the same as OTHER_EFFECT.

Type Meaning
FARE_FREE The agency currently does not require fares to ride, so no fares are available for purchase.
OTHER_EFFECT

Retrieving information about an agency

This endpoint fetches an Agency object using the provided agency id.

HTTP Request

curl "https://api.tokentransit.com/agency/bigbluebus" \
  -H "Token-Transit-Api-Key: sk_fake_1234567890abcXYZ" \
  -H "Token-Transit-Api-Version: 2020-05-21"

The above command returns JSON structured like this

{
  "agency_id": "bigbluebus",
  "agency_name": "Big Blue Bus",
  "location": {
    "latitude": 34.028189,
    "longitude": -118.489176
  },
  "logo_url": "https://storage.googleapis.com/token-transit.appspot.com/myzToIABwReXJuY3z6e4KEGPXx1vOEAP3ZbYD9isQlE-logo.png",
  "region": "Santa Monica, CA",
  "timezone": "America/Los_Angeles",
  "website": "https://www.bigbluebus.com"
}

GET https://api.tokentransit.com/agency/{agency_id}

Path components

Path component description
agency_id Unique identifier for the agency.

Optional Headers

Header Default Description
Accept-Language en-US Locale(s) for the user to indicate what language agency information should be returned in. This is only for information that will be shown to the user.

HTTP Response

This endpoint will return an Agency object.

Listing all agencies

All agencies may be retrieved in one RPC.

HTTP Request

curl "https://api.tokentransit.com/agency" \
  -H "Token-Transit-Api-Key: sk_fake_1234567890abcXYZ" \
  -H "Token-Transit-Api-Version: 2020-05-21"

The above command returns JSON structured like this

{
  "agencies": [
    {
      "agency_id": "bigbluebus",
      "agency_name": "Big Blue Bus",
      "location": {
        "latitude": 34.028189,
        "longitude": -118.489176
      },
      "logo_url": "https://storage.googleapis.com/token-transit.appspot.com/myzToIABwReXJuY3z6e4KEGPXx1vOEAP3ZbYD9isQlE-logo.png",
      "region": "Santa Monica, CA",
      "timezone": "America/Los_Angeles",
      "website": "https://www.bigbluebus.com"
    }
  ]
}

GET https://api.tokentransit.com/agency

Optional Headers

Header Default Description
Accept-Language en-US Locale for the user to indicate what language pass information should be returned in. This is only for information that will be shown to the user.

HTTP Response

Field Type Description
agencies Array of Agency objects All of the agencies from which a fare can be purchased.

Fares

A fare corresponds to a specific type of pass that a user can buy through token transit. Each fare has a price, a name, and a description. Fare ids are unique per agency.

Price object

Fares may contain Price objects. The Price object is defined as follows:

Field Name Type Description
price.amount number An integer specifying the amount of currency in the smallest denomination of the currency.
price.currency string An ISO 4217 three-letter currency code (upper-case).

Fare object

All endpoints in this section either return an Fare object or a list of Fare objects.

The Fare object response is defined as follows:

Field Name Type When Present Description
agency_id string Always A unique id representing the agency.
agency_name string Always A displayable name for the agency.
purchase_alert_text string If relevant Information that the user should be made aware of before purchasing a fare. If the alert_text for options is not displayed during fare selection, show alert_text instead.
alert_text string If relevant Information that the user should be made aware of before purchasing a fare. If the alert_text for options is displayed during fare selection, show purchase_alert_text instead.
alert_label string If relevant A brief description or label for the contents of alert_text. (Note that alert_label might be absent even if alert_text is present.)
description string Always A user-displayable string in the language specified by Accept-Language.
fare_id string Always ID used for the specified fare (same as in the path).
fare_name string Always A displayable name for the fare.
options Array of objects Only from Fare endpoints or if specified with include_fare_options A list of options as described below
options.id string Only from Fare endpoints or if specified with include_fare_options A unique id corresponding to a FareOptionValue
options.type string Only from Fare endpoints or if specified with include_fare_options A unique type corresponding to a FareOption
passes_count number Only if product_type is pass_book Represents the number of passes in the pass book (only populated if the fare respresents purchase of a pass book)
price object Only from Fare endpoints or if specified with include_fare_price (default) A Price object that contains the usual price of the fare.
discounted_price object Only from Fare endpoints or if specified with include_fare_price (default), when a discount applies A Price object that contains the discounted price of the fare. Discounts will only be applied to purchases made via Cart.
product_type ProductType enum Always Describes whether the fare is a single_pass or a pass_book. pass_book products result in multiple passes being bought at once and add an additional PassBook object to the returned response.
restricted_purchase boolean Only from Fare endpoints If true, the fare cannot be purchased by the user and requires agency approval. See restricted_purchase_text for text to show user.
restricted_purchase_text string Only from Fare endpoints Text to show the user if a fare is restricted.

ProductType is an enum type (represented as a string) that can contain the following values:

Value Meaning
pass_book This fare actually represents a purchase of multiple passes at once and will result in the purchase of a PassBook object.
single_pass This fare represents a purchase of a single pass and will result in the purchase of a Pass object.

Fare Options

Each Fare object will contain a list of option ids. These options help organize the fares into a user-friendly display. Each fare will have a number of options: a rider type, a fare type, and (sometimes) a zone. Although these are the current options that are part of fares, we may add options at any point in the future, and thus listing options is an extensible way to describe the different types.

A FareOption will describe one type of option and all of the available values. Because they are only useful when organizing fares and passes in different ways, the FareOption objects will only be present in API calls that list all fares or passes. In API calls that only return a single Pass or Fare, only the option ids will be included.

A FareOption can be described as follows.

Field Name Type Description
type string A unique id string that may be used programmatically. Some of the existing types are rider, fare, and zone.
label string This is a string label that can be shown to the user next to a selector of a given type.
value Array of FareOptionValue objects A list of all of the values for a particular type of option.

The label should be shown to the user as a label for the category, while the value.name field should be used as the display text to the user and the value.id should be used to uniquely reference the id.

A FareOptionValue object lists the id, user-displayable name, and potentially other information in future versions. The object can be described as follows.

Field Name Type Description
id string A unique id string for the FareOptionValue that may be used programmatically.
name string This is a string that can be shown to the user to represent the values of this type.
description string If populated, a description of the option.
alert_text string If populated, information that the user should be made aware of before when selecting a fare with this option. This is typically a description of a usage restriction.
alert_label string If populated, a brief description or label for the contents of alert_text that should be prominently displayed when selecting this option. (Note that alert_label might be absent even if alert_text is present.)

When all of the fares are listed, a complete structure of the fares can be obtained broken down into different categories.

The FareOptions are listed in the order that they should be used for selection.

Selecting fare options

In order to display different fare options, FareOptions may be used to break the list of fares into different categories that can be shown to the user. A selector choosing from amongst all the FareOptionValues of the first type should be shown first. Next, the user should select among all of the FareOptionValue in the second category, but only for fares that already match the first category. In the case that a fare does not have a FareOptionValue of a particular type, this selector should be marked as inactive in the UI, as not having a category indicates that the pass cannot be broken down by this particular FareOption.

You can see how passes are selected in the Token Transit to get a feel for how we use the FareOptions in our app.

Retrieving information about a fare

To retrieve information about a type of pass, a request for a Fare object should be made. Generally, it is better to just list all of the fares at once using the list fares endpoint because it also includes the FareOption list.

HTTP Request

curl "https://api.tokentransit.com/agency/bigbluebus/fare/regular_single_ride" \
  -H "Token-Transit-Api-Key: sk_fake_1234567890abcXYZ" \
  -H "Token-Transit-Api-Version: 2020-05-21"

The above command returns JSON structured like this

{
  "agency_id": "bigbluebus",
  "agency_name": "Big Blue Bus",
  "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
  "fare_id": "regular_single_ride",
  "fare_name": "Regular Single Ride",
  "options": [
    {
      "id": "rider_regular",
      "type": "rider"
    },
    {
      "id": "fare_single_ride",
      "type": "fare"
    }
  ],
  "price": {
    "amount": 125,
    "currency": "USD"
  },
  "product_type": "single_pass"
}

GET https://api.tokentransit.com/agency/{agency_id}/fare/{fare_id}

Path components

Path component description
agency_id Unique identifier for the agency.
fare_id Agency-unique identifier for type of pass.

Optional Headers

Header Default Description
Accept-Language en-US Locale for the user to indicate what language pass information should be returned in. This is only for information that will be shown to the user.

HTTP Response

A Fare object with the information about the fare with be returned.

Listing all fares

All fares for a provided agency may be retrieved in a single call.

HTTP Request

curl "https://api.tokentransit.com/agency/bigbluebus/fare" \
  -H "Token-Transit-Api-Key: sk_fake_1234567890abcXYZ" \
  -H "Token-Transit-Api-Version: 2020-05-21"

The above command returns JSON structured like this

{
  "fare_options": [
    {
      "label": "Rider",
      "type": "rider",
      "value": [
        {
          "id": "rider_regular",
          "name": "Regular"
        },
        {
          "id": "rider_reduced",
          "name": "S/D/M"
        }
      ]
    },
    {
      "label": "Fare",
      "type": "fare",
      "value": [
        {
          "id": "fare_single_ride",
          "name": "Single Ride"
        },
        {
          "id": "fare_3_ride",
          "name": "3 Ride"
        }
      ]
    }
  ],
  "fares": [
    {
      "agency_id": "bigbluebus",
      "agency_name": "Big Blue Bus",
      "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
      "fare_id": "regular_single_ride",
      "fare_name": "Regular Single Ride",
      "options": [
        {
          "id": "rider_regular",
          "type": "rider"
        },
        {
          "id": "fare_single_ride",
          "type": "fare"
        }
      ],
      "price": {
        "amount": 125,
        "currency": "USD"
      },
      "product_type": "single_pass"
    },
    {
      "agency_id": "bigbluebus",
      "agency_name": "Big Blue Bus",
      "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
      "fare_id": "reduced_single_ride",
      "fare_name": "S/D/M Single Ride",
      "options": [
        {
          "id": "rider_reduced",
          "type": "rider"
        },
        {
          "id": "fare_single_ride",
          "type": "fare"
        }
      ],
      "price": {
        "amount": 50,
        "currency": "USD"
      },
      "product_type": "single_pass",
      "restricted_purchase": true,
      "restricted_purchase_text": "This pass is restricted. Riders must be pre-authorized by the transit agency to purchase it. For more information, please contact the transit agency."
    },
    {
      "agency_id": "bigbluebus",
      "agency_name": "Big Blue Bus",
      "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
      "fare_id": "regular_3_ride",
      "fare_name": "Regular 3 Ride",
      "options": [
        {
          "id": "rider_regular",
          "type": "rider"
        },
        {
          "id": "fare_3_ride",
          "type": "fare"
        }
      ],
      "passes_count": 3,
      "price": {
        "amount": 350,
        "currency": "USD"
      },
      "product_type": "pass_book"
    },
    {
      "agency_id": "bigbluebus",
      "agency_name": "Big Blue Bus",
      "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
      "fare_id": "reduced_3_ride",
      "fare_name": "S/D/M 3 Ride",
      "options": [
        {
          "id": "rider_reduced",
          "type": "rider"
        },
        {
          "id": "fare_13_ride",
          "type": "fare"
        }
      ],
      "passes_count": 3,
      "price": {
        "amount": 140,
        "currency": "USD"
      },
      "product_type": "pass_book",
      "restricted_purchase": true,
      "restricted_purchase_text": "This pass is restricted. Riders must be pre-authorized by the transit agency to purchase it. For more information, please contact the transit agency."
    }
  ]
}

GET https://api.tokentransit.com/agency/{agency_id}/fare

Path components

Path component description
agency_id Unique identifier for the agency.

Optional Headers

Header Default Description
Accept-Language en-US Locale for the user to indicate what language pass information should be returned in. This is only for information that will be shown to the user.

HTTP Response

Field Type Description
fares Array of Fare objects All of the fares that the user is able to purchase from the agency.
fare_options Array of FareOption objects All of the options that can be used to group fares for displaying to the user.

Carts

A cart is a collection of fares to be purchased, and includes information about the prices of the selected fares. Carts are populated using the /cart endpoint, and a cart can be provided to the /purchase endpoint to purchase the selected fares.

Cart Object

The Cart object is defined as follows:

Field Type Description
cart_token string Opaque string that identifies the contents of the cart.
agency_id string ID of the agency the user is shopping.
country string An ISO 3166-1 alpha-2 2-letter country code of the country or region where this agency operates.
items Array of CartItem objects The contents of the cart.
total_price Price object The total price of the cart, before any discounts.
discounted_total_price Price object If discounts apply, the total price of the cart including discounts.
agency_fares Array of Fare objects All of the fares that the user is able to purchase from the agency.
agency_fare_options Array of FareOption objects All of the options that can be used to group fares for displaying to the user.
purchase_alert_text string Information that the user should be made aware of before purchasing the cart.
alert_labels Array of strings Brief warning text that should be displayed before purchasing the cart.
service_alerts Array of ServiceAlert objects An array of ServiceAlert objects that inform the user about any timely information about this cart.

CartItem Object

The CartItem object contains:

Field Type Description
fare Fare object A fare that is in the cart.
quantity number The number of the fare in the cart.
total_fare_price Price object The total price for this CartItem, before any discounts.
discounted_total_fare_price Price object If discounts apply, the total price for this CartItem including discounts.

Populating a cart

To populate a cart, a POST reqest should be sent to the cart endpoint. Provide either cart_token to base your cart on an existing cart or a (agency ID) to start from a new, empty cart. add_fares, update_fares, and remove_fares may all be provided, but any individual fare ID may only appear in one of them.

HTTP Request

The following command assumes you have already sent a login request and saved cookies in

/tmp/tt_fake_api_cookies

curl -X POST \
  -H "Token-Transit-Api-Key: sk_fake_1234567890abcXYZ" \
  -H "Token-Transit-Api-Version: 2020-05-21" \
  -b /tmp/tt_fake_api_cookies \
  -c /tmp/tt_fake_api_cookies \
  -H "Content-Type: application/json" \
  -d 'a=bigbluebus' \
  -d 'add_fares[regular_single_ride]=2' \
  "https://api.tokentransit.com/cart"

The above command returns JSON structured like this:

{
  "cart_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjYXJ0djEiOnsiYWduIjoiYWhSemZuUnZhMlZ1TFhSeVlXNXphWFF0ZEdWemRISVRDeElHUVdkbGJtTjVHSUNBZ0lENHdvUUtES0lCQkhSbGMzUSIsImZzIjoiYWhSemZuUnZhMlZ1TFhSeVlXNXphWFF0ZEdWemRISXRDeElHUVdkbGJtTjVHSUNBZ0lENHdvUUtEQXNTRFVaaGNtVlRkSEoxWTNSMWNtVVlnSUNBZ0lDQWdBb01vZ0VFZEdWemRBIiwiaXRlbXMiOlt7ImZhcmVfaWQiOiJmdWxsX2ZhcmVfMV9yaWRlIiwicXVhbnRpdHkiOjR9XSwiUHJpY2UiOnsiYW1vdW50IjoxNzUsImN1cnJlbmN5IjoiVVNEIn19fQ.4onJHS-CyZw_8xgWVrsvs2LnmX97ErHKEIkj2jwTrbs",
  "agency_id": "bigbluebus",
  "items": [
    {
      "fare": {
        "agency_id": "bigbluebus",
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "fare_id": "regular_single_ride",
        "fare_name": "Regular Single Ride",
        "options": [
          {
            "id": "rider_regular",
            "type": "rider"
          },
          {
            "id": "fare_single_ride",
            "type": "fare"
          }
        ],
        "price": {
          "amount": 125,
          "currency": "USD"
        },
        "product_type": "single_pass"
      },
      "quantity": 2,
      "total_fare_price": {
        "amount": 250,
        "currency": "USD"
      }
    }
  ],
  "total_price": {
    "amount": 250,
    "currency": "USD"
  },
  "agency_fares": [
    {
      "agency_id": "bigbluebus",
      "agency_name": "Big Blue Bus",
      "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
      "fare_id": "regular_single_ride",
      "fare_name": "Regular Single Ride",
      "options": [
        {
          "id": "rider_regular",
          "type": "rider"
        },
        {
          "id": "fare_single_ride",
          "type": "fare"
        }
      ],
      "price": {
        "amount": 125,
        "currency": "USD"
      },
      "product_type": "single_pass"
    },
    {
      "agency_id": "bigbluebus",
      "agency_name": "Big Blue Bus",
      "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
      "fare_id": "reduced_single_ride",
      "fare_name": "S/D/M Single Ride",
      "options": [
        {
          "id": "rider_reduced",
          "type": "rider"
        },
        {
          "id": "fare_single_ride",
          "type": "fare"
        }
      ],
      "price": {
        "amount": 50,
        "currency": "USD"
      },
      "product_type": "single_pass",
      "restricted_purchase": true,
      "restricted_purchase_text": "This pass is restricted. Riders must be pre-authorized by the transit agency to purchase it. For more information, please contact the transit agency."
    }
  ],
  "agency_fare_options": [
    {
      "label": "Rider",
      "type": "rider",
      "value": [
        {
          "id": "rider_regular",
          "name": "Regular"
        },
        {
          "id": "rider_reduced",
          "name": "S/D/M"
        }
      ]
    },
    {
      "label": "Fare",
      "type": "fare",
      "value": [
        {
          "id": "fare_single_ride",
          "name": "Single Ride"
        },
        {
          "id": "fare_3_ride",
          "name": "3 Ride"
        }
      ]
    }
  ]
}

POST https://api.tokentransit.com/cart

Parameters

Field Type Required/Default Description
cart_token string Optional Provide a cart_token to create a Cart based on a previous Cart.
a string Optional Provide an agency ID to create a new Cart from empty. (If cart_token is provided, a must not be provided.)
add_fares Key-value map of Fare IDs to quantities Optional Add the fares in the given quantities.
update_fares Key-value map of Fare IDs to quantities Optional Set the quantities for the given fares.
remove_fares Key-value map of Fare IDs to quantities Optional Decrease the fares in the cart by the given quantities.

A request must specify either a or cart_token.

HTTP Response

A cart object is returned.

Passes

Passes are the actual digital product sold by Token Transit. Possession of an active pass shows proof of purchase and can be used to board an appropriate bus for the transit agency.

Pass Object

All of the endpoints in the the passes section all return a Pass object or a list of Pass and PassBook objects.

The Pass object response is defined as follows:

Field Type Description
agency_id string String that indicates the ID of the agency the pass is for
details object Object including details of the pass. This should always be used over fare field because some passes do not have a fare field.
details.alert_text string If populated, information that the user should be made aware of before activating the pass. This is typically a description of a usage restriction.
details.alert_label string If populated, a brief description or label for the contents of alert_text that should be prominently displayed when the pass is active. (Note that alert_label might be absent even if alert_text is present.)
details.agency_name string Name of the agency that the pass belongs to.
details.description string Description of the pass to show to the user.
details.name string Name of the pass to show to the user.
display_html string Base64-encoded or raw HTML ticket that should be displayed to the user in a web view. This will be omitted from the response if your platform is not allowed to show tickets for this agency.
end_timestamp Date (RFC 3339) A timestamp indicating when the pass expires; or, if unactivated, a time based on the time of the API request (see note).
fare Fare object A Fare object containing the fare details (may only have the fare_id and fare_name fields populated). This field is not always populated in the response, specifically for passes that are part of pass books.
logo_url string Logo to show for this pass if present. This may be different than the agency logo shown for agency selection. If field is not present then the logo_url from the Agency object should be used instead. Caching the logo is recommened.
pass_id string A unique ID for the pass.
pass_book_id string If populated, the pass book that this pass belongs to. Passes from the same book should be visually grouped together unless they are active.
payment_processor string The payment processor used for payment.
purchase_timestamp Date (RFC 3339) A timestamp indicating when a pass was purchased.
start_timestamp Date (RFC 3339) A timestamp indicating when the pass was first activated; or, if unactivated, the time of the API request (see note).
status PassStatus enum A string code that indicates the status of the pass.
stripe_source string Only displayed for passes that were bought as part of a purchase on the purchase call.
ticket_expiration_timestamp Date (RFC 3339) A timestamp indicating when a pass must next be activated. This also indicates when a cached pass must be refreshed.
valid_time_ranges Array of TimeRange objects If this field is present, it indicates that the pass is only usable within a certain set of time ranges. If the current time is not within one of the given TimeRanges, the pass cannot be activated.

Pass Statuses

A pass may have a number of statuses. These statuses indicate the state of a pass and whether the pass is usable for boarding, can be activated, and is valid for later use.

Status Meaning Usable for boarding Able to be activated Valid for future rides
unactivated Pass has not yet been activated false true true
active Pass is already active on this device true true true
stale_ticket Pass has been activated before but must be activated again false true true
active_on_another_device Pass has been activated on another device and cannot be used on this device until the next day false false true
expired Pass has expired and may no longer be used false false false
refunded Pass has been refunded and may no longer be used false false false
invalid Pass has been invalidated and may no longer be used false false false
fare_capped Pass has been upgraded via fare capping and may no longer be used false false false

Passes that span multiple days will be reset to stale_ticket at the end of each day. To determine the time that this status will go into effect, use the ticket_expiration_timestamp. After this time, a pass must be reactivated to be usable again. Passes may be reactivated automatically, but keep in mind that as soon as a pass is reactivated it will be "pinned" to the device that activates it for the rest of the day. For this reason, you may want to prompt the user before reactivating passes.

TimeRange object

This object represents an interval of time, possibly unbounded on one end.

The TimeRange object is defined as follows:

Field Type Description
start_timestamp Date (RFC 3339) A timestamp indicating the start of the time range. If not populated, the time range has no start (i.e. it extends forever into the past).
end_timestamp Date (RFC 3339) A timestamp indicating the end of the time range. If not populated, the time range has no end (i.e. it extends forever into the future).

PassBook Object

Pass books are a set of passes that can be used individually but are purchased as one item. Pass books have their own ID which should be used to group together passes that are part of one book in the UI. One way to do this is to show information from the pass book instead until passes are activated and then individually show the activated passes. Pass books also have additional fields to indicate the number of passes remaining in the book.

The PassBook object is defined as follows:

Field Type Description
fare Fare object A Fare object containing the fare details (may only have the fare_id and fare_name fields populated).
logo_url string Logo to show for this pass_book if present. This may be different than the agency logo shown for agency selection. If field is not present then the logo_url from the Agency object should be used instead.
pass_book_id string A unique id for the pass book. Future endpoints may support direct lookup of pass book details. This id is also used to group together passes from the same book. Caching the logo is recommened.
pass_transfer_id string An id indicating that the pass is part of a pass transfer.
payment_processor string The
purchase_timestamp Date (RFC 3339) A timestamp indicating when a pass book was purchased.
refund_timestamp Date (RFC 3339) A timestamp indicating when a pass book was refunded.
stripe_source string A reusabled Stripe ID. It is only displayed for pass books that were bought as part of a purchase on the purchase call.
transfer_timestamp string A timestamp indicating when a transfer was transferred.
unactivated_remaining_count number The number of passes left in the book that can still be activated. This may be helpful to show to the user. This will always be 0 if the pass book can't be used (i.e. if it was transferred)

PassTransfer Object

Pass transfers correspond to the transfer of a pass from one account to another (usually from your administrative account to a new account).

The PassTransfer object is defined as follows:

Field Type Description
creation_timestamp Date (RFC 3339) A timestamp indicating when a pass book w
id string An ID corresponding to the transfer itself. These can be refunded.
items array of PassTransferItem Items that are in this transfer.
redemption object Information about redeeming the pass.
redemption.url string Send this to a user to allow them to redeem a pass.
redemption.expiration_timestamp Date (RFC 3339) The expiration timestamp of the redemption link. If the transfer has not been completed by this time, then the items in the transfer are automatically refunded. If this field is missing, there is no expiration.
status enum A field with value awaiting_transfer, transferred, refunded, partially_refunded, or cancelled indicating the status of the transfer. A transfer can only have the cancelled status if it was never redeemed.
refund_timestamp Date (RFC 3339) A timestamp indicating when the transfer was refunded or cancelled.
transfer_timestamp Date (RFC 3339) A timestamp indicating when the transfer was completed.

PassTransferItem Object

A PassTransferItem corresponds to either a Pass or PassBook that is an item in the user's account.

The PassTransferItem object is defined as follows:

Field Type Description
fare Fare object A Fare object containing the fare details (may only have the fare_id and fare_name fields populated).
pass_id string The id of the pass object that will be transferred.
pass_book_id string The id of the pass book object that will be transferred.
purchase_timestamp Date (RFC 3339) Date of purchase of the item.
refund_timestamp Date (RFC 3339) Date of refund of the item. If pass transfer state is cancelled, this won't be
type enum Either pass or pass_book depending on item type transferred.

Purchasing a pass (User Authentication Required)

To purchase a pass, make a POST request to the /purchase endpoint. This request should specify the fares to be purchased by providing either a cart_token or the combination of an agency id (via the a param) and fare_id.

HTTP Request

The following command assumes you have already sent a login request and saved cookies in

/tmp/tt_fake_api_cookies

curl -X POST \
  -H "Token-Transit-Api-Key: sk_fake_1234567890abcXYZ" \
  -H "Token-Transit-Api-Version: 2020-05-21" \
  -b /tmp/tt_fake_api_cookies \
  -c /tmp/tt_fake_api_cookies \
  -d a=bigbluebus \
  -d fare_id=regular_single_ride \
  -d include_fare_price=false \
  -d stripe_token=card_1234567890abcXYZ \
  -d ticket_format=none \
  "https://api.tokentransit.com/purchase"

The above command returns JSON structured like this:

{
  "pass_books": null,
  "passes": [
    {
      "agency_id": "bigbluebus",
      "details": {
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "name": "Regular Single Ride"
      },
      "end_timestamp": "2020-03-14T18:39:29.988256565-07:00",
      "fare": {
        "agency_id": "bigbluebus",
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "fare_id": "regular_single_ride",
        "fare_name": "Regular Single Ride",
        "product_type": "single_pass"
      },
      "logo_url": "https://storage.googleapis.com/token-transit.appspot.com/myzToIABwReXJuY3z6e4KEGPXx1vOEAP3ZbYD9isQlE-logo.png",
      "pass_id": "pass_unactivated",
      "payment_processor": "Stripe",
      "purchase_timestamp": "2020-03-13T16:39:29.988256565-07:00",
      "start_timestamp": "2020-03-14T16:39:29.988256565-07:00",
      "status": "unactivated",
      "stripe_source": "card_1234567890abcXYZ"
    }
  ]
}

The result contains all passes and their statuses in the account. The user_token in the command may be pasted into jwt.io.

POST https://api.tokentransit.com/purchase

Query Parameters

Parameter Required/Default Description
a Optional The agency_id of the agency to purchase the fare from.
fare_id Optional The ID of the fare to be purchased.
cart_token Optional The cart to be purchased.
stripe_token Required A Stripe token (begins with tok_), source (begins with card_), or payment method (begins with pm_) corresponding to a user-supplied credit or debit card that Token Transit can use to charge a user for the purchase.
ticket_format Required html_base64 returns a ticket in base64-encoded HTML, html_raw returns a ticket with HTML simply with JSON string escaping, none omits the ticket_html field.
include_fare_options false If true, includes fare option info for each pass as well as a fare_options object.
include_fare_price true If true, includes price of fare at the time of purchase.
omit_unusable_passes true If true, only returns passes that might be usable by the user.

Either fare_id or cart_token must be supplied.

Headers

The following headers are either required, strongly recommended, or specific to this endpoint.

Name Required Description
Idempotency-Key Strongly Recommended A key to unique to the given request. Specifying an idempotency key will guarantee a single purchase per key no matter how many times the endpoint is called.
Token-Transit-Passes-State Required This header specifies the state of the account as it was last shown to the user. You can fetch this header with a call to the List Passes endpoint (/pass).

HTTP Response

All of the passes in a user's account are returned in the following JSON object.

Field Type Description
fare_options Array of FareOption objects Breakdown of fare options for grouping passes in user-displayable way. Only included if include_fare_options is true.
passes Array of Pass objects All of the passes that belong to the logged-in user including any newly purchased passes.
pass_books Array of PassBook objects All of the pass books that belong to the logged-in user including any newly purchased pass books.

Response Headers

Name Description
Token-Transit-Passes-State This header specifies the state of the passes in the account after the purchase.

Purchasing and immediately activating a pass (User Authentication Required)

To purchase a pass and and activate a pass immediately , a POST request supplying a valid fare_id and purchase credentials should be sent to the pass purchase and activate endpoint.

HTTP Request

The following command assumes you have already sent a login request and saved cookies in

/tmp/tt_fake_api_cookies

curl -X POST \
  -H "Token-Transit-Api-Key: sk_fake_1234567890abcXYZ" \
  -H "Token-Transit-Api-Version: 2020-05-21" \
  -b /tmp/tt_fake_api_cookies \
  -c /tmp/tt_fake_api_cookies \
  -d stripe_token=card_1234567890abcXYZ \
  -d activate_on_purchase=true \
  -d ticket_format=html_base64 \
  "https://api.tokentransit.com/agency/bigbluebus/fare/regular_single_ride/purchase"

The above command returns JSON structured like this:

{
  "agency_id": "bigbluebus",
  "details": {
    "agency_name": "Big Blue Bus",
    "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
    "name": "Regular Single Ride"
  },
  "display_html": "PGh0bWw+PGJvZHk+VG9rZW4gVHJhbnNpdCB0aWNrZXQ8L2JvZHk+PC9odG1sPg",
  "end_timestamp": "2020-03-14T18:42:54.182663855-07:00",
  "fare": {
    "agency_id": "bigbluebus",
    "agency_name": "Big Blue Bus",
    "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
    "fare_id": "regular_single_ride",
    "fare_name": "Regular Single Ride",
    "product_type": "single_pass"
  },
  "logo_url": "https://storage.googleapis.com/token-transit.appspot.com/myzToIABwReXJuY3z6e4KEGPXx1vOEAP3ZbYD9isQlE-logo.png",
  "pass_id": "pass_active",
  "purchase_timestamp": "2020-03-14T16:42:54.182663855-07:00",
  "start_timestamp": "2020-03-14T16:42:54.182663855-07:00",
  "status": "active",
  "ticket_expiration_timestamp": "2020-03-14T18:42:54.182663855-07:00"
}

The start_timestamp and end_timestamp should be used to restrict the time in which a pass is allowed to be shown. The display_html is what should be used to display the pass in the app for the user to board the bus (see "Displaying a pass"). The user_token in the command may be pasted into jwt.io.

POST https://api.tokentransit.com/agency/{agency_id}/fare/{fare_id}/purchase

Path components

Path component Description
agency_id Unique identifier for the agency.
fare_id Unique identifier for the fare.

Query Parameters

Parameter Required/Default Description
stripe_token Required A Stripe token (begins with tok_) or source (may begin with card_ or acct_) corresponding to a user-supplied credit or debit card that Token Transit can use to charge a user for the purchase.
activate_on_purchase true Boolean indicating whether the ticket should be activated on purchase or not. This must be set to true for this endpoint. If you wish to buy a pass and not activate it, use /purchase instead.
ticket_format Required html_base64 returns a ticket in base64-encoded HTML, html_raw returns a ticket with HTML simply with JSON string escaping, none omits the ticket_html field.

Headers

The following headers are either required, strongly recommended, or specific to this endpoint.

Name Required Description
Idempotency-Key Strongly Recommended A key to unique to the given request. Specifying an idempotency key will guarantee a single purchase per key no matter how many times the endpoint is called.

HTTP Response

A Pass object will be returned representing the purchased pass.

Retrieving an existing pass (User Authentication Required)

Existing passes may be retrieved using the pass id for the specific pass. This endpoint can be called at any point to get fresh ticket HTML for a specific pass as long as the pass status is active .

HTTP Request

The following command assumes you have already sent a login request and saved cookies in

/tmp/tt_fake_api_cookies

curl "https://api.tokentransit.com/pass/pass_unactivated" \
  -H "Token-Transit-Api-Key: sk_fake_1234567890abcXYZ" \
  -H "Token-Transit-Api-Version: 2020-05-21" \
  -b /tmp/tt_fake_api_cookies \
  -c /tmp/tt_fake_api_cookies

The above command returns JSON structured like this

{
  "agency_id": "bigbluebus",
  "details": {
    "agency_name": "Big Blue Bus",
    "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
    "name": "Regular Single Ride"
  },
  "end_timestamp": "2020-03-14T18:44:37.240024959-07:00",
  "fare": {
    "agency_id": "bigbluebus",
    "agency_name": "Big Blue Bus",
    "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
    "fare_id": "regular_single_ride",
    "fare_name": "Regular Single Ride",
    "product_type": "single_pass"
  },
  "logo_url": "https://storage.googleapis.com/token-transit.appspot.com/myzToIABwReXJuY3z6e4KEGPXx1vOEAP3ZbYD9isQlE-logo.png",
  "pass_id": "pass_unactivated",
  "purchase_timestamp": "2020-03-13T16:44:37.240024959-07:00",
  "start_timestamp": "2020-03-14T16:44:37.240024959-07:00",
  "status": "unactivated"
}

If the pass status is active, stale_ticket, or active_on_another_device, then start_timestamp and end_timestamp should be used to restrict the time in which a pass should be shown as active to the user. If a pass is unactivated, then start_timestamp and end_timestamp indicate when a pass would expire if the user were to activate it at start_timestamp. This endpoint can be used to fetch the end_timestamp so it can be displayed to the user before they activate a pass. The display_html is what should be used to display the pass in the app for the user to board the bus (see "Displaying a pass").

GET https://api.tokentransit.com/pass/{pass_id}

Path components

Path component Description
pass_id Unique (base 64) identifier for pass returned after a successful purchase.

Query Parameters

Parameter Required/Default Description
ticket_format html_base64 html_base64 returns a ticket in base64-encoded HTML, html_raw returns a ticket with HTML simply with JSON string escaping, none omits the ticket_html field.

HTTP Response

A Pass object will be returned representing the retrieved pass.

Retrieving an existing pass from a specific agency (User Authentication Required)

Existing passes may be retrieved using the pass id for the specific pass. This endpoint can be called at any point to get fresh ticket HTML for a specific pass as long as the pass status is active.

HTTP Request

The following command assumes you have already sent a login request and saved cookies in

/tmp/tt_fake_api_cookies

curl "https://api.tokentransit.com/agency/bigbluebus/pass/pass_unactivated" \
  -H "Token-Transit-Api-Key: sk_fake_1234567890abcXYZ" \
  -H "Token-Transit-Api-Version: 2020-05-21" \
  -b /tmp/tt_fake_api_cookies \
  -c /tmp/tt_fake_api_cookies

The above command returns JSON structured like this

{
  "agency_id": "bigbluebus",
  "details": {
    "agency_name": "Big Blue Bus",
    "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
    "name": "Regular Single Ride"
  },
  "end_timestamp": "2020-03-14T18:47:09.639861731-07:00",
  "fare": {
    "agency_id": "bigbluebus",
    "agency_name": "Big Blue Bus",
    "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
    "fare_id": "regular_single_ride",
    "fare_name": "Regular Single Ride",
    "product_type": "single_pass"
  },
  "logo_url": "https://storage.googleapis.com/token-transit.appspot.com/myzToIABwReXJuY3z6e4KEGPXx1vOEAP3ZbYD9isQlE-logo.png",
  "pass_id": "pass_unactivated",
  "purchase_timestamp": "2020-03-13T16:47:09.639861731-07:00",
  "start_timestamp": "2020-03-14T16:47:09.639861731-07:00",
  "status": "unactivated"
}

If the pass status is active, stale_ticket, or active_on_another_device, then start_timestamp and end_timestamp should be used to restrict the time in which a pass should be shown as active to the user. If a pass is unactivated, then start_timestamp and end_timestamp indicate when a pass would expire if the user were to activate it at start_timestamp. This endpoint can be used to fetch the end_timestamp so it can be displayed to the user before they activate a pass. The display_html is what should be used to display the pass in the app for the user to board the bus (see "Displaying a pass").

GET https://api.tokentransit.com/agency/{agency_id}/pass/{pass_id}

Path components

Path component Description
agency_id Unique identifier for the agency.
pass_id Unique (base 64) identifier for pass returned after a successful purchase.

Query Parameters

Parameter Required/Default Description
ticket_format html_base64 html_base64 returns a ticket in base64-encoded HTML, html_raw returns a ticket with HTML simply with JSON string escaping, none omits the ticket_html field.

HTTP Response

A Pass object will be returned representing the retrieved pass.

Retrieving all passes for a user (User Authentication Required)

Existing passes may be retrieved for a user. All passes will be shown even if the passes are not allowed to be displayed on your platform.

HTTP Request

The following command assumes you have already sent a login request and saved cookies in

/tmp/tt_fake_api_cookies

curl "https://api.tokentransit.com/pass?ticket_format=none&omit_unusabled_passes=false&include_fare_price=false" \
  -H "Token-Transit-Api-Key: sk_fake_1234567890abcXYZ" \
  -H "Token-Transit-Api-Version: 2020-05-21" \
  -b /tmp/tt_fake_api_cookies \
  -c /tmp/tt_fake_api_cookies

The above command returns JSON structured like this

{
  "pass_books": [
    {
      "fare": {
        "agency_id": "bigbluebus",
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "fare_id": "regular_3_ride",
        "fare_name": "Regular 3 Ride",
        "product_type": "single_pass"
      },
      "logo_url": "https://storage.googleapis.com/token-transit.appspot.com/myzToIABwReXJuY3z6e4KEGPXx1vOEAP3ZbYD9isQlE-logo.png",
      "pass_book_id": "book_regular_3_ride",
      "purchase_timestamp": "2020-03-14T16:57:25.831550643-07:00",
      "unactivated_remaining_count": 1
    }
  ],
  "passes": [
    {
      "agency_id": "bigbluebus",
      "details": {
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "name": "Regular Single Ride"
      },
      "end_timestamp": "2020-03-14T18:57:25.830610242-07:00",
      "fare": {
        "agency_id": "bigbluebus",
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "fare_id": "regular_single_ride",
        "fare_name": "Regular Single Ride",
        "product_type": "single_pass"
      },
      "logo_url": "https://storage.googleapis.com/token-transit.appspot.com/myzToIABwReXJuY3z6e4KEGPXx1vOEAP3ZbYD9isQlE-logo.png",
      "pass_id": "pass_active",
      "purchase_timestamp": "2020-03-14T16:57:25.830610242-07:00",
      "start_timestamp": "2020-03-14T16:57:25.830610242-07:00",
      "status": "active",
      "ticket_expiration_timestamp": "2020-03-14T18:57:25.830610242-07:00"
    },
    {
      "agency_id": "bigbluebus",
      "details": {
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "name": "Regular Single Ride"
      },
      "end_timestamp": "2020-03-14T18:57:25.831137186-07:00",
      "fare": {
        "agency_id": "bigbluebus",
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "fare_id": "regular_single_ride",
        "fare_name": "Regular Single Ride",
        "product_type": "single_pass"
      },
      "logo_url": "https://storage.googleapis.com/token-transit.appspot.com/myzToIABwReXJuY3z6e4KEGPXx1vOEAP3ZbYD9isQlE-logo.png",
      "pass_id": "pass_unactivated",
      "purchase_timestamp": "2020-03-13T16:57:25.831137186-07:00",
      "start_timestamp": "2020-03-14T16:57:25.831137186-07:00",
      "status": "unactivated"
    },
    {
      "agency_id": "bigbluebus",
      "details": {
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "name": "Regular Single Ride"
      },
      "end_timestamp": "2020-03-14T18:57:25.831298674-07:00",
      "fare": {
        "agency_id": "bigbluebus",
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "fare_id": "regular_single_ride",
        "fare_name": "Regular Single Ride",
        "product_type": "single_pass"
      },
      "logo_url": "https://storage.googleapis.com/token-transit.appspot.com/myzToIABwReXJuY3z6e4KEGPXx1vOEAP3ZbYD9isQlE-logo.png",
      "pass_id": "pass_active_on_another_device",
      "purchase_timestamp": "2020-03-14T16:57:25.831298674-07:00",
      "start_timestamp": "2020-03-14T16:57:25.831298674-07:00",
      "status": "active_on_another_device",
      "ticket_expiration_timestamp": "2020-03-14T18:57:25.831298674-07:00"
    },
    {
      "agency_id": "bigbluebus",
      "details": {
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "name": "Regular Single Ride"
      },
      "end_timestamp": "2020-03-14T15:57:25.831343238-07:00",
      "fare": {
        "agency_id": "bigbluebus",
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "fare_id": "regular_single_ride",
        "fare_name": "Regular Single Ride",
        "product_type": "single_pass"
      },
      "logo_url": "https://storage.googleapis.com/token-transit.appspot.com/myzToIABwReXJuY3z6e4KEGPXx1vOEAP3ZbYD9isQlE-logo.png",
      "pass_id": "pass_expired",
      "purchase_timestamp": "2020-03-14T16:57:25.831343238-07:00",
      "start_timestamp": "2020-03-14T13:57:25.831343238-07:00",
      "status": "expired"
    },
    {
      "agency_id": "bigbluebus",
      "details": {
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "name": "Regular Single Ride"
      },
      "end_timestamp": "2020-03-14T17:57:25.831379728-07:00",
      "fare": {
        "agency_id": "bigbluebus",
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "fare_id": "regular_single_ride",
        "fare_name": "Regular Single Ride",
        "product_type": "single_pass"
      },
      "logo_url": "https://storage.googleapis.com/token-transit.appspot.com/myzToIABwReXJuY3z6e4KEGPXx1vOEAP3ZbYD9isQlE-logo.png",
      "pass_id": "pass_refunded",
      "purchase_timestamp": "2020-03-14T16:57:25.831379728-07:00",
      "start_timestamp": "2020-03-14T15:57:25.831379728-07:00",
      "status": "refunded"
    },
    {
      "agency_id": "bigbluebus",
      "details": {
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "name": "Regular Single Ride"
      },
      "end_timestamp": "2020-03-16T00:00:00-07:00",
      "fare": {
        "agency_id": "bigbluebus",
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "fare_id": "regular_single_ride",
        "fare_name": "Regular Single Ride",
        "product_type": "single_pass"
      },
      "logo_url": "https://storage.googleapis.com/token-transit.appspot.com/myzToIABwReXJuY3z6e4KEGPXx1vOEAP3ZbYD9isQlE-logo.png",
      "pass_id": "pass_stale_ticket",
      "purchase_timestamp": "2020-03-14T16:57:25.83141066-07:00",
      "start_timestamp": "2020-03-13T16:57:25.83141066-07:00",
      "status": "stale_ticket"
    },
    {
      "agency_id": "bigbluebus",
      "details": {
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "name": "Regular 3 Ride (1 Ride)"
      },
      "end_timestamp": "2020-03-14T15:57:25.831449513-07:00",
      "logo_url": "https://storage.googleapis.com/token-transit.appspot.com/myzToIABwReXJuY3z6e4KEGPXx1vOEAP3ZbYD9isQlE-logo.png",
      "pass_book_id": "book_regular_3_ride",
      "pass_id": "pass_regular_3_ride.sub_pass_1",
      "start_timestamp": "2020-03-14T13:57:25.831449513-07:00",
      "status": "expired"
    },
    {
      "agency_id": "bigbluebus",
      "details": {
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "name": "Regular 3 Ride (1 Ride)"
      },
      "end_timestamp": "2020-03-14T18:57:25.831481892-07:00",
      "logo_url": "https://storage.googleapis.com/token-transit.appspot.com/myzToIABwReXJuY3z6e4KEGPXx1vOEAP3ZbYD9isQlE-logo.png",
      "pass_book_id": "book_regular_3_ride",
      "pass_id": "pass_regular_3_ride.sub_pass_2",
      "start_timestamp": "2020-03-14T16:57:25.831481892-07:00",
      "status": "active",
      "ticket_expiration_timestamp": "2020-03-14T18:57:25.831481892-07:00"
    },
    {
      "agency_id": "bigbluebus",
      "details": {
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "name": "Regular 3 Ride (1 Ride)"
      },
      "end_timestamp": "2020-03-14T18:57:25.83151315-07:00",
      "logo_url": "https://storage.googleapis.com/token-transit.appspot.com/myzToIABwReXJuY3z6e4KEGPXx1vOEAP3ZbYD9isQlE-logo.png",
      "pass_book_id": "book_regular_3_ride",
      "pass_id": "pass_regular_3_ride.sub_pass_3",
      "start_timestamp": "2020-03-14T16:57:25.83151315-07:00",
      "status": "unactivated"
    }
  ]
}

The start_timestamp and end_timestamp should be used to restrict the time in which a pass is allowed to be used. You may cache the display_html until ticket_expiration_timestamp, which indicates the last time the ticket may be shown to the user before requiring a refresh.

GET https://api.tokentransit.com/pass

Query Parameters

Parameter Required/Default Description
include_fare_options false If true, includes fare option info for each pass as well as a fare_options object.
include_fare_price true If true, includes price of fare at the time of purchase.
omit_unusable_passes true If true, only returns passes that might be usable by the user.
ticket_format Required html_base64 returns a ticket in base64-encoded HTML, html_raw returns a ticket with HTML simply with JSON string escaping, none omits the ticket_html field.

HTTP Response

Field Type Description
fare_options Array of FareOption objects Breakdown of fare options for grouping passes in user-displayable way. Only included if include_fare_options is true.
passes Array of Pass objects All of the passes that belong to the logged-in user.
pass_books Array of PassBook objects All of the pass books that belong to the logged-in user.

Activating an existing pass (User Authentication Required)

Existing passes may be activated using the following endpoint. Activating a pass will generate a ticket that can be shown by the user to the driver of a vehicle. The ticket will be contained in the display_html part of the response. The field ticket_expiration_timestamp indicates when this endpoint must be called again to retrieve a new ticket. However, this ticket may be cached until that point. The status stale_ticket also indicates that a pass must be reactivated.

Activation will not always succeed. Consult the table in Pass Statuses to determine, which passes may be successfully activated. Of particular note is the pass_active_on_another_device status. This status indicates that the pass has already been activated by this user on another device. For security reasons, a user's pass may only be active on one device at once.

HTTP Request

The following command assumes you have already sent a login request and saved cookies in

/tmp/tt_fake_api_cookies

curl -X POST \
  -H "Token-Transit-Api-Key: sk_fake_1234567890abcXYZ" \
  -H "Token-Transit-Api-Version: 2020-05-21" \
  -b /tmp/tt_fake_api_cookies \
  -c /tmp/tt_fake_api_cookies \
  -d include_fare_price=false \
  -d ticket_format=html_base64 \
  -d omit_unusable_passes=false \
  "https://api.tokentransit.com/pass/pass_unactivated/activate"

The above command returns JSON structured like this

{
  "activated_pass_id": "pass_unactivated",
  "pass_books": [
    {
      "fare": {
        "agency_id": "bigbluebus",
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "fare_id": "regular_3_ride",
        "fare_name": "Regular 3 Ride",
        "product_type": "single_pass"
      },
      "logo_url": "https://storage.googleapis.com/token-transit.appspot.com/myzToIABwReXJuY3z6e4KEGPXx1vOEAP3ZbYD9isQlE-logo.png",
      "pass_book_id": "book_regular_3_ride",
      "purchase_timestamp": "2020-03-14T17:10:34.452193315-07:00",
      "unactivated_remaining_count": 1
    }
  ],
  "passes": [
    {
      "agency_id": "bigbluebus",
      "details": {
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "name": "Regular Single Ride"
      },
      "display_html": "PGh0bWw+PGJvZHk+VG9rZW4gVHJhbnNpdCB0aWNrZXQ8L2JvZHk+PC9odG1sPg",
      "end_timestamp": "2020-03-14T19:10:34.451370137-07:00",
      "fare": {
        "agency_id": "bigbluebus",
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "fare_id": "regular_single_ride",
        "fare_name": "Regular Single Ride",
        "product_type": "single_pass"
      },
      "logo_url": "https://storage.googleapis.com/token-transit.appspot.com/myzToIABwReXJuY3z6e4KEGPXx1vOEAP3ZbYD9isQlE-logo.png",
      "pass_id": "pass_active",
      "purchase_timestamp": "2020-03-14T17:10:34.451370137-07:00",
      "start_timestamp": "2020-03-14T17:10:34.451370137-07:00",
      "status": "active",
      "ticket_expiration_timestamp": "2020-03-14T19:10:34.451370137-07:00"
    },
    {
      "agency_id": "bigbluebus",
      "details": {
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "name": "Regular Single Ride"
      },
      "display_html": "PGh0bWw+PGJvZHk+VG9rZW4gVHJhbnNpdCB0aWNrZXQ8L2JvZHk+PC9odG1sPg",
      "end_timestamp": "2020-03-14T19:10:34.451858149-07:00",
      "fare": {
        "agency_id": "bigbluebus",
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "fare_id": "regular_single_ride",
        "fare_name": "Regular Single Ride",
        "product_type": "single_pass"
      },
      "logo_url": "https://storage.googleapis.com/token-transit.appspot.com/myzToIABwReXJuY3z6e4KEGPXx1vOEAP3ZbYD9isQlE-logo.png",
      "pass_id": "pass_unactivated",
      "purchase_timestamp": "2020-03-13T17:10:34.451858149-07:00",
      "start_timestamp": "2020-03-14T17:10:34.451858149-07:00",
      "status": "active",
      "ticket_expiration_timestamp": "2020-03-14T19:10:34.451858149-07:00"
    },
    {
      "agency_id": "bigbluebus",
      "details": {
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "name": "Regular Single Ride"
      },
      "end_timestamp": "2020-03-14T19:10:34.451916788-07:00",
      "fare": {
        "agency_id": "bigbluebus",
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "fare_id": "regular_single_ride",
        "fare_name": "Regular Single Ride",
        "product_type": "single_pass"
      },
      "logo_url": "https://storage.googleapis.com/token-transit.appspot.com/myzToIABwReXJuY3z6e4KEGPXx1vOEAP3ZbYD9isQlE-logo.png",
      "pass_id": "pass_active_on_another_device",
      "purchase_timestamp": "2020-03-14T17:10:34.451916788-07:00",
      "start_timestamp": "2020-03-14T17:10:34.451916788-07:00",
      "status": "active_on_another_device",
      "ticket_expiration_timestamp": "2020-03-14T19:10:34.451916788-07:00"
    },
    {
      "agency_id": "bigbluebus",
      "details": {
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "name": "Regular Single Ride"
      },
      "end_timestamp": "2020-03-14T16:10:34.451954083-07:00",
      "fare": {
        "agency_id": "bigbluebus",
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "fare_id": "regular_single_ride",
        "fare_name": "Regular Single Ride",
        "product_type": "single_pass"
      },
      "logo_url": "https://storage.googleapis.com/token-transit.appspot.com/myzToIABwReXJuY3z6e4KEGPXx1vOEAP3ZbYD9isQlE-logo.png",
      "pass_id": "pass_expired",
      "purchase_timestamp": "2020-03-14T17:10:34.451954083-07:00",
      "start_timestamp": "2020-03-14T14:10:34.451954083-07:00",
      "status": "expired"
    },
    {
      "agency_id": "bigbluebus",
      "details": {
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "name": "Regular Single Ride"
      },
      "end_timestamp": "2020-03-14T18:10:34.451998482-07:00",
      "fare": {
        "agency_id": "bigbluebus",
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "fare_id": "regular_single_ride",
        "fare_name": "Regular Single Ride",
        "product_type": "single_pass"
      },
      "logo_url": "https://storage.googleapis.com/token-transit.appspot.com/myzToIABwReXJuY3z6e4KEGPXx1vOEAP3ZbYD9isQlE-logo.png",
      "pass_id": "pass_refunded",
      "purchase_timestamp": "2020-03-14T17:10:34.451998482-07:00",
      "start_timestamp": "2020-03-14T16:10:34.451998482-07:00",
      "status": "refunded"
    },
    {
      "agency_id": "bigbluebus",
      "details": {
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "name": "Regular Single Ride"
      },
      "end_timestamp": "2020-03-16T00:00:00-07:00",
      "fare": {
        "agency_id": "bigbluebus",
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "fare_id": "regular_single_ride",
        "fare_name": "Regular Single Ride",
        "product_type": "single_pass"
      },
      "logo_url": "https://storage.googleapis.com/token-transit.appspot.com/myzToIABwReXJuY3z6e4KEGPXx1vOEAP3ZbYD9isQlE-logo.png",
      "pass_id": "pass_stale_ticket",
      "purchase_timestamp": "2020-03-14T17:10:34.452049739-07:00",
      "start_timestamp": "2020-03-13T17:10:34.452049739-07:00",
      "status": "stale_ticket"
    },
    {
      "agency_id": "bigbluebus",
      "details": {
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "name": "Regular 3 Ride (1 Ride)"
      },
      "end_timestamp": "2020-03-14T16:10:34.452083442-07:00",
      "logo_url": "https://storage.googleapis.com/token-transit.appspot.com/myzToIABwReXJuY3z6e4KEGPXx1vOEAP3ZbYD9isQlE-logo.png",
      "pass_book_id": "book_regular_3_ride",
      "pass_id": "pass_regular_3_ride.sub_pass_1",
      "start_timestamp": "2020-03-14T14:10:34.452083442-07:00",
      "status": "expired"
    },
    {
      "agency_id": "bigbluebus",
      "details": {
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "name": "Regular 3 Ride (1 Ride)"
      },
      "display_html": "PGh0bWw+PGJvZHk+VG9rZW4gVHJhbnNpdCB0aWNrZXQ8L2JvZHk+PC9odG1sPg",
      "end_timestamp": "2020-03-14T19:10:34.452124076-07:00",
      "logo_url": "https://storage.googleapis.com/token-transit.appspot.com/myzToIABwReXJuY3z6e4KEGPXx1vOEAP3ZbYD9isQlE-logo.png",
      "pass_book_id": "book_regular_3_ride",
      "pass_id": "pass_regular_3_ride.sub_pass_2",
      "start_timestamp": "2020-03-14T17:10:34.452124076-07:00",
      "status": "active",
      "ticket_expiration_timestamp": "2020-03-14T19:10:34.452124076-07:00"
    },
    {
      "agency_id": "bigbluebus",
      "details": {
        "agency_name": "Big Blue Bus",
        "description": "Valid on all local Big Blue Bus routes. Single ride is valid for 30 minutes after use.",
        "name": "Regular 3 Ride (1 Ride)"
      },
      "end_timestamp": "2020-03-14T19:10:34.452163203-07:00",
      "logo_url": "https://storage.googleapis.com/token-transit.appspot.com/myzToIABwReXJuY3z6e4KEGPXx1vOEAP3ZbYD9isQlE-logo.png",
      "pass_book_id": "book_regular_3_ride",
      "pass_id": "pass_regular_3_ride.sub_pass_3",
      "start_timestamp": "2020-03-14T17:10:34.452163203-07:00",
      "status": "unactivated"
    }
  ]
}

All passes are returned in this request (if successful). As in other calls, the start_timestamp and end_timestamp should be used to restrict the time in which a pass is allowed to be used. You may cache the display_html until ticket_expiration_timestamp, which indicates the last time the ticket may be shown to the user before requiring a refresh.

POST https://api.tokentransit.com/pass/{pass_id}/activate

Path components

Path component Description
pass_id Unique identifier for the pass.

Query parameters

Parameter Required/Default Description
include_fare_options false If true, includes fare option info for each pass as well as a fare_options object.
include_fare_price true If true, includes price of fare at the time of purchase.
ticket_format Required html_base64 returns a ticket in base64-encoded HTML, html_raw returns a ticket with HTML simply with JSON string escaping, none omits the ticket_html field.
omit_unusable_passes true If true, only returns passes that might be usable by the user.

HTTP Response

All of the passes in a user's account are returned in the following JSON object.

Field Type Description
activated_pass_id string The ID of the pass that was activated. In certain cases, this may be different from the one requested for activation but will always be one of the passes included in the result.
fare_options Array of FareOption objects Breakdown of fare options for grouping passes in user-displayable way. Only included if include_fare_options is true.
passes Array of Pass objects All of the passes that belong to the logged-in user including the newly activated pass.
pass_books Array of PassBook objects All of the pass books that belong to the logged-in user.

Response Headers

Name Description
Token-Transit-Passes-State This header specifies the state of the passes in the account after the activation.

Redemptions

A redemption is an action where the user is able to change the state of their account (e.g. add passes or unlock restricted passes) by redeeming a link.

Redemption is performed by the user by following a deeplink to your app. After following the deeplink, information about the redemption can be retrieved by the redeeming app as well letting the user redeem the contents.

In order to use redemption, you will have to register a redemption link deeplink format with us and respond to these deeplinks in your app. The deeplink must have the following format:

https://{domain}/{prefix}/redeem

The prefix part of the URL path can be any path that you wish to use including the root path. However, the full path must end in /redeem.

Please notify us of the schema you decide to use once you choose one.

The following are valid redemption links in test. You can just send them in the link parameter in the following calls:

Link ErrorCode Behavior
test_redemption_already_used redemption_already_used Fails with error code redemption_already_used if logged in or error code redemption_used_by_another_account if not logged in.
test_redemption_ineligible redemption_ineligible Fails with error code redemption_ineligible if logged in. There is no error if not logged in.
test_redemption_invalid redemption_invalid Fails with error code redemption_invalid
test_redemption_limits_exceeded redemption_limits_exceeded Fails with error code redemption_limits_exceeded if logged in. There is no error if not logged in.
test_redemption_into_source_account pass_transfer_into_source_account Fails with error code pass_transfer_into_source_account if logged in. There is no error if not logged in.
test_redemption_success None Redemption will succeed
test_redemption_used_by_another_account redemption_used_by_another_account Fails with error code redemption_used_by_another_account.

Redemption UX

Upon deeplink into your app, we recommend that you show a user a dialog or screen with a header, a body and two options.

The header should come from the title field of the GET /redeem response. The body should come from message message field of the GET /redeem response.

If the user is logged in, then there should ideally be two buttons:

If the user is not logged in, then there should ideally be two buttons:

Retrieving redemption information

Information about the redemption can be retrieved beform the redemption is performed. This is structed in the format of a title and message in the user's language. A user does not have to be logged in.

HTTP Request

curl "https://api.tokentransit.com/redeem?link=redemption.link" \
  -H "Token-Transit-Api-Key: sk_fake_1234567890abcXYZ" \
  -H "Token-Transit-Api-Version: 2020-05-21"
  -b /tmp/tt_fake_api_cookies \
  -c /tmp/tt_fake_api_cookies

The above command returns JSON structured like this

{
  "title": "Redeem Fares",
  "message": "The following fares will be made available in your account:\n  - Adult Single Ride, Quantity: 1"
}

GET https://api.tokentransit.com/redeem

Query Parameters

Parameter Required/Default Description
link Required The exact link including query parameters that the user followed to deeplink into your app.
text_mime_type text/plain The desired mime type of the message field in the response. Supported options are text/plain and text/html.

HTTP Response

A description of what is contained in the redemption.

Field Type Description
title string A title to display to the user in a modal.
message string The body of the message to display to the user in a modal.
code string The code from the link.
universal_url string A URL that can be opened in a browser to show the user all of the possible apps that can be used to redeem.
agency Agency object An agency object may be returned if the redemption is associated with a specific agency. May be omitted if the redemption is not associated with an agency.

Redeeming a link will execute logic that will affect the user's account state. This may include allowing access to restricted passes or adding passes to a user's account.

HTTP Request

curl -X POST \
  -H "Token-Transit-Api-Key: sk_fake_1234567890abcXYZ" \
  -H "Token-Transit-Api-Version: 2020-05-21" \
  -b /tmp/tt_fake_api_cookies \
  -c /tmp/tt_fake_api_cookies \
  -d link=redemption.link \
  "https://api.tokentransit.com/redeem"

The above command returns JSON structured like this

{}

POST https://api.tokentransit.com/redeem

Query Parameters

Parameter Required/Default Description
link Required The exact link including query parameters that the user followed to deeplink into your app.

HTTP Response

The response will always be an empty JSON object upon success.

Validations

Validations refer to the process where the user proves that they have the pass required to ride a particular vehicle.

Visual Validation

Visual validation is validation performed by the rider showing the vehicle operator a valid ticket from the display_html field in the Pass object. Information about these validations can be reported to Token Transit.

Logging a visual validation (User Authentication Required)

Logs a visual validation to the server including the pass validated, user's location, and current time. This should be sent every time the user taps to show the ticket html even if this happens in short succession.

HTTP Request

curl -X POST \
  -H "Token-Transit-Api-Key: sk_fake_1234567890abcXYZ" \
  -H "Token-Transit-Api-Version: 2020-05-21" \
  -b /tmp/tt_fake_api_cookies \
  -c /tmp/tt_fake_api_cookies \
  -d pass_id=pass_active \
  --data-urlencode timestamp="2020-03-14T17:00:01-07:00" \
  -d latitude=37.773972 \
  -d longitude=-122.431297 \
  "https://api.tokentransit.com/validation/visual"

The above command returns JSON structured like this

{}

POST https://api.tokentransit.com/validation/visual

Query Parameters

Parameter Required/Default Description
pass_id Required The id of the pass that the user tapped for validation.
latitude 0.0 Decimal number indicating the latitude that user validated the pass. If the latitude could not be acquired, the validation should still be logged but this parameter may be omitted.
longitude 0.0 Decimal number incating the longitude that the suer validated the pass. If the latitude could not be acquired, the validation should still be logged but this parameter may be omitted.
timestamp Required Time of validation (in RFC 3339). Time may include milliseconds microseconds, or nanoseconds.

Headers

The following headers are either required, strongly recommended, or specific to this endpoint.

Name Required Description
Idempotency-Key Strongly Recommended A key to unique to the given validation report. Specifying an idempotency key will help us ensure that the validation is only counted once.

HTTP Response

The response will always be an empty JSON response upon success.

Misc

Displaying Content

Displaying a pass purchase screen

It is recommended that you show the user all of the fares using selectors for different FareOptions. These should be shown in the order that is returned in the fare_options field from a called to the List Fares endpoint. Remember that not all combinations of options will yield a valid fare. Care should be taken to make sure that the each selector is only populated with options that when combined with the previous option still yield available fares.

Displaying the user's passes

Many of the API calls return a user's passes. It is recommended that you show all of the user's passes in a single (scrollable) screen that represents the user's pass wallet. It is recommended that you show active passes at the top and unactivated passes below. Passes that are expired or refunded should not be shown here but instead in a separate screen in the app.

Displaying a pass for boarding

Part of the return value of a pass is the display_html field in the JSON that contains the HTML to display to the user. This can be requested in either raw HTML or base64-encoded HTML. The HTML returned from this should be shown to the user in an web view.

Integrating with Stripe

All our our payments are processed through Stripe. The purchase endpoint accepts a number of different types of Stripe payment credentials via the stripe_token parameter including payment methods, sources, and tokens.

The difference between a Stripe token, a Stripe source, and a Stripe payment method is that the source and payment method are reusable while the token is not. A Stripe source or payment method will always be returned upon purchase.

Stripe SDK/Web API Integration

The easiest way to integrate with Stripe is to follow the Stripe guide. However, once you get to step 3, you will want to just send the payment method via the /purchase endpoint via the stripe_token param. Do not follow the remaining steps for now.

Spreedly / Other Vault Integration

This integration assumes you are using a credit card vault (such as spreedly). In this integration we will give you a special Stripe key that must only be used by the server. This key should let you make the required stripe calls. Here are the docs for the vaults that we know about.