How do customers, app user IDs and logIn work?
A customer is one person. They can have several app user IDs: the anonymous ID the SDK made on first launch and the ID you pass to logIn. Purchases belong to the customer, so they follow the person across IDs. When a purchase that already belongs to someone else is restored, the project's transfer behaviour decides who gets it.
Anonymous app user IDs#
Without an ID, the SDK generates one like $RCAnonymousID:4f2c9a... and stores it on the device. RevenueDot creates the customer the first time the SDK asks for customer info (GET /v1/subscribers/{id} answers 201 for a new customer).
- Anonymous IDs are fine for apps without accounts. Restores then move purchases to the device's current anonymous ID.
- An anonymous ID is lost when the app is deleted, so give users an account if they buy on several devices.
logIn: attach your own ID#
Purchases.logIn("user_42") calls POST /v1/subscribers/identify with the current and the new ID. What happens depends on who already exists (apps/server/src/repo/customers.ts):
| Current ID | user_42 already exists? |
Result | Status |
|---|---|---|---|
| Anonymous, with no other IDs | No | The anonymous customer takes user_42 as a second ID and keeps its purchases |
201 |
| Anonymous, with no other IDs | Yes | The anonymous customer is merged into user_42, unless user_42 already has an anonymous ID of its own |
200 |
| A known ID (not anonymous) | No | A new, empty customer user_42 |
201 |
| A known ID | Yes | Switches to user_42; nothing is merged |
200 |
logOut makes the SDK generate a new anonymous ID. GET /v2/projects/{project_id}/customers/{customer_id}/aliases lists every ID of a customer, and any of them works as customer_id in REST API v2.
Who owns a restored purchase#
A store purchase has one owner. When a different customer posts the same receipt (a restore or syncPurchases() on a second account, or after reinstalling), RevenueDot applies these rules in order (apps/server/src/services/purchases.ts):
- The owner is anonymous only: the old anonymous customer is merged into the one posting the receipt. No setting changes this.
- The poster is anonymous only and the owner has a real ID: the poster's anonymous ID is merged into the owner, like logging in as them.
- Otherwise the project's
transfer_behaviordecides:
transfer_behavior |
What happens | Event |
|---|---|---|
transfer (default) |
The purchase moves to the customer who restored it | TRANSFER |
transfer_if_no_active |
It moves only if the current owner has no active subscription; otherwise the receipt post fails with 7102 "receipt already in use" | TRANSFER when it moves |
keep |
It stays with the first owner; the receipt post fails with 7102 | none |
share |
The two customers are merged, so both IDs have access | none |
Set it in the dashboard (Project settings) or with the API. sandbox_transfer_behavior overrides it for sandbox purchases; null uses the main setting.
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID" \
-H "Authorization: Bearer $SECRET_KEY" -H "Content-Type: application/json" \
-d '{"transfer_behavior":"transfer_if_no_active","sandbox_transfer_behavior":"transfer"}'Store notifications never move a purchase: only a receipt posted from a device does. A TRANSFER webhook carries transferred_from and transferred_to, the ID lists of both customers.
Attributes#
Customers carry key-value attributes. The SDK sets them with setEmail, setDisplayName, setAttributes and similar calls (POST /v1/subscribers/{app_user_id}/attributes). Keys that start with $ are reserved names such as $email; RevenueDot refuses an invalid $email with code 7263 and saves the others. Attributes appear in webhooks as subscriber_attributes and in REST API v2 under /attributes. A null value deletes an attribute.