Identity
Cluestick works before you know who someone is, and keeps their history when you find out. Identity is resolved by the server, never asserted by the app.
Anonymous by default
You do not have to do anything to support anonymous users. On the first call the server mints an anonymous user and issues a session token, which the SDK stores in the keychain. Someone can open support and write in before they have an account, and their conversation survives app restarts.
If your app never signs anyone in, this is the whole story — you can stop reading here.
Why the app cannot just send a user ID
A publishable ck_key ships inside your binary, so anyone can extract it. If sending a key and a user ID were enough to read that user’s conversation, then anyone with your app could read anyone else’s support history by guessing IDs.
So the SDK never sends a user ID. Instead your backend — which already knows who is logged in — signs a short-lived token, and the server issues a session in exchange for it. The app only ever holds a session it was given.
Identifying a user
Supply a token provider at configure time
Pass a closure that fetches a fresh identity token from your backend. The SDK calls it when it needs one.
Cluestick.configure(apiKey: "ck_live_8f2a…") {
// Called by the SDK. Return a freshly signed token from your API.
try await YourAPI.fetchCluestickIdentityToken()
}Call identify() after sign-in
func signInDidSucceed() async {
do {
try await Cluestick.identify()
} catch {
// Support still works anonymously; surface or log as you prefer.
}
}Any conversation the person started while anonymous is merged into their identified account, so a question they asked before signing up does not disappear the moment they do.
Signing the token on your backend
Find your project’s identity secret in the dashboard, under the project’s settings. Treat it like any other server secret: it never belongs in your app binary, in client-side code, or in version control. You can rotate it at any time, which invalidates tokens signed with the previous value.
Sign a compact JWT with HS256 and exactly two claims:
{
"sub": "your-internal-user-id",
"exp": 1785312000
}sub— your own stable identifier for the user. This is what the conversation is filed under, so it must not change between sessions.exp— expiry, as seconds since the epoch. Keep it short; the SDK asks for a new token whenever it needs one.
Return the encoded token as a string from your endpoint. Any standard JWT library will do — there is nothing Cluestick-specific about the signing itself.
Attaching traits and service IDs
setProfile tells the dashboard how to enrich this person. Service IDs let Cluestick line them up with your other tools; traits are your own key-value facts.
Cluestick.setProfile(
serviceIds: [
"revenuecat": rcAppUserId,
"posthog": posthogDistinctId,
],
traits: [
"plan": "annual",
"workspaces": 3,
"beta": true,
]
)Both parameters are optional, and traits accept strings, integers, doubles and booleans as literals. If you do not set service IDs, matching a person to their RevenueCat or PostHog record is up to you.
Logging out
Call reset()when someone signs out. It clears the stored session and establishes a fresh anonymous one, so the next person to use the device starts clean and cannot read the previous user’s conversation.
Cluestick.reset()