as

Settings
Sign out
Notifications
Alexa
Amazon Appstore
AWS
Documentation
Support
Contact Us
My Cases
Get Started
Design and Develop
Publish
Reference
Support

Account Login Integration Guide

The Vega Media Account Login API provides a way for apps to report their authentication status to the system.

Apps need to refresh the user's login status upon each launch, which is done by the Account Login API. There are two account login states: signed in, and signed out. By keeping the login status up-to-date, apps make sure that users have access to appropriate content and features. The interface allows apps to communicate a boolean value indicating whether a user is logged in, both at launch and whenever the login state changes. The system can also query this status as needed, enabling a personalized user experience.

Products can leverage this information to feature or filter content from services where the user is already authenticated, allowing for instant access to content.

The Account Login functionality offers two key components for managing authentication status: the updateStatus method, and the handleReadStatus callback function. The updateStatus method reports an app's current login status for real-time updates when the authentication state changes. The handleReadStatus callback function can respond to system queries about the app's current login status. Both methods use the StatusType for account status, with two possible values: SIGNED_IN and SIGNED_OUT.

Vega Media Account Login API reference

For reference documentation about this API, see Content Launcher API and Account Login API Overview.

Vega Media Account Login common use cases

Apps should update the system with their login status in the following situations:

  • During the app's initial launch
  • When there's a change in the user's subscription status
  • Upon request from other services

Install and setup Vega Media Account Login

Step 1: Update the app manifest

Use the service component to handle a portion of the Account Login interactions. Familiarize yourself with service components, which are also called headless components. Get a service running alongside your interactive component to make this integration easier to perform and troubleshoot. See the service component documentation.

First, update your app manifest to use the Vega Media Account Login API. When modifying the manifest entries, replace com.amazondeveloper.media.sample with your app's package ID, as seen in the following example. The Account Login API is typically implemented as a service to provide account login status, especially during read operations. Don't require users to open an interactive interface-based app. Media provider apps are most often implemented as interactive apps to handle the user interface. Your interactive and service apps should exchange Account Login information. You can use shared files or other inter-process communication mechanisms to do this.

The example below shows a sample manifest for both the Content Launcher and Account Login APIs. It defines both interactive and service components.

Copied to clipboard.

schema-version = 1

[package]
title = "<Your app title>"
id = "com.amazondeveloper.media.sample"

[components]

[[components.interactive]]
id = "com.amazondeveloper.media.sample.main"
runtime-module = "/com.amazon.kepler.keplerscript.runtime.loader_2@IKeplerScript_2_0"
launch-type = "singleton"
# The category "com.amazon.category.kepler.media" is only necessary for the primary component, which is identified in the [[extras]]
# section of the manifest using the "component-id" value.
categories = ["com.amazon.category.main", "com.amazon.category.kepler.media"]

[[components.service]]
id = "com.amazondeveloper.media.sample.interface.provider"
runtime-module = "/com.amazon.kepler.headless.runtime.loader_2@IKeplerScript_2_0"
launch-type = "singleton"

[processes]

[[processes.group]]
component-ids = ["com.amazondeveloper.media.sample.main"]

[[processes.group]]
component-ids = ["com.amazondeveloper.media.sample.interface.provider"]

[offers]

[[offers.interaction]]
id = "com.amazondeveloper.media.sample.main"

[[offers.service]]
id = "com.amazondeveloper.media.sample.interface.provider"
required-privileges = ["com.amazon.multimedia.privilege.session.manage"]

[[message]]
uri = "pkg://com.amazondeveloper.media.sample.main"
# Match the privileges used in [[offers.interaction]]. If privileges are not added, then use "*".
sender-privileges = ["*"]
receiver-privileges = ["self"]

[[offers.module]]
id = "/com.amazondeveloper.media.sample.module@ISomeUri1"
includes-messages = ["pkg://com.amazondeveloper.media.sample.main"]

[[extras]]
key = "interface.provider"
component-id = "com.amazondeveloper.media.sample.main"

