Content Launcher: Request Processing and Decision Logic

:exclamation: At this time, Content Launcher and Account Login are available to select partners only.

New to Content Launcher? Start with our Complete Integration Guide which covers manifest setup, Account Login, and Content Personalization. This guide focuses specifically on Content Launcher request processing logic.

1. Introduction

The Content Launcher API enables Vega apps to seamlessly integrate with both the Fire TV home launcher and Alexa voice commands. This integration allows users to search and play content directly through these interfaces.

This guide details how Vega apps can handle content search and playback requests using Content Launcher, with examples demonstrating the data schema for various use cases.

2. Content Launcher Interface

Refer to the Content Launcher integration guide to implement the content launcher interface in your app. The callback method handleLaunchContent is invoked when the application receives content search or play requests from the Fire TV.

handleLaunchContent method has 3 parameters:

  1. contentSearch: IContentSearch

    This parameter has all the data required to handle launch request. Refer to this section in the developer guide for more details to retrieve launch data received.

  2. autoPlay: Boolean

    This data indicates if the request is for playing content or to search. true indicates quickplay and false indicates search.

  3. _optionalFields: ILaunchContentOptionalFields

    This is an optional field and not used now. No need to look at this value.

In upcoming sections, we will explore how to use contentSearch and autoPlay parameters to implement searching and playing content when the interface is invoked.

3. Content Launcher request examples

The following examples reference content from the sample ‘streamz_us’ catalog:

Movie: “Seabound”
TV Show: “The SeaShow: The Real Story”

...
<Movie>
  <ID>1700000725</ID>
  <Title locale="en-US">Seabound</Title>
  <Offers>
    <SubscriptionOffer>
      <LaunchDetails>
        <Quality>UHD</Quality>
        <Subtitle>en-US</Subtitle>
        <LaunchId>tv.streamz/movie/46720001</LaunchId>
      </LaunchDetails>
    </SubscriptionOffer>
    <FreeOffer>
      <Regions>
        <Territories>US</Territories>
      </Regions>
      <LaunchDetails>
        <Quality>SD</Quality>
        <Subtitle>en-US</Subtitle>
        <LaunchId>tv.streamz/movie/467200002</LaunchId>
      </LaunchDetails>
    </FreeOffer>
  </Offers>
</Movie>

<TvShow>
  <ID>1700000123</ID>
  <Title locale="en-US">The SeaShow: The Real story</Title>
  <Offers>
    <FreeOffer>
      <Regions>
        <Territories>US</Territories>
      </Regions>
      <LaunchDetails>
        <Quality>HD</Quality>
        <Subtitle>en-US</Subtitle>
        <LaunchId>459800033</LaunchId>
      </LaunchDetails>
    </FreeOffer>
  </Offers>
</TvShow>

<TvSeason>
  <ID>1700000234</ID>
  <Title locale="en-US">Season 1</Title>
  <Offers>
    <FreeOffer>
      <Regions>
        <Territories>US</Territories>
      </Regions>
      <LaunchDetails>
        <Quality>HD</Quality>
        <Subtitle>en-US</Subtitle>
        <LaunchId>453200012</LaunchId>
      </LaunchDetails>
    </FreeOffer>
  </Offers>
  <ShowID>1700000123</ShowID>
  <SeasonInShow>2</SeasonInShow>
</TvSeason>

<TvEpisode>
  <ID>1700000825</ID>
  <Title locale="en-US">The Seashow. Story Starts</Title>
  <Offers>
    <FreeOffer>
      <Regions>
        <Territories>US</Territories>
      </Regions>
      <LaunchDetails>
        <Quality>HD</Quality>
        <Subtitle>en-US</Subtitle>
        <LaunchId>453100008</LaunchId>
      </LaunchDetails>
    </FreeOffer>
  </Offers>
  <ShowID>1700000123</ShowID>
  <SeasonID>1700000234</SeasonID>
  <EpisodeInSeason>5</EpisodeInSeason>
