Data model

Identity and aliases

How Sankofa resolves anonymous traffic to known users — the distinct ID, the alias transport, and the server-side stitching that rewrites history.

Every event in Sankofa belongs to a distinct ID. Until a user signs in or otherwise tells you who they are, that ID is an opaque anonymous string the SDK generated on first launch. After they identify, you want everything they did before that moment to retroactively belong to the new known ID — so funnels, cohorts, and retention all stitch correctly.

That stitching is what identify and alias exist to make happen.

Three IDs, three jobs

IDWhere it comes fromLifetime
anon_idGenerated by the SDK at first launch. UUIDv4.Until reset() or storage cleared.
distinct_idEither the anon_id (until identified) or the user's stable ID after identify(...).The current session's identity.
alias_idA previous identifier you want to attach to the current distinct_id.Stored permanently in the alias table.

The SDK always sends distinct_id on every event. It also sends anon_id separately so the server can stitch correctly across the identity transition.

The lifecycle of a real user

  1. First launch — anonymous

    SDK generates anon_a3b9ff…. Every event uses distinct_id = anon_a3b9ff…. The user browses, adds items to cart, fires cart_updated events.

  2. The user signs up

    Your app calls Sankofa.identify("user_123"). The SDK:

    1. Posts an alias from anon_a3b9ff…user_123 to /api/v1/alias.
    2. Switches the active distinct_id to user_123 for all subsequent events.
    3. Forwards anon_id on the next decision handshake so cohort-targeted flags / configs are continuous across the boundary.
  3. Server-side stitching

    The engine writes a row to the alias table. Within a few minutes, the analytics pipeline rewrites the historical anonymous events to attribute them to user_123. After this, every chart and cohort attributes the pre-signup activity to the same user.

  4. The user signs in on another device

    SDK on device 2 generates a fresh anon_4ef2cc…, then identify("user_123") aliases it onto the same canonical ID. Both devices now produce events under user_123.

  5. The user signs out

    Your app calls Sankofa.reset(). The SDK rotates to a new anon_id, clears the active distinct_id, and starts a new session. Future events on this device are anonymous again until identify runs.

The alias transport

identify calls alias for you. If you need to wire identity across systems where you can't call identify (e.g. a backfill from your data warehouse), use the alias transport directly.

bash
curl -X POST https://api.sankofa.dev/api/v1/alias \
-H "Content-Type: application/json" \
-H "x-api-key: sk_live_..." \
-d '{
  "alias_id": "anon_a3b9ff",
  "distinct_id": "user_123"
}'

The semantics: "anything that ever appeared as anon_a3b9ff is the same person as user_123." Call this once per identity transition; calling it many times is idempotent.

People profiles vs. aliases

alias, identify, and setPerson (a.k.a. peopleSet) all touch identity, but they do different things:

MethodWhat it does
alias(old_id, new_id)Connects two identifiers as the same person. Server-side stitching uses this to rewrite history.
identify(user_id, traits)Calls alias under the hood, then switches the SDK's active distinct_id to user_id, and (if traits provided) also calls setPerson.
setPerson(traits)Updates the user's profile with email, name, plan, etc. Does not affect identity stitching — only the People view.

Use identify for the common case. Use alias when you need to bridge identifiers without changing the SDK's session. Use setPerson to enrich the profile without touching identity.

Multi-device merge

Sankofa merges identities by design — the same distinct_id (user_123) on phone, web, and desktop produces one user in the People view. There's no extra config; just call identify(user_123) on each device after sign-in.

What we don't automatically merge: two anonymous IDs across devices. If a user browses anonymously on web, then anonymously again on mobile without ever signing in, those are two separate people from Sankofa's perspective. (That's by design — we don't fingerprint.)

Pitfalls and edge cases

What's next

Edit this page on GitHub