Skip to content

πŸ—ΊοΈ Zubie Web Component ​

Designed for quick integration and a seamless user experience, the Zubie web component allows a map view of Zubie vehicle trip history to be embedded directly into partner applications. It handles the fetching, parsing, and visualization of trip data, removing the need to build complex data visualizations from scratch.

The component is a simple HTML element (<zubie-trips-map>) that can be placed anywhere in your application. It includes an interactive or static map, renders trip routes, and displays key event information like trip start and end points.

Web component screenshot

πŸš€ Getting Started ​

Follow these steps to get the component up and running in your application.

βœ… Requirements ​

To use the web component, you will need the following:

  1. Zinc App
  2. Web Component Authentication Token
  3. Mapbox Access Token
  4. Web Component File

Zinc App ​

You will need a Zinc Application Client ID and Client Secret in order for users to authorize your application to access their Zubie account data.

You can create a free developer account for building and testing your integration at https://developer.zubie.com/manage-apps.

Web Component Authentication Token ​

To display trip data, the component must be authenticated for a specific Zubie account. This is typically the account of your application's end-user. Your application will be responsible for obtaining an authentication token for the user's account and passing it to the web component.

An authentication token is required to authorize access to a Zubie user's data. This token should be securely obtained by your application and passed into the zubie-token attribute. If the token is missing or invalid, the map will not load any trip data, and an auth-error event will be emitted.

See Authentication for more information.

Mapbox Access Token ​

Mapbox is the map provider used by the component. You must use your own Mapbox public access token to load the map. This token is passed into the mapbox-access-token attribute. For more information, see the Mapbox documentation on creating public tokens.

Web Component File ​

Download a copy of the web component file (zubie-web-components.umd.js) from https://github.com/zubie/zubie_sdk/tree/main/web-components. Save the file in your project directory to be referenced in the HTML <script> tag.

πŸ“¦ Installation ​

Insert the <script> tag into the <head> of your HTML template, pointing to the location of the component's JavaScript module. Then, insert the component tag where you want the map to render.

NOTE The src attribute will need to contain the correct path to the web component file you've downloaded from Github E.g. {PATH_TO_FILE_DIRECTORY}/zubie-trips-map.umd.js.
html
<html>
<head>
  <script type="module" src="/dist/zubie-trips-map.umd.js"></script>
</head>
<body>
  <zubie-trips-map
    mapbox-access-token="mapboxtoken123"
    map-type="interactive"
    vehicle-key="vehiclekey123"
    zubie-token="exampleauthtoken">
  </zubie-trips-map>
</body>
</html>

πŸ”’ Authentication ​

The web components require JSON Web Tokens (JWT) provided by Zubie to work.

IMPORTANT These JWTs provide specific access to the data needed for this purpose and are different from the access tokens granted Zubie's normal OAuth 2.0 flows.

βœ… Prerequisites ​

  1. You must have a Zinc app and its associated client_id and client_secret (the Zinc client credentials).
  2. Your Zinc app must have been granted access through OAuth 2.0 to the account whose data the component will display and you must have the access token returned by that OAuth exchange (the OAuth token).

When provided together, these data show:

  • You are authorized to act on behalf of the Zinc client
  • The Zubie customer whose data you will display has granted you to it.

⚠️ Authorization Requests ​

To request an authentication token that can be used by a web component, submit a POST request to:

http
https://login.zubiecar.com/api/special-purpose/v1/token

The following header is required:

http
Authorization: bearer {OAuth token}

Where OAuth token is the token returned to your Zinc app's callback URL after the account in question granted you access to their data. Including this token demonstrates that you should have access to the account's data. See our OAuth2 guide for more information.

The body of the request must include the following data:

PropertyDescription
client_idYour Zinc app's client_id
client_secretYour Zinc app's client_secret (providing this demonstrates your authorization to use that Zinc app)
subIdentification of the subject of the token (always a vehicle at this point) in the form of vehicle:[vehicle key], where "[vehicle key]" is the unique key identifier for the vehicle (available from our normal Zinc APIs and webhooks)
audThe origin where the web component is included, e.g., https://zubie.com You must use HTTPS
rolesWhich component you are using, currently always zubie://<zubie-trips-map />
scopeOne of the allowed scopes from the list below

Allowed sub format ​

  1. vehicle:[vehicle key]

Allowed roles ​

  1. zubie://<zubie-trips-map /> (a verbatim string)

Allowed scope ​

  1. loc-past: All past location data
  2. loc-past-gte:[timestamp]: Past location data more recent than or equal to the ISO 8601 timestamp (GMT assumed unless otherwise specifed)
  3. loc-past-lt:[timestamp]: Past location data older than the ISO 8601 timestamp
