Countly integration
Integrate Countly product analytics into any app (web, iOS, Android, Flutter, React Native, or server-side via HTTP) with a data collection model designed so the Countly dashboard can actually answer product questions. Use this skill whenever the user wants to add Countly, add analytics/tracking to an app, instrument events, track sessions/views/users/crashes, design an event taxonomy or tracking plan, wire up login/logout identity for analytics, or asks why their Countly dashboard can't answer a question (funnels, retention, cohorts, drill). Trigger even if they only say "add analytics" and Countly is the platform in use, or mention an app_key / count.ly / countly server URL.From its SKILL.md
npx -y skills add Countly/countly-sdk-skill --skill countly-integrationAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
14.7 KB, ~3.3k tokens by cl100k_base, as published. Nobody here has run it
Countly Integration with a Proper Data Collection Model
Countly dashboards (Drill, Funnels, Retention, Cohorts, Flows, User Profiles) are only as good as the data model behind them. Analytics data is retroactive-proof but not retro-fillable: funnels and cohorts can be defined later at no cost, but data you didn't collect is gone forever — and data collected with wrong granularity (high-cardinality event keys, IDs in segmentation values) actively poisons the dashboard. So the integration order is always: design the data model first, write SDK calls second.
Workflow
- Collect the questions. Ask the user (or infer from the product) which questions the dashboard must answer. Write them down explicitly — e.g. "Where do users drop off during onboarding?", "Do paying users retain better?", "Which plan tier crashes most?"
- Map each question to a Countly feature and its required data (table below). This yields the tracking plan: which events, which segmentation keys, which user properties, whether sessions/views/crashes are needed.
- Choose the identity strategy (section below) — this is the hardest thing to change later.
- Write the tracking plan down (a
TRACKING.mdor similar in the user's repo): event keys, segmentation keys with example values, user properties, who sets them and when. Get user sign-off on this before writing code if the plan is non-obvious. - Implement: init → consent (if required) → identity → sessions → events/views/user properties → crashes. Per-SDK method names are in references/sdk-methods.md; raw HTTP API for server-side integrations is in references/http-api.md.
- Verify against the checklist at the end.
What powers what — question → data mapping
| Dashboard question | Feature | Data it needs |
|---|---|---|
| How many/active users, new vs returning, session length, top countries/devices/versions | Analytics overview | Sessions (begin/update/end) + metrics sent with begin_session |
| Where do users drop off in flow X? | Funnels | Events — one event per step (or one event + step segmentation), consistent per-user identity |
| Do users come back? (Day-N retention) | Retention | Sessions (session retention) or a specific event (event retention) |
| Which users did X but not Y? Target/segment them | Cohorts | Events + custom user properties |
| Ad-hoc: "signups from Germany on Android with plan=pro" | Drill | Events with rich segmentation + user property snapshot (captured automatically at event time) |
| What paths do users take? | Flows | Events or views in sequence |
| Which screens/pages are used, time-on-screen, bounces | Views analytics | Views with normalized names |
| Is the app stable? Which version/device crashes? | Crashes | Automatic crash handler + handled exceptions + breadcrumbs |
| Who is this user? (support lookups, tier breakdowns) | User Profiles | user_details predefined + custom properties |
| How do users rate us / NPS? | Feedback | Feedback widgets (server-configured, one SDK call) |
If a question maps to no collected data, the dashboard cannot answer it — add the event/property now, not when someone asks.
Identity strategy (decide first)
Countly identifies users by device_id; internally the user is
sha1(app_key + device_id), so identity is per-app and merging is a real
server-side operation. Four documented strategies — pick one deliberately:
- Device-based (default) — SDK-generated ID, no login concept. Fine for anonymous-only products.
- Known users only — init Countly only after login, passing the stable user ID as device ID. No anonymous data at all.
- No anonymous data, but init early — start SDK in temporary/offline ID
mode; on login call
setID(userId)(queued data gets re-stamped and flushed); on logout re-enter temporary mode. - Anonymous → merge on login (most common for consumer apps) — start
with SDK-generated ID; on first login call
setID(userId). Because the previous ID was SDK-generated, the server merges the anonymous history into the known user. On logout, do nothing — leave the device ID as is. On a later login, compare: if the same user, do nothing; if a different account, callsetID(newUserId)— the current ID is developer-supplied, so it switches without merging, which is exactly right for an account change.
Rules that prevent painful surprises:
setIDmerges only when the current ID is SDK-generated. If the current ID is developer-supplied, it switches identity without merge — a new user is created, the session ends, timed events are dropped, and consent is reset.- Merging is a costly server-side operation — design for at most one merge per user (the first login). Never reset to a fresh anonymous ID on logout: every logout→login cycle would mint a new anonymous user and force another merge.
- Use the same user ID across platforms if cross-device identity matters.
- Prefer an opaque customer/user ID over email as device_id — the identifier
rides in every request and log, so email there is PII exposure. If the
organization's privacy policy permits it, email is acceptable (it makes
finding users in the dashboard easier); either way normalize
developer-supplied IDs before every
setID(lowercase, trim) —[email protected]and[email protected]are two different users.
Sessions
Sessions anchor retention, loyalty, session frequency/duration, and carry the device metrics (OS, version, device, resolution, carrier, locale, app version). Always enable session tracking unless the integration is a stateless backend service.
- Mobile SDKs track sessions automatically (foreground/background). Web
requires opting in:
Countly.track_sessions(). - The wire model is
begin_session=1(+ metrics) →session_duration=Nheartbeat every ~60 s →end_session=1. The server merges sessions that restart within the cooldown window (15 s default). - Metrics are only accepted with
begin_session— a mid-session app-version change shows up on the next session. - Server caps a single reported duration (
session_duration_limit, default 86400 s). - Use manual session control only for non-standard lifecycles (kiosk apps, media players, server-side); otherwise automatic is correct.
Events — the heart of the data model
An event is {key, count, sum, dur, segmentation}. Design rules, each of
which exists because of a hard server limit or a dashboard behavior:
Few keys, rich segmentation. The server allows 500 unique event keys per
app, but a good model uses 20–80. One Purchase event segmented by
{category, paymentMethod, plan} beats PurchaseBook/PurchaseSub/… —
funnels, drill and flows all work by filtering segmentation, and every extra
key clutters every event dropdown in the dashboard.
Segmentation values must be low-cardinality. Max 1,000 distinct values
per segmentation key before aggregated views degrade. Good values: enums,
booleans, bucketed numbers ("1-10", "11-50"), normalized names. Never
put user IDs, emails, timestamps, raw URLs, session IDs, or free text in
segmentation — that data belongs in user properties, or nowhere.
Name for humans. Keys like signup-completed, Subscription Upgraded —
clear, consistent casing, under 30 characters, no [CLY]_ prefix (reserved
for internal events). Pick one convention and state it in the tracking plan.
Use the built-in numeric fields. sum for monetary/numeric totals
(revenue: {key:"Purchase", sum: 9.99}), dur for durations, count for
batched occurrences. Don't smuggle numbers into segmentation strings — sum
and dur get proper aggregation and Drill math.
Timed events (startEvent/endEvent) measure durations with the start
timestamp preserved. They're cleared on identity change without merge — don't
span login boundaries with them.
Milestones and state changes are events too. "Completed onboarding", "Trial expired", "Upgraded plan" — a transaction, an action, or a milestone each deserve an event; UI minutiae (every button hover) do not. Start small and add; deleting a bad event key later doesn't remove its historic cost.
User properties
Sent as user_details. Two kinds:
- Predefined:
name, username, email, organization, phone, picture, gender, byear— use these, they get first-class UI treatment. Setting a property to""deletes it. - Custom (
custom: {...}): default limit is 20 distinct custom properties per app — treat it as a budget. A property earns its slot by being (a) stable or slowly changing and (b) useful for segmenting other data.plan_tier,signup_source,company_size_bucket: yes.last_button_clicked,connection_type: no — those are event segmentation.
Modifier operations (increment, multiply, max, min, setOnce,
push, pushUnique, pull) update server-side without read-modify-write —
use increment for lifetime counters (total_purchases), max for
high-water marks, setOnce for first-touch attribution, pushUnique for
tag lists (arrays keep max 50 elements). On Web, call
Countly.userData.save() after queuing modifiers or nothing is sent.
Every event automatically snapshots the user's properties at event time —
that's what makes "purchases by plan tier" work in Drill without adding
plan to every event. Keep properties current and events stay queryable.
Views
Views feed page/screen analytics and Flows. The single most common
integration mistake: unbounded view names. /order/1234 as a view name
exhausts the server's view limit and silently stops recording new views.
Normalize to route patterns: /order/:id, Checkout, ProductDetail.
- Web:
Countly.track_pageview()auto-tracks; pass a name for SPAs or overrideCountly.getViewNameto normalize URLs. - Mobile: prefer manual
startView("Human Name")over automatic activity/ViewController tracking — auto-collected class names (MainActivityV2Fragment) make dashboards unreadable. - View segmentation exists but reserved keys (
name, visit, start, exit, bounce, dur, segment, view, domain, platform) must not be overridden.
Crashes
Enable automatic crash reporting at init (one config flag on mobile,
Countly.track_errors() on web). Then:
- Record handled exceptions in every significant catch block
(
recordHandledException/log_error) — nonfatal errors are where quality problems show up before they become crash spikes. - Leave breadcrumbs (
addCrashBreadcrumb/add_log) at navigation and state-change points; the last 100 are attached to the next crash. - Add global crash segmentation for dimensions you'll triage by
(e.g.
plan,ab_variant). - Upload symbol files (dSYM / ProGuard mapping / JS source maps) in CI so stack traces are readable.
Consent (GDPR)
If the app needs consent, enable enforcement at init (require_consent /
setRequiresConsent(true)) — the SDK then collects nothing until per-feature
consent is given: sessions, events, views, location, crashes, users, attribution, push, star-rating, remote-config, apm, feedback, ....
The SDK does not persist consent choices — the app must store them and
re-provide on every init. Identity change without merge resets all consent.
Server limits cheat sheet (defaults, configurable server-side)
| Limit | Default |
|---|---|
| Unique event keys per app | 500 |
| Segmentation keys per event | 100 |
| Unique values per segmentation key | 1,000 |
| Elements kept from an array segmentation value | 10 |
| Custom user properties | 20 |
| Elements in a custom-property array | 50 |
| Key length (event/view/segmentation/property) | 128 chars |
| String value length (SDK truncation) | 256 chars |
| Session duration per report | 86,400 s |
| SDK event queue flush threshold | 100 events |
Hitting a limit is silent from the app's perspective — the server drops or aggregates the excess. Design under the limits; don't plan to raise them.
Verification checklist
Before calling the integration done, verify with the SDK's debug logging on
and the Countly dashboard (or /i responses — 2xx JSON with "result"):
- Sessions appear (Analytics → Overview shows the test device; session duration grows with the heartbeat).
- Every event in the tracking plan fires with the exact planned key and segmentation (Events → All Events; check segmentation dropdowns show the expected values, and nothing high-cardinality).
- Login flow: anonymous activity merges into the known user (User Profiles shows one user, not two, after login).
- Logout leaves the device ID unchanged (strategy 4) or re-enters temporary mode (strategy 3) — no fresh anonymous ID; logging into a different account switches identity without merging the two accounts' data.
- View names are bounded (no IDs/query strings in Analytics → Views).
- A forced test crash and a handled exception both appear in Crashes, symbolicated, with breadcrumbs.
- User properties appear on the profile; an event fired after a property change carries the new value in Drill.
- If consent is enforced: nothing is sent before consent; each feature starts flowing when its consent is granted.
References
- references/sdk-methods.md — exact init/method names per SDK (Web/JS, Android, iOS, Flutter, React Native). Read the section for the platform being integrated before writing code.
- references/http-api.md — raw
/iHTTP API (params, wire formats for events/user_details/crash/consent, bulk endpoint). Read for server-side/backend integrations or when debugging what the SDK sends. - Official docs: https://support.countly.com (SDK docs, Server API Reference, "Designing Events", Integration Strategy series). Prefer fetching the platform's SDK page when method signatures matter — SDKs evolve.
What ships with it: 2 files
15.3 KB alongside SKILL.md
references/
- http-api.md7.0 KB
- sdk-methods.md8.3 KB
Gives 0 of the 12 instructions most analytics metrics skills give in ~3.3k tokens
Counted across 333 of the 342 authors here whose files we hold, read 2026-09-06
- Read product marketing context before asking questionsin 37 of 333, across 16 files
- Test one variable at a timein 26 of 333, across 11 files
- Pre-determine sample size before launchin 24 of 333, across 16 files
- Verify tracking and QA variants before launchin 17 of 333, across 8 files
- Monitor for technical issues during the testin 14 of 333, across 6 files
- Match each save offer to the cancel reasonin 14 of 333, across 5 files
- Start every test with a specific hypothesisin 14 of 333, across 7 files
- Keep the continue-cancelling option visiblein 13 of 333, across 4 files
- Document every test with hypothesis, variants, results, and learningsin 13 of 333, across 6 files
- Gather churn, billing, product, usage, and constraint context firstin 12 of 333, across 3 files
- Build a health score from weighted signalsin 12 of 333, across 3 files
- Retry soft declines 3-5 times over 7-10 daysin 12 of 333, across 3 files
Said here and by no other author read
- Design the data model before writing SDK calls
- Collect the questions the dashboard must answer
- Decide the identity strategy first
- Write a tracking plan and get user sign-off
- Implement in init, consent, identity, sessions, events, crashes order
- Enable session tracking unless the service is stateless
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.