User IDs can be used for reporting purposes, storing the read status of Clips and Stories, followed categories and user preferences. By default, the Storyteller SDK creates an externalId for users when the SDK is first initialized.
Each time null value is passed to Storyteller.initialize(userInput = null) SDK will handle user management and the default externalId is stored until the user uninstalls the app.
However, if you have a user account system then you may wish to set your own user IDs within the Storyteller SDK. The externalId should be an identifier that is unique per user and does not change. Therefore, using something like the user's email address is not a good choice for an externalId as the user may change it at some point in the future. However, using a unique UUID/GUID would be a good choice as it is guaranteed to not change over time.
Note: Storyteller SDK is not storing any user IDs passed to initialize. We follow VPPA compliance and the user IDs are hashed during the App runtime
For VPPA compliance, the SDK does not expose the current user ID. If you need it for your app logic, keep it in your own user/session store.
You can access the following SDK information via these properties:
Storyteller.currentApiKey// Returns the current API key (avoid logging)Storyteller.isInitialized// Returns whether the SDK is initializedStoryteller.version// Returns the SDK version
If you use login in your app and wish to allow users to logout and log back in as a new user (or proceed as an anonymous user) then when a user logs out you should call initialize again, specifying a new externalId. Note that this will reset the local store of which Pages the user has viewed.
In order to set the locale to the Storyteller SDK, call the following method:
Storyteller.user.setLocale(locale:String?)
Ensure the locale parameter uses an ISO 639-1 two-letter if available, otherwise, use an ISO 639-2 three-letter language code for precise language specification:
One of the features of Storyteller is the ability to set custom attributes for requests. These
custom attributes can be used to provide additional information about a request, such as the user's
location or the device they are using. It can be also used for audience targeting.
To set a custom attribute, you can use the setCustomAttribute() method of the Storyteller.user
object. This method takes two parameters: the key of the custom attribute and its value. For
example, to set a custom attribute for the user's location, you can use the following code:
This will add a custom attribute to all requests made by the user, with the key "location" and the
value "New York". You can set multiple custom attributes by calling setCustomAttribute() multiple
times with different keys and values. Only String values are allowed. The key
__storyteller_followed_category_attribute_keys__ is reserved for SDK use and must not be passed
to setCustomAttribute() or removeCustomAttribute().
Setting custom attributes should be done after Storyteller SDK is initialized.
To remove custom attribute, you can use the removeCustomAttribute() method of
the Storyteller.user object. This method takes one parameter: the key of the custom attribute to
remove. For example, to remove a custom attribute for the user's location, you can use the following
code
To set a followed category, call the addFollowedCategory() function for a singular category
or addFollowedCategories() for a collection. Access these methods through the Storyteller.user
object. For instance, to designate a followed category attribute for the user, implement the
following code:
These categories are stored locally and used to decide whether the plus or tick icon is displayed in
the clips UI.
Setting followable categories attributes should be done after Storyteller SDK is initialized.
Use setFollowedCategories() when you have the complete list of categories that should be followed
and need the SDK's current followed category state to match that list exactly. This method is a
suspend function, so you can wait for the SDK to update local followed category IDs and request
personalization attributes before continuing.
Category IDs that are absent from a successful category-details response or do not have usable
metadata are omitted. The successfully resolved IDs become the exact replacement set. If none
resolve, the followed category set is cleared. Request failures still throw without changing the
previous followed IDs or their derived personalization attributes.
When any app-managed followed-category mutation changes Followings while a selected top-level Clips
feed is empty, the SDK automatically refreshes that feed. If a host screen such as Manage Favourites
temporarily covers the player, the refresh runs when the selected feed becomes visible again; the
host does not need to call reloadData(). This applies to both the full-screen Clips Player and
Embedded Clips. A non-empty or deselected feed is intentionally not reloaded, preserving playback
and position. When the host explicitly calls reloadData(), the SDK refreshes the currently selected
For You or Following feed without switching to another feed.
To remove a followed category, use the removeFollowedCategory() method of the Storyteller.user
object. This method requires a single parameter—the key of the followed category to be removed. For
example, to remove a followable category related to the user's location, utilize the following code:
The SDK handles following activity internally and automatically syncs with Storyteller's servers. This simplifies integration by removing the need for custom following logic in your app.
This means that all of the above methods on Storyteller.user are no-ops in this mode. Also the categoryFollowActionTaken is not called.
To check if a specific category is being followed, use the isCategoryFollowed() method. To retrieve a list of all currently followed categories, use the getFollowedCategories() method.
Use Storyteller.user.getFollowableCategories() to fetch backend-backed followable
category metadata. The response includes a flat categories list, with isFollowed
derived from the SDK's locally stored followed categories. Continue to use
isCategoryFollowed() and followedCategories when you only need the SDK's locally
stored followed-category IDs.
StorytellerFollowableCategory.type contains the normalized backend category type code when one is
available: nonblank dynamic codes are trimmed and lowercased. Missing or blank codes fall back to
the trimmed legacy category type with its original backend casing. Storyteller-managed follow and
unfollow activity uses the same resolved type. Android also retains compatible Other state for
custom category types that were previously represented as Other, so existing Following feeds and
personalization continue to recognize those categories.
{"slug": "users", "page_title": "Working with Users", "page_url": "Users/", "canonical_url": "/android/Users/", "markdown": "# Working with Users\n\nUser IDs can be used for reporting purposes, storing the read status of Clips and Stories, followed categories and user preferences. By default, the Storyteller SDK creates an `externalId` for users when the SDK is first initialized.\nEach time `null` value is passed to `Storyteller.initialize(userInput = null)` SDK will handle user management and the default `externalId` is stored until the user uninstalls the app.\n\n**However, if you have a user account system then you may wish to set your own user IDs within the Storyteller SDK. The `externalId` should be an identifier that is unique per user and does not change. Therefore, using something like the user's email address is not a good choice for an `externalId` as the user may change it at some point in the future. However, using a unique UUID/GUID would be a good choice as it is guaranteed to not change over time.**\n\n> Note: Storyteller SDK is not storing any user IDs passed to initialize. We follow VPPA compliance and the user IDs are hashed during the App runtime\n\n## Showcase examples\n\n- [Compose \u2014 user ID, custom attributes, locale (`AccountScreen`)](https://github.com/getstoryteller/storyteller-showcase-android/blob/main/compose/app/src/main/java/com/getstoryteller/storytellershowcaseapp/ui/features/account/AccountScreen.kt#L67)\n- [Compose \u2014 initialization + user attribute updates (`StorytellerServiceImpl`)](https://github.com/getstoryteller/storyteller-showcase-android/blob/main/compose/app/src/main/java/com/getstoryteller/storytellershowcaseapp/data/StorytellerServiceImpl.kt#L102)\n- [XML \u2014 login + verify flow (user ID)](https://github.com/getstoryteller/storyteller-showcase-android/blob/main/xml/app/src/main/java/com/getstoryteller/storytellershowcaseapp/ui/features/login/LoginViewModel.kt#L53)\n- [XML \u2014 initialization + user attribute updates (`StorytellerServiceImpl`)](https://github.com/getstoryteller/storyteller-showcase-android/blob/main/xml/app/src/main/java/com/getstoryteller/storytellershowcaseapp/data/StorytellerServiceImpl.kt#L85)\n\n## Setting a User ID\n\nIn order to supply a `externalId` to the Storyteller SDK, call the following method:\n\n```kotlin\n Storyteller.initialize(\n apiKey = \"[APIKEY]\",\n userInput = StorytellerUserInput(\"unique-externalId\"),\n onSuccess = {\n // onSuccess action\n },\n onFailure = { error ->\n // onFailure action\n }\n )\n```\n\nThis should be called as soon as the value for the `externalId` is known in your app. For example, this could be on app start or when a user logs in.\n\n## Current User Information\n\nFor VPPA compliance, the SDK does not expose the current user ID. If you need it for your app logic, keep it in your own user/session store.\n\nYou can access the following SDK information via these properties:\n\n```kotlin\nStoryteller.currentApiKey // Returns the current API key (avoid logging)\nStoryteller.isInitialized // Returns whether the SDK is initialized\nStoryteller.version // Returns the SDK version\n```\n\n## Changing Users\n\nIf you use login in your app and wish to allow users to logout and log back in as a new user (or proceed as an anonymous user) then when a user logs out you should call `initialize` again, specifying a new `externalId`. Note that this will reset the local store of which Pages the user has viewed.\n\n## Setting the User's Locale\n\nIn order to set the locale to the Storyteller SDK, call the following method:\n\n```kotlin\n Storyteller.user.setLocale(locale: String?)\n```\n\nEnsure the locale parameter uses an ISO 639-1 two-letter if available, otherwise, use an ISO 639-2 three-letter language code for precise language specification:\n\n```kotlin\n Storyteller.user.setLocale(\"fr\")\n Storyteller.user.setLocale(\"ace\")\n```\n\nTo clear or reset the locale settings, simply pass `null` as the parameter:\n\n```kotlin\n Storyteller.user.setLocale(null)\n```\n\n## User Customization\n\n### Custom Attributes\n\nOne of the features of Storyteller is the ability to set custom attributes for requests. These\ncustom attributes can be used to provide additional information about a request, such as the user's\nlocation or the device they are using. It can be also used for audience targeting.\n\n### How to Use\n\nTo set a custom attribute, you can use the `setCustomAttribute()` method of the `Storyteller.user`\nobject. This method takes two parameters: the key of the custom attribute and its value. For\nexample, to set a custom attribute for the user's location, you can use the following code:\n\n```kotlin\n Storyteller.user.setCustomAttribute(\"location\", \"New York\")\n```\n\nThis will add a custom attribute to all requests made by the user, with the key \"location\" and the\nvalue \"New York\". You can set multiple custom attributes by calling setCustomAttribute() multiple\ntimes with different keys and values. Only String values are allowed. The key\n`__storyteller_followed_category_attribute_keys__` is reserved for SDK use and must not be passed\nto `setCustomAttribute()` or `removeCustomAttribute()`.\n\n> Setting custom attributes should be done after Storyteller SDK is initialized.\n\nTo remove custom attribute, you can use the `removeCustomAttribute()` method of\nthe `Storyteller.user` object. This method takes one parameter: the key of the custom attribute to\nremove. For example, to remove a custom attribute for the user's location, you can use the following\ncode\n\n```kotlin\n Storyteller.user.removeCustomAttribute(\"location\")\n```\n\n## Followed Categories\n\nStoryteller provides a mechanism for managing followed categories associated with clips.\n\n### How to Use\n\n#### Set followed categories\n\nTo set a followed category, call the `addFollowedCategory()` function for a singular category\nor `addFollowedCategories()` for a collection. Access these methods through the `Storyteller.user`\nobject. For instance, to designate a followed category attribute for the user, implement the\nfollowing code:\n\n```kotlin\n Storyteller.user.addFollowedCategory(\"location\")\n```\n\nor\n\n```kotlin\n Storyteller.user.addFollowedCategories(listOf(\"location\", \"city\", \"country\"))\n```\n\nThese categories are stored locally and used to decide whether the plus or tick icon is displayed in\nthe clips UI.\n\n> Setting followable categories attributes should be done after Storyteller SDK is initialized.\n\nUse `setFollowedCategories()` when you have the complete list of categories that should be followed\nand need the SDK's current followed category state to match that list exactly. This method is a\nsuspend function, so you can wait for the SDK to update local followed category IDs and request\npersonalization attributes before continuing.\n\nCategory IDs that are absent from a successful category-details response or do not have usable\nmetadata are omitted. The successfully resolved IDs become the exact replacement set. If none\nresolve, the followed category set is cleared. Request failures still throw without changing the\nprevious followed IDs or their derived personalization attributes.\n\nWhen any app-managed followed-category mutation changes Followings while a selected top-level Clips\nfeed is empty, the SDK automatically refreshes that feed. If a host screen such as Manage Favourites\ntemporarily covers the player, the refresh runs when the selected feed becomes visible again; the\nhost does not need to call `reloadData()`. This applies to both the full-screen Clips Player and\nEmbedded Clips. A non-empty or deselected feed is intentionally not reloaded, preserving playback\nand position. When the host explicitly calls `reloadData()`, the SDK refreshes the currently selected\nFor You or Following feed without switching to another feed.\n\n```kotlin\ncoroutineScope.launch {\n try {\n Storyteller.user.setFollowedCategories(listOf(\"location\", \"city\"))\n } catch (error: StorytellerError) {\n // Handle category metadata request errors.\n }\n}\n```\n\n#### Remove followable category\n\nTo remove a followed category, use the `removeFollowedCategory()` method of the `Storyteller.user`\nobject. This method requires a single parameter\u2014the key of the followed category to be removed. For\nexample, to remove a followable category related to the user's location, utilize the following code:\n\n```kotlin\n Storyteller.user.removeFollowedCategory(\"location\")\n```\n\nThis function accepts a singular string parameter, removing the specified category from the locally\nstored list.\n\n## Storyteller-Managed Following\n\nThe SDK handles following activity internally and automatically syncs with Storyteller's servers. This simplifies integration by removing the need for custom following logic in your app.\n\nThis means that all of the above methods on `Storyteller.user` are no-ops in this mode. Also the `categoryFollowActionTaken` is not called.\n\n### Common methods\n\nTo check if a specific category is being followed, use the `isCategoryFollowed()` method. To retrieve a list of all currently followed categories, use the `getFollowedCategories()` method.\n\n```kotlin\nval isFollowing: Boolean = Storyteller.user.isCategoryFollowed(\"location\")\nval followedCategories: Set<String> = Storyteller.user.followedCategories\n```\n\nUse `Storyteller.user.getFollowableCategories()` to fetch backend-backed followable\ncategory metadata. The response includes a flat `categories` list, with `isFollowed`\nderived from the SDK's locally stored followed categories. Continue to use\n`isCategoryFollowed()` and `followedCategories` when you only need the SDK's locally\nstored followed-category IDs.\n\n`StorytellerFollowableCategory.type` contains the normalized backend category type code when one is\navailable: nonblank dynamic codes are trimmed and lowercased. Missing or blank codes fall back to\nthe trimmed legacy category type with its original backend casing. Storyteller-managed follow and\nunfollow activity uses the same resolved type. Android also retains compatible `Other` state for\ncustom category types that were previously represented as `Other`, so existing Following feeds and\npersonalization continue to recognize those categories.\n", "copy_markdown_include_header": false, "base_path": "android", "ai_dir": "ai", "missing_payload_behavior": "empty"}