Skip to main content

Identity: users, login and tags

Every subscription belongs to a user. The rules below decide which user, and what happens to tags along the way. They are implemented once, in the API (identity.ts), and every SDK and the REST API go through the same code.

Anonymous by default

A device that subscribes without an externalId gets a fresh anonymous user (externalId: null). It can be targeted (by segment, all, or subscriptionIds), it can carry tags, it just is not linked to anyone you know.

login(externalId)

  • First time seen → the anonymous user is renamed. Tags set before login survive.
  • Already exists (another device logged in earlier) → the device moves; the existing user's properties win, the anonymous tags are dropped.
  • A user with two phones ends up with one user row and two subscriptions — setTags on either phone updates both.

Calling login with the id the device already has is a no-op.

logout()

The device moves to a new anonymous user. The user you logged out of keeps their tags and any other devices; logging back in on that device finds everything as it was.

Broadcasts (all, segments that match) still reach a logged-out device — it is a device that opted in, just not one tied to a person. Use unsubscribe() if the intent is "stop notifying this device".

Tags

tags is a flat map of string | number | boolean, up to 64-char keys, 512-char string values. Writes are merges:

PATCH /v1/subscriptions/:id/user
{ "tags": { "plan": "premium", "streak": null } }
  • keys given are set,
  • a null value removes the key (removeTags),
  • keys not mentioned are untouched.

An SDK never has to read-modify-write the whole map. Tags live on the user, so the write reaches every device the user owns; each subscription row carries a write-through copy so segments and personalisation never need a join.

language, timezone, country work the same way (set = replace).

Server-side identity

With a secret key your backend can act on users without a device in sight:

# set tags on someone before their first device registers — creates the user
curl -X PATCH https://push.example.com/v1/users/by-external-id/user_123 \
-H "Authorization: Bearer sk_live_…" -H "content-type: application/json" \
-d '{ "tags": { "plan": "premium" }, "language": "tr", "timezone": "Europe/Istanbul" }'

When that user's phone later calls login("user_123"), the device moves under this row and inherits the tags.

Orphans and deletion

  • An anonymous user whose last device is deleted, unsubscribed or moved away is deleted.
  • A named user lives on without devices.
  • DELETE /v1/users/:id (or /by-external-id/:externalId) removes the user and every device — the GDPR path. Past events keep only ids.

Cheat sheet

CallEffect on the device's user
subscribe (no externalId)new anonymous user (first time) or unchanged
subscribe (with externalId)same as login
login(id)rename or move (see above)
logout()new anonymous user
setTags / removeTags / setLanguagemerge into the user, copied to all devices
unsubscribe()device deleted; anonymous orphan deleted
PATCH /v1/users/…server-side merge, same copy rule