</TvEpisode>
...

The following examples explain contentSearch parameter values for some key content launcher use cases. All supported use cases are listed here.

3.1 Play content through remote

Launch content Seabound through remote from Fire TV home screen.

"parameterList": [
  {
    "type": 13,
    "value": "",
    "externalIdList": [
      {
        "name": "catalogContentId",
        "value": "tv.catalog/movie/46720000"
      },
      {
        "name": "amzn_id",
        "value": "tv.catalog/movie/46720000"
      }
    ]
  }
]

Identify the content “tv.streamz/movie/46720000” using amzn_id and play it. Note that this is launchId. The app should play the content directly without going to intermediate pages like the content details page.

Note: In this example, “tv.streamz/movie/46720000” is launchId. If the catalog does not have LaunchId, ID will be passed as value.

3.2 Play content through voice

Utterance: “Alexa, Play Seabound

Action: Play movie Seabound when interface is invoked

autoPlay will be true here. contentSearch: IContentSearch will have following values.

"parameterList": [
  {
    "type": 13,
    "value": "Seabound",
    "externalIdList": [
      {
        "name": "streamz_us",
        "value": "1700000725"
      },
      {
        "name": "amzn_id",
        "value": "1700000725"
      }
    ]
  }
]

Identify the content “1700000725” using amzn_id and play it. The app should play the content directly without going to intermediate pages like the content details page.

Note: Alexa will not send LaunchId even when it is present in the catalog.

3.3 Episodic Play with voice

Utterance: “Alexa, Play Season 2 Episode 5 from The SeaShow: The Real story

Action: Play episode 5 from Season 2 in the TV Show The SeaShow: The Real story when interface is invoked

autoPlay will be true here. contentSearch: IContentSearch will have following values.

"parameterList": [
  {
    "type": 13,
    "value": "Season Two Episode Five The SeaShow: The Real story",
    "externalIdList": [
      {
        "name": "streamz_us",
        "value": "1700000123"
      },
      {
        "name": "amzn_id",
        "value": "1700000123"
      }
    ]
  },
  {
    "type": 14,
    "value": "2",
    "externalIdList": []
  },
  {
    "type": 15,
    "value": "5",
    "externalIdList": []
  }
]

Note that contentSearch will have only the ID of the Show, not the episode ID. The app has to fetch the episode URL using Show ID 1700000123, ContentSearchParamType::SEASON and ContentSearchParamType::EPISODE values to play the requested episode.

3.4 Episodic Play with voice - variance

Utterances:

  1. Alexa, Play Episode 5 from The SeaShow: The Real story
    Payload will have only show ID and episode number. It is not clear from which season.

  2. Alexa, Play Season 2 from The SeaShow: The Real story
    Payload will have only show ID and season number. It is not clear which episode to play.

  3. Alexa, Play The SeaShow: The Real story
    Payload will have only show ID. It is not clear what to play here.

autoPlay will be true here. contentSearch: IContentSearch will have following values.

"parameterList": [
  {
    "type": 13,
    "value": "Season Two Episode Five The SeaShow: The Real story",
    "externalIdList": [
      {
        "name": "streamz_us",
        "value": "1700000123"
      },
      {
        "name": "amzn_id",
        "value": "1700000123"
      }
    ]
  },
  {
    "type": 15,
    "value": "5",
    "externalIdList": []
  }
]

The app can choose to play relevant content such as continuing the previously watched episode, recent episode, etc. Alternatively, the app can show relevant show, season, or episode detail pages if autoplay is true.

3.5 In-App search for content title using voice

Utterance: “Alexa, find Seabound in Streamz

Action: Display content details page for the requested content.

Autoplay will be false here. Use amzn_id 1700000725 to identify the content.

