Contacts
Every request the SDK sends belongs to a contact. This page explains how the SDK creates that contact, how it switches to a known contact at login, and what happens at logout.
What it is
The SDK keeps one contact key on the device. It gets the key from Positive User with the first ping and sends it with every event, screen event and product event. Everything the SDK records goes to the contact with that key.
A contact starts as anonymous. It becomes identified when your app sends a user ID or an email for it.
How it works
First launch
- Your app initializes the SDK and sends a ping.
- Positive User creates an anonymous contact with the device and returns its key.
- The SDK stores the key on the device. Later launches reuse it.
Events need the key, so they can only be recorded after the first successful ping of an installation.
Login
When your app sends contact data (for example at login), Positive User looks for a matching contact in this order:
- User ID: the contact with this user ID.
- Email: only if Authorize users by email is enabled in your workspace settings.
- Contact key: the contact the device already uses.
- No match: a new contact.
If a contact with the user ID already exists, the SDK switches to it and stores its key. The anonymous contact is not merged into it: its earlier activity stays on the anonymous contact. If the user ID is new, it is added to the contact the device already uses, so the anonymous activity stays with the identified contact.
A contact that already has a different user ID is never reused for another user ID. Positive User creates a new contact instead.
Contact data
- Standard fields: user ID, email, first name, last name, phone.
- Custom attributes: only attributes that already exist in the panel are saved. Other names are ignored without an error. A value of the wrong type for an existing attribute rejects the whole request, even though part of the data may already be saved.
- Empty and
nullvalues are skipped. They do not clear a value on the contact. - On iOS from 1.3.0 the SDK keeps no copy of contact data. What you send with
updateContactis added to the contact once, and later pings do not repeat it.
Logout
Logout ends the session of the current contact and starts a new anonymous one. The previous contact keeps its data and activity.
On iOS from 1.3.0, logout takes these steps in order:
- It records a
logoutevent on the contact who is leaving. - It deletes the contact key and contact data on the device, and a waiting in-app message.
- It sends a ping that creates a new anonymous contact. The ping carries the same push token.
- It removes the token you pass to
logoutfrom the contact who is leaving, if that contact still has it.
The push token belongs to the device, not to the person, so it moves to the anonymous contact and the device keeps receiving push notifications. A push notification sent to the logged out contact does not reach the device. If your app should not receive push notifications after logout, that is up to your app.
Events collected offline before logout are still sent, for the contact who logged out. Contact updates of that contact that are still waiting are dropped and end with the error loggedOut.
Reinstall and change of workspace
On iOS the SDK detects a reinstall of the app and starts a new anonymous contact. Changing the workspace domain in an existing installation does not reset the key. Delete the app before testing with another workspace.
Identify contacts only through the SDK on the device. If your servers also create or update the same users through the REST API with different identifiers, the SDK's contact and the one you look at in the panel can be two different contacts. Events then appear "missing" while they are recorded on the other contact.
Platform differences
| iOS | Android | Flutter | |
|---|---|---|---|
| First ping | Your app calls ping() | Sent by UserCom.Builder(...).build() | Sent by initialize() |
| Event before the first ping | Not sent. The completion reports notIdentified. | Sent without a contact key and rejected, then saved and sent again with the key on the next UserCom.getInstance() call | Sent without a contact key and rejected |
| Contact key in your code | UserSDK.default?.userKey | RegisterResponse.getKey() in the register callback | No public API |
| Identify | updateContact(_:fcmToken:_:) with a Contact | register(customer, callback) | registerUser(customer:) |
| Logout | logout(fcmToken:_:): logout event, new anonymous contact with the same token, then removes the token passed in from the previous contact. Before 1.3.0 the new anonymous contact had no token | logout(): logout event, removes the token from the previous contact, new anonymous contact with the same token | logoutUser(): logout event, new anonymous contact with the same token |