# Customers and attributes

> Anonymous and signed-in users, aliases, merging, customer attributes, last-seen details and search.

Customer tracking follows RevenueCat's model, so apps and data move over unchanged.

## Identity

SDKs start with an anonymous id (`$anon:<uuid>`; RevenueCat's `$RCAnonymousID:<hex>` is accepted too). When your user signs in, `identify` moves the anonymous customer's purchases and attributes to the signed-in user. The anonymous id lives on as an alias, so any endpoint called with it finds the user. `original_app_user_id` is the first id the customer had.

```sh
npx -y @salesdogg/cli customers identify --anonymous-id '$anon:6f1c…' --app-user-id user_1
```

## Attributes

- String values up to 500 characters; `null` or `""` deletes one.
- Custom keys start with a letter (letters, digits, `_`, `-`), at most 40 characters and 50 keys per customer.
- `$` keys are reserved: `$email`, `$displayName`, `$phoneNumber`, campaign attribution (`$mediaSource`, `$campaign`, `$adGroup`, `$ad`, `$keyword`, `$creative`) and integration ids.
- Each write may carry `updated_at_ms`; an older write never replaces a newer one.
- Apps write attributes with the public key but cannot read them back, and campaign attribution set from an app cannot be overwritten by the app.

```ts
await sd.setAttributes(user.id, { $email: user.email, plan_tier: "gold" });
```

Anyone holding your public key can write attributes for any user id, as with RevenueCat, so never base access decisions on attributes.

## Last seen

SDK requests send `X-Platform`, `X-Platform-Version`, `X-App-Version` and `X-Locale`; the customer keeps the latest, plus a country taken from the locale. A server-side access check with a secret key does not count as the user being seen unless it sends `X-Platform`.

## Search

`GET /v1/customers` (`npx -y @salesdogg/cli customers list`) filters by `search` (id or alias prefix, exact e-mail), `entitlement`, `active`, `platform`, `country`, `app_version`, `store`, `attribute_key` / `attribute_value`, and first- or last-seen ranges. The dashboard exports any filtered list as CSV.

## Grants

Give access without a purchase, for support cases, partners or tests:

```sh
npx -y @salesdogg/cli customers grant user_1 --entitlement pro --expires-at 2026-12-31T00:00:00Z
npx -y @salesdogg/cli customers revoke-grant user_1 pro
```