[extras.value.application]
[[extras.value.application.interface]]
interface_name = "com.amazon.kepler.media.IContentLauncherServer"
attribute_options = ["partner-id"]
static-values = { "partner-id" = "<Your partner id>" }

[[extras.value.application.interface]]
interface_name = "com.amazon.kepler.media.IAccountLoginServer"
attribute_options = ["Status"]
# In this manifest example, we have both Content Launcher and Account Login interfaces defined.
# Since the Account Login cluster should be handled by a service component instead of an
# interactive one for responsiveness, we use "override_attribute_component" to redirect
# calls for the "Status" attribute to the service component.
override_attribute_component = { Status = "com.amazondeveloper.media.sample.interface.provider" }

[needs]

[[needs.module]]
# The dot (.) after "media" is intentional in this format. This notation will be changed in a 
# future release.
id = "/com.amazon.kepler.media.@IAccountLogin1"

[[needs.module]]
id = "/com.amazon.kepler.media@IContentLauncher1"

Media apps often require integration with three key APIs:

  • Content Launcher Cluster API for finding and launching content
  • Vega Media Controls API for controlling launched media
  • Account Login API for communicating Account Login status to the system

The Content Launcher and Vega Media Control APIs are most often implemented as an interactive component, which handles Vega media queries. This component is specified in the app manifest's extras section using the interface.provider key, as shown in the following TOML snippet and in the previous example.

Copied to clipboard.

[[extras]]
key = "interface.provider"
component-id = "<primary interactive component-id>"

The Account Login API is implemented as a service app, allowing the system to query Account Login status without waking up the entire app. The Account Login API uses a single "Status" attribute, which is handled by the service component. To ensure proper routing of login status attribute interactions, the app manifest must specify the service component using the "override_attribute_component" setting, as demonstrated in this TOML example and in the example above:

Copied to clipboard.

override_attribute_component = { Status = "<service component-id implementing Account Login api>" }

Use this approach to optimize system performance and provide a clear separation between media control and account management functionalities.

Step 2: Include package dependencies

The Vega Media Account Login API is offered to TypeScript developers using the system turbo module amzn/kepler-media-account-login. This turbo module is part of the SDK package. To use the API, update the package.json file to take the turbo module as a dependency, and add amzn/headless-task-manager to create a headless service app.

Copied to clipboard.

"dependencies": {
  "@amazon-devices/kepler-media-account-login": "^1.1.0",
  "@amazon-devices/headless-task-manager": "^1.1.0"
}

Step 3: Add the Vega Media Account Login business logic

Create a file named AccountLoginWrapper.ts to contain the core business logic for the Vega Media Account Login API. In this file, create and export a class named AccountLoginWrapper. This class has a single member variable: an instance of the IAccountLoginServerAsync interface, used to interact with the Account Login server. This file will also globally create an instance of AccountLoginServerComponent and store it in a variable named accountLoginServerComponent.

Copied to clipboard.

import { IComponentInstance } from '@amazon-devices/react-native-kepler';
import {
  AccountLoginServerComponent,
  IAccountLoginHandlerAsync,
  StatusType,
  IAccountLoginServerAsync,
  IStatus,
} from '@amazon-devices/kepler-media-account-login';
import AsyncStorage from '@react-native-async-storage/async-storage';

const accountLoginServerComponent = new AccountLoginServerComponent();

export class AccountLoginWrapper {
  accountLoginServer?: IAccountLoginServerAsync;
}

Step 4: Asynchronously return the Account Login status

Now using the IAccountLoginHandlerAsync interface, implement the handleReadStatus() method that takes no arguments and returns a Promise<IStatus>. By designing the method in this way, we ensure that the service app can asynchronously return the Account Login status without blocking the caller. The Promise<IStatus> return type indicates that the method eventually resolves with an IStatus object, which likely contains information about the current login state.

The headless service created later in Step 8 invokes the handleReadStatus() handler. Since the interactive component (UI app) and the headless service run in separate processes, they do not share in-memory state. Relying on in-memory variables is insufficient. To ensure reliable communication between the two components, use a shared mechanism for persisting and transferring the login status. Login status must be read asynchronously from this persistent storage. Currently the headless service supports React's AsyncStorage and the Vega File System. MMKV is not supported. In this example, we will use AsyncStorage.

