ActivationPal/docs
← all guidesraw .md

Events

The full event vocabulary: automatic, fixed manual, and custom events, plus the context stamped on every one.

Other guides: quickstart.md · revenuecat.md · agent-api.md · apps.md · apple-ads.md

Automatic — the SDK sends these; never call them yourself

Event When Dashboard use
first_open Once per install, at first configure Installs headline; the install cohort every funnel stage is measured against; pricing-tier splits; install bars
app_open Every new session: launch, or foreground after 30+ min in background "Online now", activity chart, app-opens in the agent API

Session length is measured from the session_id the SDK stamps on every event: the time between a session's consecutive events, ignoring any gap over 5 minutes. That last part matters for apps that log from a notification handler — the SDK only rotates session_id on a foreground transition, so without the cap one id can read as a 19-hour session. first_open and install_attribution fire before the id exists and sit outside every session.

| install_attribution | SDK captures the AdServices token at first open; the server resolves it against Apple's attribution API. Props: resolved + Apple's payload (attribution, campaignId, ...) | Acquisition card: Apple Ads vs organic, per-campaign split, ads-attributed revenue |

Events that happen somewhere else

A Watch app has no SDK of its own — it relays to the phone over WatchConnectivity, and the phone stamps its own hardware model, which files the action under iPhone. Mark the relay point and the Devices card files it under Apple Watch instead:

ActivationPal.trackFromWatch("workout_started")          // shorthand
ActivationPal.track("set_logged", ["reps": 12], from: .watch)

Origin is .phone, .watch, .widget, .vision or .mac; it lands in props.origin and outranks the hardware model. A device that drives both its phone and its watch is counted under both, so that column can sum past the unique-device total.

Fixed manual — helpers on ActivationPal

Names and prop keys are fixed; the funnel and paywall cards match on them exactly. Never reuse them for other meanings.

Event Call Dashboard use
onboarding_step onboardingStep(_ index: Int, id: String, answer: String? = nil) One funnel row per step, ordered by index; answer values split under question steps
onboarding_completed onboardingCompleted() Onboarded headline; funnel "completed" stage
paywall_shown paywallShown(_ placement: String) Placements card (all users) + funnel paywall stage and the Paywall conv. headline (install cohort only)
paywall_plan_selected paywallPlanSelected(_ plan: String) Funnel stage, per-plan split
paywall_purchased paywallPurchased(_ plan: String) Funnel stage, per-plan split; the Conversion and Paywall conv. headlines; median first_open → first purchase
paywall_dismissed paywallDismissed() Event feed and per-user timelines

Every funnel stage counts unique devices from the range's install cohort (first_open inside the range) — no stage exceeds 100% of installed. paywall_shown is also counted un-cohorted in the placements card.

Purchases, trials, renewals, and churn arrive server-side as rc_* events — revenuecat.md.

Custom

ActivationPal.track("workout_logged", ["type": "run", "duration_min": 42, "pro": true])

Context on every event

SDK-stamped: app_version, build, os_version, device_model, locale, app_language (the localization the app actually resolved to), timezone, storefront (App Store country; resolves shortly after launch), installed_at, sdk_version, env (debug / testflight / release), stable per-install device_id, per-session session_id. Server-added from the request IP: country, city. These power the device, geo, and language breakdowns.

Delivery

Buffer in memory, persist to disk (survives force-quit), flush every 10 s / at 20 buffered events / on background; failed flushes retry. Never crashes the app, never blocks the main thread.