Identifying Customers
RevenueCat provides a source of truth for a customer's subscription status across different platforms. User identity is one of the most important components of many mobile applications, and it's crucial to make sure the subscription status that RevenueCat is tracking is associated with the correct user.
What is a customer in RevenueCat?
In RevenueCat, the term "customer" refers to the person using an app that utilizes RevenueCat to handle or track the user's purchases. Every customer has a unique CustomerInfo
object that can be referenced via their App User ID. As described below, the RevenueCat SDK will automatically generate anonymous App User IDs for customers unless a custom App User ID is provided by the developer. Some apps will use a combination of both custom and anonymous App User IDs, or only one App User ID type.
If you use a combination of anonymous and custom App User IDs, it’s expected that Customers may be merged over time due to various actions they perform within the app, like logins or restores. Scenarios explaining when merges occur are covered in more detail below, and will depend on the restore behavior you select for your project. When a merge of customers occurs, there will be only one App User ID within the original_app_user_id
field. If you’re listening to Webhooks, the other App User IDs associated with the customer will be within an array in the aliases
field. When referenced via the SDK or API, any merged App User IDs will all be treated as the same “customer”. Looking up any of the merged App User IDs in RevenueCat will return the same CustomerInfo
, customer history, customer attributes, subscription status, etc.
Overall, the concept of a customer in RevenueCat is central to the platform's ability to track and manage in-app purchases and subscriptions. By maintaining a detailed record of each customer's activity, developers can gain insights into their app's revenue streams, allowing them to make informed decisions to optimize their monetization strategies.
Anonymous App User IDs
If you don't provide an App User ID when instantiating the Purchases SDK, RevenueCat will generate a new random App User ID for you and cache it on the device. In the event that the user deletes and reinstalls the app, a new random App User ID will be generated.
- Swift
- Obj-C
- Kotlin
- Kotlin Multiplatform
- Java
- Flutter
- React Native
- Cordova
- Capacitor
- Unity
Purchases.configure(withAPIKey: <my_api_key>)
[RCPurchases configureWithAPIKey:@<my_api_key>];
Purchases.configure(PurchasesConfiguration.Builder(this, <api_key>).build())
Purchases.configure(apiKey = "<api_key>")
Purchases.configure(new PurchasesConfiguration.Builder(context, <api_key>).build());
await Purchases.configure(PurchasesConfiguration(<public_sdk_key>));
Purchases.configure({apiKey: <public_sdk_key>});
Purchases.configureWith({ apiKey: <public_sdk_key> });
Purchases.configure({ apiKey: <public_sdk_key> });
// The SDK can be configured through the Unity Editor.
// See Unity installation instructions https://docs.revenuecat.com/docs/unity
// If you'd like to do it programmatically instead,
// make sure to check "Use runtime setup" in the Editor and then:
Purchases.PurchasesConfiguration.Builder builder = Purchases.PurchasesConfiguration.Builder.Init("api_key");
Purchases.PurchasesConfiguration purchasesConfiguration = builder.Build();
purchases.Configure(purchasesConfiguration);
For SDK versions 3+, anonymous App User IDs are always prefixed with $RCAnonymousID:
, which may be useful for identifying anonymous users on your server.
If your app does not enforce the use of custom App User IDs, you should consider revealing the user's App User ID in your app's support page. This will help expedite support requests, including those with RevenueCat Support.
You can find the App User ID on the CustomerInfo
object.
Logging In with a Custom App User ID
Setting your own App User ID will allow you to reference users in the RevenueCat dashboard, via the API, as well as in the webhooks and other integrations.
Using an externally managed App User ID also provides a mechanism by which to restore purchases in a few scenarios:
- When a user deletes and reinstalls your app - using the same App User ID will ensure they still have access to subscriptions previously started without requiring a restore .
- When the user logs in on multiple devices - you can honor a subscription that was purchased on one device across any other platform.
App User IDs are case-sensitive.
Provide App User ID on configuration
In certain cases, iOS may prewarm your app - this essentially means your app will be launched silently in the background to improve app launch times for your users.
If you are not using RevenueCat's anonymous IDs as described above, and are instead providing your own app user ID on configuration, do not call configure
in application:didFinishLaunchingWithOptions:
. Instead, call the configure
method in your root view controller's initialization method.
If you have your own App User IDs at app launch, you can pass those on instantiation to Purchases. Make sure to not hard-code this identifier, if you do all users will be considered the same one and will share purchases.
- Swift
- Obj-C
- Kotlin
- Kotlin Multiplatform
- Java
- Flutter
- React Native
- Cordova
- Capacitor
- Unity
Purchases.configure(withAPIKey: <my_api_key>, appUserID: <my_app_user_id>)
[RCPurchases configureWithAPIKey:@<my_api_key> appUserID:@<my_app_user_id>];
Purchases.configure(PurchasesConfiguration.Builder(this, <api_key>).appUserID(<my_app_user_id>).build())
Purchases.configure(apiKey = "<api_key>") {
appUserId = "<app_user_id>"
}
Purchases.configure(new PurchasesConfiguration.Builder(context, <api_key>).appUserID(<my_app_user_id>).build());
await Purchases.configure(
PurchasesConfiguration(<public_sdk_key>)
..appUserID = <my_app_user_id>
);
Purchases.configure({apiKey: <public_sdk_key>, appUserID: <my_app_user_id>});
Purchases.configureWith({ apiKey: <public_sdk_key>, appUserID: <my_app_user_id> });
await Purchases.configure({ apiKey: <public_sdk_key>, appUserID: <my_app_user_id> });
// The appUserID can be set through the Unity Editor.
// See Unity installation instructions https://docs.revenuecat.com/docs/unity
// If you'd like to do it programmatically instead,
// make sure to check "Use runtime setup" in the Editor and then:
Purchases.PurchasesConfiguration.Builder builder = Purchases.PurchasesConfiguration.Builder.Init("api_key");
Purchases.PurchasesConfiguration purchasesConfiguration =
builder.SetUserDefaultsSuiteName("user_default")
.SetDangerousSettings(new Purchases.DangerousSettings(false))
.SetObserverMode(true)
.SetUseAmazon(false)
.SetAppUserId(appUserId)
.Build();
purchases.Configure(purchasesConfiguration);
Often times, you may not have your own App User IDs until later in the application lifecycle. In these cases, you can pass the App User ID later through the .logIn()
method.
Provide App User ID after configuration
If your app doesn't receive its own App User ID until later in its lifecycle, you can set (or change) the App User ID at any time by calling .logIn()
. If the logged in identity does not already exist in RevenueCat, it will be created automatically.
- Swift
- Obj-C
- Kotlin
- Kotlin Multiplatform
- Java
- Flutter
- React Native
- Cordova
- Capacitor
- Unity
// Configure Purchases on app launch
Purchases.configure(withAPIKey: <my_api_key>)
// ...
// Later log in provided user Id
Purchases.shared.logIn(<my_app_user_id>) { (customerInfo, created, error) in
// customerInfo updated for my_app_user_id
}
// Configure Purchases on app launch
[RCPurchases configureWithAPIKey:@<my_api_key>];
//...
// Later log in provided user Id
[[RCPurchases sharedPurchases] logIn:@<my_app_user_id> completion:^(RCCustomerInfo *customerInfo, BOOL created, NSError *error) {
// customerInfo updated for my_app_user_id
}];
// Configure Purchases on app launch
Purchases.configure(PurchasesConfiguration.Builder(this, <api_key>).build())
//...
// Later log in provided user Id
Purchases.sharedInstance.loginWith(<my_app_user_id>, ::showError) { customerInfo, created ->
// customerInfo updated for my_app_user_id
}
// Configure Purchases on app launch
Purchases.configure(apiKey = "<api_key>")
// Later log in with the provided user Id
Purchases.sharedInstance.logIn("<my_app_user_id>", ::showError) { customerInfo, created ->
// customerInfo updated for my_app_user_id
}
// Configure Purchases on app launch
Purchases.configure(new PurchasesConfiguration.Builder(context, <api_key>).build());
//...
// Later log in provided user Id
Purchases.getSharedInstance().logIn(<my_app_user_id>, new LogInCallback() {
@Override
public void onReceived(@NotNull CustomerInfo customerInfo, boolean created) {
// customerInfo updated for my_app_user_id
}
@Override
public void onError(@NotNull PurchasesError error) {
}
});
// Configure Purchases on app launch
await Purchases.configure(PurchasesConfiguration(<public_sdk_key>));
//...
// Later log in provided user Id
LogInResult result = await Purchases.logIn(<my_app_user_id>);
// Configure Purchases on app launch
Purchases.configure({apiKey: <public_sdk_key>});
//...
// Later log in provided user Id
const { customerInfo, created } = await Purchases.logIn(<my_app_user_id>);
// customerInfo updated for my_app_user_id
// Configure Purchases on app launch
Purchases.configureWith({ apiKey: <public_sdk_key> });
//...
// Later log in provided user Id
Purchases.logIn(
<my_app_user_id>,
({ customerInfo, created }) => {
// customerInfo updated for my_app_user_id
},
error => {
}
);
// Configure Purchases on app launch
await Purchases.configure({ apiKey: <public_sdk_key> });
//...
// Later log in providing user Id
try {
const logInResult = await Purchases.logIn({ appUserID: <my_app_user_id>});
} catch (error) {
// Handle error logging in
}
// configure the SDK either through the Editor or through
// programmatic setup (see section above), then:
var purchases = GetComponent<Purchases>();
purchases.LogIn(<myAppUserUD>, (customerInfo, created, error) =>
{
if (error != null)
{
// show error
}
else
{
// show customerInfo
}
});
logIn()
method alias behavior
When logging in from an Anonymous ID to a provided custom App User ID, RevenueCat will decide whether the identities should be merged (aliased) into the same CustomerInfo object or not. This is decided depending on whether the provided custom App User ID already exists, and if it does exist whether it has an anonymous alias already.
Current App User ID | Provided Custom App User ID already exists? | Provided Custom App User ID has anonymous alias? | Result |
---|---|---|---|
Anonymous | No | N/A | Anonymous ID and Provided ID have CustomerInfo merged. |
Anonymous | Yes | No | Anonymous ID and Provided ID have CustomerInfo merged. |
Anonymous | Yes | Yes | CustomerInfo transfers to Provided ID, no aliases created. |
Non-anonymous | Any | Any | CustomerInfo transfers to Provided ID, no aliases created. |
Logging Out
When an identified user logs out of your application you should call the logOut()
method within the SDK. This will generate a new anonymous App User ID for the logged out state. However, if you plan to use only custom App User ID's, you can follow the instructions here.
Logging back in
To log in a new user, the provided App User ID should be set again with .logIn()
.
Switching accounts
If you need to switch from one provided App User ID to another, it's okay to call the .logIn()
method directly - you do not need to call logOut()
first.
Sharing Subscriptions Across Apps and Platforms
Apps within the same RevenueCat Project share the same App User ID namespace, which means that they also share subscriptions. A user logged in to the same user ID in different apps of the same Project will have access to the same entitlements. This allows sharing of subscription status between different apps, even on different platforms.
Note that anonymous App User IDs are not able to share subscription status across apps and platforms, so you'll need to identify with a custom App User ID via your own authentication system.
A user can only manage their subscription on the platform it was purchased from.
How to only use Custom App User IDs
To only use custom App User IDs, you must take care not to generate any anonymous App User IDs in the SDK. Anonymous App User IDs are generated when the SDK is configured without a provided custom App User ID, and are also created on logOut()
as mentioned above. Many apps use a combination of anonymous App User IDs and their own custom App User IDs via an authentication system to provide flexibility in their user purchase flows. However, some applications are intended only to be used while using a known App User ID, without anonymous users at all. Some limits of anonymous IDs include added difficulty in personalization of user experiences, optimization of monetization strategies, and not being able to share subscriptions across platforms. Depending on the application's transfer behavior, a provided user's subscription may be transferred to an anonymous ID - which can only be brought back through a restore on the original purchase platform.
In any case, to never see Anonymous IDs, you only need to make sure to do the following: Only configure the SDK with a custom App User ID, and never call .logout()
.
Only Configure the SDK with a custom App User ID
The most frequent place that anonymous App User IDs are created is when the SDK is first configured. Calling .configure
on the SDK without providing a known user ID will cause the SDK to generate an Anonymous ID for the current user. To avoid anonymous IDs, you will need to identify the user’s ID, before configuring the SDK. Many applications use their own authentication system, often shared on multiple platforms, to identify users and provide their unique App User IDs.
Do not Logout the User
After the user is logged in with a known App User ID, they may want to logout or switch accounts on your application - logout and then login with a different known ID. However, calling logout in the SDK will result in an anonymous App User ID being created. Simply do not logout the SDK. In the case of switching accounts, you can call login when the user logs in to the different account with their new App User ID.
Tips for Setting App User IDs
ℹ️ App User IDs Should Not Be Guessable |
---|
RevenueCat provides subscription status via the public API, having App User IDs that are easily guessed is not good. It is recommended to use a non-guessable pseudo-random ID. |
ℹ️ Keep App User IDs shorter than 100 characters |
---|
App User IDs should not be longer than 100 characters. |
⚠️ Don't set emails as App User IDs |
---|
For the above reasons about guessability, and GDPR compliance, we don't recommend using email addresses as App User IDs |
⚠️ Don't set IDFA as App User IDs |
---|
Advertising identifiers should not be used as App User IDs since they can be easily rotated and are not unique across users if limit ad tracking is enabled. |
You should never hardcode a string as an App User ID, since every install will be treated as the same user in RevenueCat. This will create problems and could unlock entitlements for users that haven't actually purchased.
Every app user ID must be unique per user. If you don't have your own user IDs for some of your users, you should not pass any value for the App User ID on configuration which will rely on the anonymous IDs created by RevenueCat.
Certain App User IDs are blocked in RevenueCat. This is by design to help developers that may be unintentionally passing non-unique strings as user identifiers. The current block-list is: 'no_user'
, 'null'
, 'none'
, 'nil'
, '(null)'
, 'NaN
, '\\x00'
(NULL
character), ''
(empty string), 'unidentified'
, 'undefined'
, 'unknown'
, 'anonymous'
, 'guest'
, '-1'
, '0'
, '[]'
, '{}'
, '[object Object]'
and any App User IDs containing the character /
.
Next Steps
- Enrich your app by reacting to the user's current subscription status
- If you're moving to RevenueCat from another system, see our guide on migrating your existing subscriptions
- Once you're ready to test your integration, you can follow our guides on testing and debugging