Here are some examples of component implementations that leverages AsyncStorage.

Copied to clipboard.

// Interactive Component
// Inside "com.amazondeveloper.media.sample.main".
const onLoginChange = async (newStatus: boolean) => {
  await AsyncStorage.setItem('loginStatus', newStatus);
};

Copied to clipboard.

// Service Component
// Inside "com.amazondeveloper.media.sample.interface.provider".
const getLoginStatus = async (): boolean => {
  return await AsyncStorage.getItem('loginStatus');
};

Register the handleReadStatus() handler to return the login status when it's invoked by the system. getLoginStatus() must read the status through persistent storage as seen previously. getLoginStatus() is app specific.

Copied to clipboard.

export class AccountLoginWrapper {
  async getAccountLoginStatus(): IStatus {
    return accountLoginServerComponent.makeStatusBuilder()
      .status(
        // Fetch status from persistent storage.
        (await getLoginStatus())
          ? StatusType.SIGNED_IN
          : StatusType.SIGNED_OUT)
      .build();
  }

  createAccountLoginHandler(): IAccountLoginHandlerAsync {
    return {
      handleReadStatus: async (): Promise<IStatus> => {
        console.log('handleReadStatus() invoked.')
        return await this.getAccountLoginStatus();
      }
    };
  }
}

Next, introduce the setupAccountLoginServer() method in the AccountLoginWrapper class. This method accepts an IComponentInstance object as a parameter. Implement it in the following manner:

  1. Create a singleton instance of IAccountLoginServerAsync and store it in the member variable accountLoginServer.
  2. Next, invoke setHandlerForComponent(), passing in the handleReadStatus() handler created in Step 4, and ComponentInstance already provided.

Copied to clipboard.

export class AccountLoginWrapper {
  setupAccountLoginServer(componentInstance: IComponentInstance) {
    console.log('setupAccountLoginServer() invoked.');
    try {
      this.accountLoginServer = accountLoginServerComponent.getOrMakeServer();
    } catch (error) {
      this.accountLoginServer = undefined;
      console.error(
        'setupAccountLoginServer() failed creating Account Login server: ',
        error,
      );
      return;
    }

    try {
      this.accountLoginServer?.setHandlerForComponent(this.createAccountLoginHandler(), componentInstance);
    } catch (error) {
      console.error(
        'setupAccountLoginServer() failed to set handler: ',
        error,
      );
    }
    console.log('setupAccountLoginServer() completed.');
  }
}

Step 6: Update the account login status

To update the account login status, use the updateStatus() method asynchronously. Be sure to store the login status in persistent storage before calling this function.

Copied to clipboard.

export class AccountLoginWrapper {
  async updateStatus(loginStatus: boolean) {
    console.log('updateStatus() invoked.');
    const status = accountLoginServerComponent.makeStatusBuilder()
      .status(loginStatus ? StatusType.SIGNED_IN : StatusType.SIGNED_OUT,)
      .build();
    try {
      this.accountLoginServer?.updateStatus(status);
    } catch (error) {
      console.error(
        'updateStatus() Failed updating login status: ',
        error,
      );
    }
  }
}

Step 7: Integrate the Account Login server with the service lifecycle

To make this integration, the AccountLoginWrapper class exposes two methods: onStart() and onStop().

onStart()

The onStart() method initializes the Account Login server by calling setupAccountLoginServer() with the provided IComponentInstance.

onStop()

The onStop() method adds the cleanup logic needed when the service stops.

After implementing these, create a singleton instance called AccountLoginWrapperInstance and use it to expose two equivalent top-level methods: onStartService() and onStopService(). Use these methods for both the service and interactive components to start and stop the Account Login server.

Copied to clipboard.

export class AccountLoginWrapper {
  onStart(componentInstance: IComponentInstance): Promise<void> {
    this.setupAccountLoginServer(componentInstance);
    return Promise.resolve();
  }

