Product Flows

Project Users Activity

How Logga derives tracked product users and exposes their activity across the web dashboard and iOS app.

Logga includes a project-scoped Users experience in both the web dashboard and the iOS app. It is intended to answer a simple operator question:

which product users were active in this project, when were they last seen, and what did they do?

As of April 2026, tracked users have a dedicated TrackedUser table backing them — populated automatically from event ingestion and editable from the dashboard.

Scope

Current implementation boundaries:

  • the surface is per project, not workspace-wide
  • the web page lives at /app/[workspaceId]/projects/[projectId]/users
  • the iOS app exposes the same project-scoped activity from ProjectDetailView
  • it respects the currently selected environment
  • it uses stable event identity only
  • the web app opens detail in a side sheet
  • the iOS app uses a native push detail flow

Canonical Identity Contract

Tracked users are keyed from the event identity contract:

  • actor.id is the preferred canonical product-user identifier
  • userId is the fallback when a full actor snapshot is not available
  • when both are present, actor.id wins

Display metadata for an identity is stored on the matching TrackedUser row and updated every time an event arrives with those fields in its actor payload. Fields not carried on an event are left untouched.

Reserved identity columns on TrackedUser:

  • name
  • email
  • avatarUrl
  • role
  • label

In addition, TrackedUser.properties is a free-form JSONB column for custom key-value fields (strings, numbers, booleans, null; ≤50 keys). Use actor.properties on outgoing events, or edit them from the dashboard. Events patch properties key by key (null removes a key); saving from the dashboard replaces the whole object. Properties can narrow analytics to a kind of user, see Funnels and retention.

These fields are presentation data, not identity keys — the canonical key is still externalId (what the SDK sends as actor.id or userId).

What Appears In The Users List

Each row in the list represents one stable product user inside one project.

The current list shows:

  • user identity summary
  • last access
  • event count in the selected range
  • distinct non-null session count in the selected range
  • distinct channel count in the selected range
  • last event type in the selected range

The default sort is last access desc.

The supported list controls are:

  • free-text search over userId, latest name, email, label, and role
  • activity range: 24h, 7d, 30d, all

Detail Sheet Semantics

Selecting a row opens a side sheet with:

  • all-time first seen
  • all-time last seen
  • range-scoped total events
  • range-scoped distinct sessions
  • range-scoped distinct channels
  • custom properties card (renders TrackedUser.properties as key-value tiles)
  • top channels in range
  • top event types in range
  • recent event timeline in range

The selected user id is mirrored into the URL query string as ?userId=... so refresh and back-forward navigation preserve state.

Editing a tracked user

An Edit button in the sheet header (web) or the detail header (iOS) opens an editor that can change:

  • the reserved identity fields: name, email, avatarUrl, role, label
  • custom properties (add, edit, remove per key)

Editor semantics:

  • values can be string, number, boolean, or null
  • saving does a full replace of the properties object — send the final state you want
  • empty-string inputs for identity fields clear them to null
  • the edit persists to the TrackedUser row immediately, but subsequent events carrying those fields in actor will overwrite the edit. Decide once whether the SDK or the dashboard is authoritative for each user.

The editor calls PATCH /v1/dashboard/projects/:projectId/users.

Unattributed Activity

Events without actor.id or userId still ingest normally, but they are not merged into tracked users.

Instead, the users page surfaces them as:

  • Unattributed events in the summary cards

This avoids heuristic user merging and keeps the identity model explicit.

Integration Guidance

If you want a user to appear in the Users page, emit one of these:

Preferred

json
{
  "channel": "auth",
  "event": "signed_in",
  "actor": {
    "id": "user_123",
    "name": "Francesco",
    "email": "[email protected]",
    "role": "admin"
  }
}

Fallback

json
{
  "channel": "auth",
  "event": "signed_in",
  "userId": "user_123"
}

Current Limitations

This surface does not attempt to provide:

  • retention or cohort analysis
  • workspace-wide cross-project user views
  • inferred user records for anonymous traffic
  • pageview or funnel analytics beyond event-derived behavior
  • a "lock" that prevents SDK events from overwriting a dashboard edit (the editor is an override, not a pin)