Web and mobile instrumentation playbook

Instrument outcomes before screens. A small shared event spine lets dashboards, CLI queries, and coding agents answer activation and release-impact questions without requiring every app to use the same UI.

The minimum product spine

Use stable event names for the lifecycle stages your product actually has:

StageExample event
Acquisitionlanding_page_view or page_view
Intentaggregate:cta_clicked or signup_initiated
Account activationauth_signup_succeeded
Workspace activationproject_created
Technical activationsdk_initialized
First valuefirst_event_received and first_query_succeeded
Engagementa product-specific outcome event
Monetizationcheckout_started, purchase:success
Reliabilitynormalized crash or failure outcome
Agent valueagent:brief_generated, agent:recommendation_accepted

Do not rename existing production events just to match this list. Add canonical events alongside legacy names, then migrate queries through an explicit alias plan.

Shared context

Keep dimensions bounded and useful across surfaces:

Never send emails, phone numbers, names, tokens, secrets, complete URLs containing personal query parameters, receipts, or payment credentials as event properties.

Web setup

import { init } from '@analyticscli/sdk';

const analytics = init({
  apiKey: import.meta.env.PUBLIC_ANALYTICSCLI_PUBLISHABLE_API_KEY,
  platform: 'web',
  projectSurface: 'dashboard',
  identityTrackingMode: 'consent_gated',
  initialConsentGranted: false,
  initialFullTrackingConsentGranted: false,
});

Open event collection and persistent identity only when the host application’s consent decision allows it:

analytics.setConsent(true);
analytics.setFullTrackingConsent(true);

For a consentless marketing counter, use aggregate mode in the website integration. Aggregate events receive a new unlinkable ID per event. They support page and CTA counts, but not unique people, user funnels, or retention.

Expo and React Native setup

import * as Application from 'expo-application';
import { Platform } from 'react-native';
import { init } from '@analyticscli/sdk';

const analytics = init({
  apiKey: process.env.EXPO_PUBLIC_ANALYTICSCLI_PUBLISHABLE_API_KEY,
  debug: typeof __DEV__ === 'boolean' ? __DEV__ : false,
  platform: Platform.OS,
  projectSurface: 'app',
  appVersion: Application.nativeApplicationVersion ?? undefined,
  identityTrackingMode: 'consent_gated',
  dedupeOnboardingStepViewsPerSession: true,
  dedupeScreenViewsPerSession: true,
});

Prefer the scoped onboarding and paywall trackers so flow IDs and step indexes stay consistent. Track terminal outcomes such as complete, skip, purchase success, purchase cancel, and purchase failure—not only impressions.

Verify before expanding

Use Debug to prove transport and schema:

analyticscli --include-debug schema events --last 24h

Then verify real Release traffic:

analyticscli agent brief --last 14d
analyticscli generic --metric event_count --group-by eventName --last 14d

Only after the spine is visible should you add deeper screen or feature events. Every new event should answer a named product question, have an owner, and define how long its data remains useful.

Release-impact loop

For each shipped change:

  1. Record the release version and deployment time.
  2. Define the primary event, guardrail metric, and expected direction.
  3. Compare the same metric and segment before and after release.
  4. Check crash and revenue context before assigning causality.
  5. Keep the change reversible until the signal is stable.

This same loop works for a landing-page CTA, dashboard onboarding, an Expo paywall, or an AI-agent workflow.