  onStop(): Promise<void> {
    // Add any cleanup code here.
    return Promise.resolve();
  }
}

export const AccountLoginWrapperInstance = new AccountLoginWrapper();

export const onStartService = (componentInstance: IComponentInstance): Promise<void> => {
  return AccountLoginWrapperInstance.onStart(componentInstance);
};

export const onStopService = (): Promise<void> => {
  return AccountLoginWrapperInstance.onStop();
};

Step 8: Implement a headless service

A headless service is a background process that runs without a user interface, utilizing its own React Native runtime. This service is key for responding to login status requests and can be created by the following steps.

  1. Create a new file named service.js in your app's root directory.
  2. Import AccountLoginWrapper, created in the previous steps, and also import headless-task-manager.

    Copied to clipboard.

     import { HeadlessEntryPointRegistry } from '@amazon-devices/headless-task-manager';
     import { onStartService, onStopService } from './src/AccountLoginWrapper';
    
  3. Define the start and stop entry points.

    Copied to clipboard.

     HeadlessEntryPointRegistry.registerHeadlessEntryPoint2(
       'com.amazondeveloper.media.sample.interface.provider::onStartService',
       () => onStartService
     );
    
     HeadlessEntryPointRegistry.registerHeadlessEntryPoint2(
       'com.amazondeveloper.media.sample.interface.provider::onStopService',
       () => onStopService
     );
    
    • Replace com.amazondeveloper.media.sample with your app's actual package ID.
    • The entry point naming convention is: <app_package_id>.interface.provider::onStartService or ::onStopService.

Fire TV system components automatically invoke onStartService() to initialize the Account Login server as a headless service. During this process, the handleReadStatus() function is registered for the specific service component instance.

When the system invokes this handler later, handleReadStatus() returns the current login state (signed-in or signed-out) as reported by the app.

Step 9: Initialize the Account Login server to report the login state

The interactive component (UI app) must also initialize the Account Login server to report the user’s login state using the updateStatus() method.

Copied to clipboard.

// Inside "com.amazondeveloper.media.sample.main".
useEffect(() => {
  AccountLoginWrapperInstance.onStart(componentInstance);

  return () => {
    AccountLoginWrapperInstance.onStop();
  };
}, []);

Step 10: Send login status updates

The interactive component (UI app) must call the updateStatus() method with the current login status in the following scenarios:

  1. When the app is launched.
  2. When the user's login status changes

Each time, the status must be stored in persistent storage before calling updateStatus(), so that handleReadStatus() retrieves and returns the correct value. This way, the system consistently reflects the user's authentication state, providing a seamless user experience.

This setup allows your app to manage login status requests, even when the main app is not actively running.

You have now created a single instance of IAccountLoginServerComponentAsync. We have established a link between the handleReadStatus() method and the IComponentInstance, so that any read requests are directed to the service component. Doing this will handle Account Login status queries. It also calls the updateStatus() method, which serves a dual purpose. It should be called either to force a login status update, or whenever the status changes.

Troubleshooting Vega Media Account Login

There are several possible issues that can arise when integrating with Account Login, but here are some more common ones.

Confirm The service component starts

See if your updateStatus() calls being picked up. This confirms the service component can be started without crashing.

To do this, begin capturing logs before running kepler device launch-app com.your.app.id.interface.provider and you should be able to watch your service component get through its start-up sequence. You should see logs corresponding to your service component name, and see if any crashes occur during this phase. The logs are located after registerHeadlessEntryPoint() (onStartService) entrypoint is reached.

If you don't see this, you may see logs indicating a failure or crash. In that event, Vega Crash Debugging Documentation is a useful reference.

Confirm the handler is called

Once you are confident your service is starting successfully, confirm that your service properly registers a handle using setHandlerForComponent() . You can add logs around this invocation to confirm this path is getting hit. You will see equivalent logs from for handle_set_response_handler for your service component ID to confirm.

You should also add logs around handleReadStatus() to confirm it is functioning as expected.


Last updated: Sep 30, 2025