NOTE An error will be returned if the timestamp provided in the token scope is more restrictive than the timestamps used on the web component's started-after or started-before attributes.
Sample request curl
http
curl --request POST \
  --url https://login.zubiecar.com/api/special-purpose/v1/token \
  --header 'Authorization: Bearer ...' \
  --header 'Content-Type: application/json' \
  --data '{
  "client_id": "GAA4FCB7DC85660B6D32",
  "client_secret": "7GQyreSH8QfnkuejLETh435raQpigWvv",
  "sub": "vehicle:agpzfnp1YmllY2FycjcLEjdBY2NvpW50IhZ1SmF0Wmp1NHlZd9FVbnh0TFIyB3RLDAsSA0NhciILVVgzZmdCNUZEVVoM",
  "aud": "https://zubie.com",
  "roles": "zubie://<zubie-trips-map />",
  "scope": "loc-past"
}'
Sample response 200
json
{
	"token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik5JTjlqZkVZdFR0Rzg5RGJzSVI0MkVERE1IcGFIUS1YbXdUQmp1cjZMaVUiLCJqa3UiOiJodHRwOi8vbG9jYWxob3N0OjMwMDEvLndlbGwta25vd24vandrcy5qc29uIn0.eyJzdWIiOiJ2ZWhpY2xlOmFncHpmbnAxWW1sbExYRmhjamNMRWdkQlkyTnZkVzUwSWhad2JsTTNhbTg0UW1OMWNXczBjV3BSWlVaVVdIbEVEQXNTQTBOaGNpSUxkelY2ZEdkTGIySkRWRzhNIiwiYXVkIjoiaHR0cHM6Ly9nb29nbGUuY29tIiwiY2xpZW50X2lkIjoiQkY4NjI2NzcxRkI1QzhEQ0I5MzUiLCJzY29wZSI6ImxvYy1wYXN0Iiwicm9sZXMiOiJ6dWJpZTovLzx6dWJpZS10cmlwcy1tYXAgLz4iLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjMwMDEiLCJqdGkiOiIzYWUxOTA4Mi1hNzI5LTQ2YTItYTM0ZS1mN2E0MmIzOGU1ODEiLCJpYXQiOjE3NTMzMDM3NzIsImV4cCI6MTc1MzMwNzM3Mn0.hN9zdMzJiaTYSb2kkNL-8lx_40fwE3-c8q_rtMCcg8fvdlXBS8TmFnTydG1KCKhcrjD20edufS1vqPpBCRCrKGhRmgtRvvAUsm7J_AzgFOfOwOWbUULIQjDWHA6lSpAE6OuCzg_vsT3cAjwFSIWrvd57mtTVeV2hvoZTJ6zE6VPvw7My6uTx70s3PsU5kJ73wGokH9sudVUddbR-XrtCeX8Ox4WvL05hv0h6dW5WTYebVtThQwD6jpxt4hpWbxm-xsIOH6fpKf7RGB5O5KFUFQ9HkcnnCiFisjFtsqjpqZ9IU9ld2u4YZr6CNN-meQL7l--IiyPBzmQGV6QMsP2lFw",
	"expires_in": 3600,
	"token_type": "Bearer"
}
NOTE The authentication token will expire 1 hour after being issued. After expiration, a new token will need to be requested and the zubie-token component property will need to be updated.

Error responses ​

HTTP StatusError MessageDescription
400Missing client credentialsYou must include the client_id and client_secret in the request body
400Missing auth tokenYou must include an Authentication header with a bearer token
400Missing required claimsYou must include aud, roles, scope, and sub in the request body
400Invalid audThe aud value does not appear to be a URL or does not use HTTPS
400Invalid rolesThe roles value must exactly match one of the options provided above
400Invalid subThe sub value is incorrectly formatted
400Invalid scopeThe scope value must only include options in the format specified above
401Invalid client credentialsThe included client_id or client_secret is incorrect
401Invalid tokenThe token used in the Authorization header has at least one of these problems:
  • it does not exist
  • it was not issued to the Zinc app specified by your client credentials
  • it is not allowed to access the vehicle specified by sub

βš™οΈ Attributes ​

Note: The component can render data in one of two ways:

  • For a single trip, use both the trip-key and vehicle-key attributes.
  • For multiple trips for a single vehicle (within a time range), use the vehicle-key attribute.

You must always provide a vehicle-key to this component.

A Zubie authentication token scoped to a specific vehicle (see Authentication) is required to authorize access to trip and vehicle data. This token must be securely obtained by your application and passed into the component. IMPORTANT This is NOT the same as the OAuth2 access_token specified in Zubie's regular OAuth2 authentication flow.