"parameterList": [
  {
    "type": 13,
    "value": "Seabound",
    "externalIdList": [
      {
        "name": "streamz_us",
        "value": "1700000725"
      },
      {
        "name": "amzn_id",
        "value": "1700000725"
      }
    ]
  }
]

3.6 In-App search for Genre using voice

Utterance: “Alexa, find comedy movies in Streamz

Action: Show search result for “comedy movies”

autoPlay will be false. contentSearch: IContentSearch will have following values.

"parameterList": [
  {
    "type": 6,
    "value": "Comedy",
    "externalIdList": []
  },
  {
    "type": 6,
    "value": "Dark Comedy",
    "externalIdList": []
  },
  {
    "type": 6,
    "value": "Romantic Comedy",
    "externalIdList": []
  },
  {
    "type": 13,
    "value": "comedy movies",
    "externalIdList": []
  }
]

Alexa will return multiple genres and types. You can choose your own logic to implement search.

You can fetch the search keyword customer used from ContentSearchParamType::VIDEO type. In the following example, it is “comedy movies”.

{
  "type": 13,
  "value": "comedy movies",
  "externalIdList": []
}

3.7 In-App search for Actor using voice

Utterance: “Alexa, find Sean Connery movies in Streamz

Action: Show search result for “Sean Connery movies”

autoPlay will be false. contentSearch: IContentSearch will have following values.

"parameterList": [
  {
    "type": 0,
    "value": "Sean Connery",
    "externalIdList": []
  },
  {
    "type": 13,
    "value": "Sean Connery movies",
    "externalIdList": []
  }
]

Alexa might return multiple types. You can choose your own logic to implement search.

You can fetch the search keyword customer used from ContentSearchParamType::VIDEO type. It is “Sean Connery movies” in this example.

4. Processing launch request - Implementation Guide

This section provides a detailed walkthrough of processing content search data and handling requests effectively. We define three primary intents to standardize request handling across both Alexa voice commands and remote input:

4.1 ‘Play’ intent

App can play the requested content directly when:

  • autoplay is true
  • amzn_id is present
  • Neither episode nor season number are present

It is possible to have the Show ID (when the customer asks to play show, without mentioning season/episode) here. In that case, fallback to Show search page.

4.2 ‘Search’ intent

App cannot play the requested content directly when:

  • autoplay is false
  • autoplay is true, but amzn_id is not present. Ex: “Alexa, play comedy movies”
  • autoplay is true, but only Episode or season number is present. Ex: “Alexa, play Seabound season 3”

4.3 ‘playshow’ intent

We handle “Alexa, play Seabound season 3 episode 5” here. It is not handled in ‘play’ intent as apps need to fetch episode url to play the requested episode.

4.4 Sample Implementation

First, we process the contentSearch parameter to determine the appropriate launch intent.

We extract the search keyword from the Alexa utterance, and retrieve the amzn_id, episode number, and season number from the contentSearch parameter. Based on these values and the autoplay flag, we determine the correct launch intent.

We define a ContentLaunchPayload interface to structure these extracted values and pass them to the app for further processing.

import {
  IContentSearch,
  ILaunchContentOptionalFields,
  ContentSearchParamType,
} from '@amazon-devices/kepler-media-content-launcher';

interface ContentLaunchPayload {
  launchIntent: 'play' | 'search' | 'playshow' | null;
  searchKeyword: string | null;
  amzn_id: string | null;
  episodeNumber: string | null;
  seasonNumber: string | null;
}

const processedPayload: ContentLaunchPayload = {
  launchIntent: null,
  searchKeyword: null,
  amzn_id: null,
  episodeNumber: null,
  seasonNumber: null,
};

Following function processContentSearch() processes the input data and returns ContentLaunchPayload.

