# Content Launcher: Request Processing and Decision Logic

**URL:** <https://community.amazondeveloper.com/t/content-launcher-request-processing-and-decision-logic/5388>\
**Category:** Knowledge Base\
**Tags:** vega-content\
**Created:** [July 30, 2025, 6:30pm UTC](https://community.amazondeveloper.com/t/content-launcher-request-processing-and-decision-logic/5388 "2025-07-30T18:30:00Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![Senthil](https://avatars.discourse-cdn.com/v4/letter/s/c89c15/32.png) [@Senthil](https://community.amazondeveloper.com/u/Senthil)\
**Post date:** [July 30, 2025, 6:30pm UTC](https://community.amazondeveloper.com/t/content-launcher-request-processing-and-decision-logic/5388/1 "2025-07-30T18:30:00Z")

</div>

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

**New to Content Launcher?** Start with our [Complete Integration Guide](https://community.amazondeveloper.com/t/content-launcher-integration-guide/14104) 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](https://developer.amazon.com/docs/vega/latest/content-launcher-overview.html) 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](https://developer.amazon.com/docs/vega/latest/content-launcher-integration-guide.html) to implement the content launcher interface in your app. The callback method _[handleLaunchContent](https://d32acggsvimfuc.cloudfront.net/NonPublicAPIsDocumentation/content-launcher-0.18/interfaces/turbo_modules_ContentLauncher.IContentLauncherHandler.html?Expires=1775591702&Signature=ozp~MObVPhJkK~gWmou8~DSGvjR-Y3b~Ab00EXAoOMTIkLd2JQ~RbeWz6zrCKBG3ZAWK1rKspLwMWUb8AQfrRqANHNkXIje~NpicoAJYcQrcEBGfWyozci-4tqnUOG~-PaNxX7YxbwE1omvGFpUC7urj3fYD7~Ebj8XOBPrlmYjEMMml2pn-CDWZG89kO7csYKvesg9URjtYmIAfZ4XMhaMy8Zfh7vFRvHTwg18gmbwLWSeDQWHsyOokDzhKtb0J36dO1nBGrDA3jzwfX98AOI9d0u2iGm8oQOwa3PF7Infk8EWkx5~WIs0FrVlPckgEHQVBMavNLYufHJI2iOi-Dw__&Key-Pair-Id=K2P1VJSLUJ0Y8#handleLaunchContent.handleLaunchContent-1)_ is invoked when the application receives content search or play requests from the Fire TV.

_handleLaunchContent_ method has 3 parameters:

1. **contentSearch: IContentSearch**

2. **autoPlay: Boolean**

3. **\_optionalFields: ILaunchContentOptionalFields**

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”

```xml
...
<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](https://developer.amazon.com/docs/kepler-tv/content-launcher-certification-test-cases.html).

### 3.1 [Play content through remote](https://developer.amazon.com/docs/vega/latest/content-launcher-certification-test-cases.html#requirements-for-launching-content-through-the-remote)

Launch content _Seabound_ through remote from Fire TV home screen.

```json
"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](https://developer.amazon.com/docs/vega/latest/content-launcher-certification-test-cases.html#requirements-for-watch-utterances)

Utterance: “_Alexa, Play Seabound_”

Action: Play movie _Seabound_ when interface is invoked

_autoPlay_ will be true here. _contentSearch: IContentSearch_ will have following values.

```json
"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](https://developer.amazon.com/docs/vega/latest/content-launcher-certification-test-cases.html#requirements-for-watch-utterances)

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.

```json
"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](https://developer.amazon.com/docs/vega/latest/content-launcher-certification-test-cases.html#requirements-for-watch-utterances)

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.

```json
"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](https://tiny.amazon.com/qrzei0mx/deveamazdocskeplconthtml)

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.

```json
"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](https://tiny.amazon.com/qrzei0mx/deveamazdocskeplconthtml)

Utterance: “_Alexa, find comedy movies in Streamz_”

Action: Show search result for “comedy movies”

_autoPlay_ will be false. _contentSearch: IContentSearch_ will have following values.

```json
"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”.

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

```

### 3.7 [In-App search for Actor using voice](https://tiny.amazon.com/qrzei0mx/deveamazdocskeplconthtml)

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.

```json
"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.

```typescript
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.

```typescript
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.

```typescript
/*
 * 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.

```typescript
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](https://developer.amazon.com/docs/vega/latest/content-launcher-testing.html). It includes the complete code implementation discussed in the previous section. You can use [the provided CLI tool](https://community.amazondeveloper.com/t/content-launcher-and-account-login-cli-tool-for-testing/12163/4) to invoke the Content Launcher and validate its behavior.

[content\_launcher\_sample\_v2.zip](https://community.amazondeveloper.com/uploads/short-url/5sv9F3858WdCUl4Hh8VAGMTmXdR.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](https://developer.amazon.com/docs/kepler-tv/content-launcher-testing.html) from this document to test for your app’s content

Following are the processed payload for some sample utterances:

### 5.1 “Alexa, Watch Seabound”

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

```

### 5.2 “Alexa, Watch Seabound Season 1”

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

```

### 5.3 “Alexa, Watch Seabound season 1 episode 3”

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

```

### 5.4 “Alexa, Find Seabound”

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

```

### 5.5 “Alexa, Find Seabound season 1 episode 3”

```json
{
  "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”

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

```

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

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

```

_Last updated: Mar 3, 2026_