AttributeValueRequiredDefaultDescription
mapbox-access-tokenstringβœ…--A valid Mapbox access token used to authenticate map rendering. This is required for the component to load and display the map.
zubie-tokenstringβœ…--The authentication token specified in the Authentication section. IMPORTANT This is NOT the same as the OAuth2 access_token specified in Zubie's regular OAuth2 tutorial/authentication flow.
vehicle-keystringβœ…--Specifies the vehicle from which to load trips. The vehicle-key must match the vehicle authorized by the provided token, or the request will be rejected. You can use the optional time filters (started-after, started-before) to control which trips are rendered for the vehicle.
trip-keystringoptional--Renders a single trip by its unique trip identifier. If present, it takes priority over vehicle-key, and any time-based filters (started-after, started-before) are ignored.
started-afterISO 8601 datetimeoptional--This will be ignored if a trip-key is provided. Filters trips that started after this timestamp up to the end of the day. If started-before is also provided, both will be used as long as the time window remains within the same calendar day. If started-before exceeds midnight of the same day, it will be ignored.
started-beforeISO 8601 datetimeoptional--This will be ignored if a trip-key is provided. Filters trips that started before this timestamp back to the beginning of the day. If started-after is also provided, both will be used as long as the time window remains within the same calendar day. If started-before exceeds midnight of the same day, it will be ignored.
map-typestringoptional"interactive"Determines the map rendering mode.

Possible values: interactive, static

interactive renders the interactive map, allowing panning, zooming, and interaction.

static renders a fixed image-based map with no interactivity.
map-stylestringoptional"mapbox://styles/mapbox/standard"Sets the visual style of the interactive Mapbox map. Accepts any valid Mapbox style URL.

This attribute only applies when map-type is interactive and is ignored in static mode.
default-centerstringoptional"-98.5795,39.828175" (center of US)Fallback map center when trip data is unavailable, in "longitude,latitude" format (no brackets). Defaults to the geographic center of the contiguous United States.
default-zoomnumberoptional3Fallback zoom level used when data is still loading or is otherwise unavailable. For valid values, see Mapbox zoom levels.
reverse-geocodebooleanoptionalfalseEnables reverse geocoding using Mapbox’s Geocoding API to show a human-readable address in the Trip Stop popup. Disabled by default to avoid additional API usage.

You can also define your own reverse geocoding service by setting the reverseGeocodeHandler property. See advanced usage for more details.

πŸ”” Events ​

The component emits custom events to report on its lifecycle and state changes. More information on the Event interface can be found at: https://developer.mozilla.org/en-US/docs/Web/API/Event

Event NameFired when…event.detailCancelableBubbles
mountThe component has mounted.The componentβŒβœ…
unmountThe component has unmounted.The componentβŒβœ…
map-loadThe Mapbox map is fully initialized and visible.Mapbox Map instanceβŒβœ…
trip-loadingTrip data has started/finished loading.true if loading started, false if loading endedβŒβœ…
api-errorAn error occurs when retrieving trip data.ErrorβŒβœ…
auth-errorToken is rejected while retrieving trip data.ErrorβŒβœ…
trip-renderToken is rejected while retrieving trip data.Mapbox Map instanceβŒβœ…
errorAn error occurs in the component or bubbled from the Map instance.ErrorβŒβœ…
address-not-foundA reverse geocoded address is not found or is otherwise unavailable. (Only if reverse-geocode attribute is used and using the default reverse geocode handler).{ "message": "<error message>", "latitude": <latitude>, "longitude": <longitude>, }βŒβœ…

πŸ‘‚ Listening to Events ​

You can add event listeners to the component just like any other HTML element.

javascript
const zubieMap = document.querySelector('zubie-trips-map');

if (zubieMap) {
  // Fired when trip data has finished rendering on the map
  zubieMap.addEventListener('trip-render', (event) => {
    console.log('Trip has been rendered!', event.detail); // event.detail is the Mapbox Map instance
  });

  // Fired when a data loading error occurs
  zubieMap.addEventListener('error', (event) => {
    console.error('An error occurred:', event.detail); // event.detail is an Error object
    // You could show a fallback UI message to the user here
  });
}

πŸ§™β€β™‚οΈ Advanced Usage ​

πŸ“ Custom Reverse Geocoding ​

If you prefer to use a different geocoding service instead of the default Mapbox API, you can provide your own by setting the reverseGeocodeHandler property on the component element.

Your handler function will receive the longitude and latitude and must return a Promise that resolves to an object with the following structure:

typescript
{
  street: string;
  city: string;
  state: string;
}

For example:

javascript
// Custom reverse geocoding handler
document.getElementById('zubieTripsMap').reverseGeocodeHandler = async (
  longitude,
  latitude
) => {
  console.log('Reverse geocoding:', longitude, latitude);

  // Perform your reverse geocoding logic here.
  const address = await new Promise((resolve) =>
    setTimeout(() => {
      // Mock address data (for demo purposes) in proper format
      const mockAddress = {
        street: 'Name Street',
        city: 'A City',
        state: 'Some State',
      };
      resolve(mockAddress);
    }, 1000)
  );

  return address;
};