function processContentSearch(
  contentSearch: IContentSearch,
  autoPlay: boolean
): { processedPayload: ContentLaunchPayload | null } {
  const processedPayload: ContentLaunchPayload = {
    launchIntent: null,
    searchKeyword: null,
    amzn_id: null,
    episodeNumber: null,
    seasonNumber: null,
  };

  const searchParameters = contentSearch.getParameterList();

  if (searchParameters.length === 0) {
    console.warn('Content_Launcher_Sample: Error fetching search string');
    return {
      processedPayload,
    };
  }

  for (let i = 0; i < searchParameters.length; i++) {
    const param = searchParameters[i];
    const paramType = param.getParamType();
    const searchString = param.getValue();

    const externalIdList = param.getExternalIdList();
    if (externalIdList && externalIdList.length > 0) {
      for (const externalId of externalIdList) {
        const name = externalId.getName();
        const value = externalId.getValue();

        // Store the amzn_id if it is present. If multiple amzn_ids are present, we store the first one
        // Update the code if you want to handle multiple amzn_ids when present
        if (name === 'amzn_id' && !processedPayload.amzn_id) {
          processedPayload.amzn_id = value;
        }
      }
    }

    // Handle paramType specific logic. If launched by Alexa, ContentSearchParamType.VIDEO type will have the search string
    if (paramType === ContentSearchParamType.VIDEO) {
      processedPayload.searchKeyword = searchString;
    } else if (paramType === ContentSearchParamType.EPISODE) {
      processedPayload.episodeNumber = searchString;
    } else if (paramType === ContentSearchParamType.SEASON) {
      processedPayload.seasonNumber = searchString;
    }
  }

  // Determine launchIntent value based on the app's logic
  processedPayload.launchIntent = determineLaunchIntent(
    processedPayload,
    autoPlay
  );

  return {
    processedPayload,
  };
}

We implement determineLaunchIntent() function to decide play, search and playshow intent.

/*
 * Function to determine the launch intent based on the processed content launcher payload.
 * It checks the presence of amzn_id, episode number, and season number to decide whether
 * to play content directly, search for content, or play a specific episode of a show.
 */
function determineLaunchIntent(
  payload: ContentLaunchPayload,
  autoPlay: boolean
): 'play' | 'search' | 'playshow' | null {
  const { amzn_id, episodeNumber, seasonNumber } = payload;

  const hasCompleteEpisodeInfo = () =>
    episodeNumber !== null && seasonNumber !== null;
  const hasPartialEpisodeInfo = () =>
    (episodeNumber !== null) !== (seasonNumber !== null);
  const hasNoEpisodeInfo = () =>
    episodeNumber === null && seasonNumber === null;

  try {
    // Case 1: Direct play Intent
    // If autoPlay is true, amzn_id is present, and there is no episode or season number,
    // you can play content directly without additional logic.
    if (autoPlay && amzn_id && hasNoEpisodeInfo()) {
      return 'play';
    }

    // Case 2: Search Intent
    // 1. If autoPlay is false, the request is handled as a content search
    // 2. If amzn_id is not present, direct playback is not possible even when autoplay is true. Hence, the request is handled as a content search.
    // 3. If either episode number or season number is provided without the other, direct playback cannot be resolved even when autoplay is true. Hence, the request is handled as a content search.
    if (
      !autoPlay ||
      !amzn_id ||
      hasPartialEpisodeInfo()
    ) {
      return 'search';
    }

    // Case 3: Playshow Intent (for episodic content)
    // If autoPlay is true, amzn_id is present, and both episode and season numbers are provided,
    // it is a request to play a specific episode of a show.
    // This is handled in 'playshow' as it needs additional logic to play a specific episode of a show.
    if (autoPlay && amzn_id && hasCompleteEpisodeInfo()) {
      return 'playshow';
    }

    return null;
  } catch (error) {
    console.error(
      'Content_Launcher_Sample: Error determining launch intent:',
      error
    );
    return null;
  }
}

Next, we implement launchPlayOrSearch() function that will use processedPayload and perform the action. You will navigate to your app’s player or search screen as given in the pseudo code.

