npx agentspec-cli install 5c51f495-41c5-4c6f-ac97-85a366291309 --file-type cursor-rules→ .cursor/rules/
# Wide Events
Wide events measure a user's complete journey through a multi-step flow, sending a single event when the journey concludes. Use them when a flow has multiple steps that can succeed or fail independently and you need to understand drop-off and errors across the whole journey.
**Use a wide event when:** a user journey has multiple steps, the outcome of earlier steps affects the interpretation of later ones, or you need to understand where users drop off.
**Use a pixel when:** recording a single discrete action with no multi-step flow.
For design guidance on naming, status, data selection, latency bucketing, and best practices, refer to the platform-agnostic doc at `windows-browser/.cursor/rules/wide-events.mdc`.
---
## Android API
The entry point is `WideEventClient` from `statistics-api`. Inject it directly or, for complex flows, wrap it in a feature-specific interface (see below).
```kotlin
// Start a flow — returns a flow ID used for all subsequent calls
val flowId: Long = wideEventClient.flowStart(
name = "my-feature-action", // kebab-case, e.g. "subscription-purchase"
flowEntryPoint = origin, // optional: where the flow was triggered from
metadata = mapOf("key" to "value"), // initial data
cleanupPolicy = CleanupPolicy.OnProcessStart(ignoreIfIntervalTimeoutPresent = true),
).getOrNull() ?: return
// Record a step
wideEventClient.flowStep(
wideEventId = flowId,
stepName = "create_account", // snake_case
success = true,
metadata = mapOf("platform" to "google"),
)
// Measure latency between two points (always use bucketed values, not raw ms)
wideEventClient.intervalStart(wideEventId = flowId, key = "creation_latency_ms_bucketed")
// ... operation ...
wideEventClient.intervalEnd(wideEventId = flowId, key = "creation_latency_ms_bucketed")
// Finish the flow
wideEventClient.flowFinish(
wideEventId = flowId,
status = FlowStatus.Success,
metadata = mapOf("last_step" to "activate"),
)
// Or abort silently (no event sent)
wideEventClient.flowAbort(wideEventId = flowId)
```
### FlowStatus
```kotlin
FlowStatus.Success
FlowStatus.Failure(reason = "payment_declined")
FlowStatus.Cancelled
FlowStatus.Unknown // unexpected termination; always pair with a last_step in metadata
```
### CleanupPolicy
Defines what happens to flows abandoned due to app termination or timeout:
```kotlin
// Complete the flow with Unknown status on next process start
CleanupPolicy.OnProcessStart(
ignoreIfIntervalTimeoutPresent = true, // keep alive if an interval timeout is set
flowStatus = FlowStatus.Unknown,
)
// Complete the flow with Unknown status after a duration
CleanupPolicy.OnTimeout(
duration = Duration.ofDays(7),
flowStatus = FlowStatus.Unknown,
)
```
---
## Implementation Pattern
For non-trivial flows, wrap `WideEventClient` in a feature-specific interface. This keeps the calling code clean and makes the flow lifecycle explicit.
```kotlin
// 1. Define a feature-specific interface (in the feature's -impl module)
interface MyFeatureWideEvent {
suspend fun onFlowStarted(origin: String?)
suspend fun onStepCompleted()
suspend fun onFlowSucceeded()
suspend fun onFlowFailed(reason: String)
}
// 2. Implement it, injecting WideEventClient
@SingleInstanceIn(AppScope::class)
@ContributesBinding(AppScope::class)
class MyFeatureWideEventImpl @Inject constructor(
private val wideEventClient: WideEventClient,
) : MyFeatureWideEvent {
private var flowId: Long? = null
override suspend fun onFlowStarted(origin: String?) {
flowId = wideEventClient.flowStart(
name = "my-feature-action",
flowEntryPoint = origin,
cleanupPolicy = CleanupPolicy.OnProcessStart(ignoreIfIntervalTimeoutPresent = false),
).getOrNull()
}
override suspend fun onFlowSucceeded() {
val id = flowId ?: return
wideEventClient.flowFinish(wideEventId = id, status = FlowStatus.Success)
flowId = null
}
override suspend fun onFlowFailed(reason: String) {
val id = flowId ?: return
wideEventClient.flowFinish(
wideEventId = id,
status = FlowStatus.Failure(reason),
metadata = mapOf("last_step" to currentStep),
)
flowId = null
}
}
```
Reference implementations:
- `subscriptions/subscriptions-impl/.../SubscriptionPurchaseWideEvent.kt` — multi-step purchase flow with intervals
- `app/src/main/java/com/duckduckgo/app/browser/pageload/PageLoadWideEvent.kt` — per-tab tracking with `ConcurrentHashMap`
---
## How Events Are Sent
Wide events are persisted to a Room database and sent asynchronously by `CompletedWideEventsProcessor` (a `MainProcessLifecycleObserver`). They are sent via two transports controlled by the `wideEvents` remote feature flag:
- As pixels: `wide_{name}_c` (count) and `wide_{name}_d` (daily)
- Via a dedicated POST endpoint (internal builds only by default)
You don't need to think about transport — just call `flowFinish` and the infrastructure handles the rest.
---
## Feature Flag
Wide events are gated by the `wideEvents` remote feature (`WideEventFeature` in `statistics-impl`). Individual features should also guard their wide event calls behind their own feature flag to avoid sending events when a feature is disabled.
analytics
When the user wants to set up, improve, or audit analytics tracking and measurement. Also use when the user mentions "set up tracking," "GA4," "Google Analytics," "conversion tracking," "event tracking," "UTM parameters," "tag manager," "GTM," "analytics implementation," "tracking plan," "how do I measure this," "track conversions," "Mixpanel," "Segment," "are my events firing," or "analytics isn't working." Use this whenever someone asks how to know if something is working or wants to measure marketing res
by AgentHub Crawler
upstash-workflow-js
Lightweight guidance for using the Upstash Workflow SDK to define, trigger, and manage workflows. Use this Skill whenever a user wants to create workflow endpoints, run steps, or interact with the Upstash Workflow client.
by AgentHub Crawler
analytics-tracking
When the user wants to set up, improve, or audit analytics tracking and measurement. Also use when the user mentions "set up tracking," "GA4," "Google Analytics," "conversion tracking," "event tracking," "UTM parameters," "tag manager," "GTM," "analytics implementation," "tracking plan," "how do I measure this," "track conversions," "attribution," "Mixpanel," "Segment," "are my events firing," or "analytics isn't working." Use this whenever someone asks how to know if something is working or wants to measur
Sign in to leave a comment.
by AgentHub Crawler