async function launchPlayOrSearch(processedPayload: ContentLaunchPayload) {
  const { launchIntent, searchKeyword, amzn_id, seasonNumber, episodeNumber } = processedPayload;

  switch (launchIntent) {
    case 'play': {
      /*
       * 1. Search for amzn_id in your catalog.
       * 2. If it is Movie or Episode, play directly
       * 3. If it is a Show, decide your logic. Ex: playing the recent episode or showing a list of episodes.
       */

      // Your implementation might look like:
      const movieUrl = await fetchMovieStreamUrl(amzn_id);

      if (movieUrl) {
        videoPlayer.play(movieUrl);
      } else {
        showSearchScreen(searchKeyword);
      }
      break;
    }

    case 'playshow': {
      /*
       * You will get here if the play request has specific amzn_id to play and has episode and season number.
       * Use your API to fetch the episode url using amzn_id, seasonNumber and episodeNumber.
       * Note that the amzn_id is the ID of the show and not the episode
       */

      // Your implementation might look like:
      const episodeUrl = await fetchEpisodeUrl(amzn_id, seasonNumber, episodeNumber);
      videoPlayer.play(episodeUrl);
      break;
    }

    case 'search': {
      /*
       * 1. Handle Search using searchKeyword
       * 2. For TV Shows, you can search for amzn_id, season number, episode number and show specific content details page
       */

      if (amzn_id || seasonNumber || episodeNumber) {
        showSearchScreen(amzn_id, seasonNumber, episodeNumber);
      } else {
        showSearchScreen(searchKeyword);
      }
      break;
    }
  }
}

5. Testing Content Launcher - Sample code

Use the attached sample to test the Content Launcher. It includes the complete code implementation discussed in the previous section. You can use the provided CLI tool to invoke the Content Launcher and validate its behavior.

content_launcher_sample_v2.zip

  1. Change the package name “com.kepler.contentlaunchersampleapp” to your app’s package name across your app’s files
  2. Change the partner id “PARTNER_ID” in manifest.toml to your app’s partner id
  3. Follow the testing instructions from this document to test for your app’s content

Following are the processed payload for some sample utterances:

5.1 “Alexa, Watch Seabound”

{
  "launchIntent": "play",
  "searchKeyword": "Seabound",
  "amzn_id": "1700000849",
  "episodeNumber": null,
  "seasonNumber": null
}

5.2 “Alexa, Watch Seabound Season 1”

{
  "launchIntent": "search",
  "searchKeyword": "Seabound season one",
  "amzn_id": "1700000849",
  "episodeNumber": null,
  "seasonNumber": "1"
}

5.3 “Alexa, Watch Seabound season 1 episode 3”

{
  "launchIntent": "playshow",
  "searchKeyword": "Seabound season one episode three",
  "amzn_id": "1700000849",
  "episodeNumber": "3",
  "seasonNumber": "1"
}

5.4 “Alexa, Find Seabound”

{
  "launchIntent": "search",
  "searchKeyword": "Seabound",
  "amzn_id": "1700000849",
  "episodeNumber": null,
  "seasonNumber": null
}

5.5 “Alexa, Find Seabound season 1 episode 3”

{
  "launchIntent": "search",
  "searchKeyword": "Seabound season one episode three",
  "amzn_id": "1700000849",
  "episodeNumber": "3",
  "seasonNumber": "1"
}

5.6 “Alexa, Find Comedy Movies” / “Alexa, Watch Comedy Movies”

{
  "launchIntent": "search",
  "searchKeyword": "comedy movies",
  "amzn_id": null,
  "episodeNumber": null,
  "seasonNumber": null
}

5.7 “Alexa, Find Sean Connery Movies” / “Alexa, Watch Sean Connery Movies”

{
  "launchIntent": "search",
  "searchKeyword": "Sean Connery movies",
  "amzn_id": null,
  "episodeNumber": null,
  "seasonNumber": null
}

Last updated: Mar 3, 2026