agentsclimarketplace

Countly integration

Skill Countly/countly-sdk-skill/skills/countly-integration

Agent Skill for AI coding agents: integrate Countly analytics with a proper data collection model (Claude Code, Cursor, Codex, Copilot, any LLM)

Install
npx -y skills add Countly/countly-sdk-skill --skill countly-integration

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • 27 days oldThe repository was created 27 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
  • 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.

What its author says it does

Copied from the file, not written here

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.

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

  1. 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?"
  2. 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.
  3. Choose the identity strategy (section below) — this is the hardest thing to change later.
  4. Write the tracking plan down (a TRACKING.md or 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.
  5. 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.
  6. Verify against the checklist at the end.

What powers what — question → data mapping

Dashboard questionFeatureData it needs
How many/active users, new vs returning, session length, top countries/devices/versionsAnalytics overviewSessions (begin/update/end) + metrics sent with begin_session
Where do users drop off in flow X?FunnelsEvents — one event per step (or one event + step segmentation), consistent per-user identity
Do users come back? (Day-N retention)RetentionSessions (session retention) or a specific event (event retention)
Which users did X but not Y? Target/segment themCohortsEvents + custom user properties
Ad-hoc: "signups from Germany on Android with plan=pro"DrillEvents with rich segmentation + user property snapshot (captured automatically at event time)
What paths do users take?FlowsEvents or views in sequence
Which screens/pages are used, time-on-screen, bouncesViews analyticsViews with normalized names
Is the app stable? Which version/device crashes?CrashesAutomatic crash handler + handled exceptions + breadcrumbs
Who is this user? (support lookups, tier breakdowns)User Profilesuser_details predefined + custom properties
How do users rate us / NPS?FeedbackFeedback 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:

  1. Device-based (default) — SDK-generated ID, no login concept. Fine for anonymous-only products.
  2. Known users only — init Countly only after login, passing the stable user ID as device ID. No anonymous data at all.
  3. 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.
  4. 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, call setID(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:

  • setID merges 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=N heartbeat 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 override Countly.getViewName to 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)

LimitDefault
Unique event keys per app500
Segmentation keys per event100
Unique values per segmentation key1,000
Elements kept from an array segmentation value10
Custom user properties20
Elements in a custom-property array50
Key length (event/view/segmentation/property)128 chars
String value length (SDK truncation)256 chars
Session duration per report86,400 s
SDK event queue flush threshold100 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 /i HTTP 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.

Gives 0 of the 12 instructions most analytics metrics skills give in ~3.3k tokens

Counted across 368 of the 369 authors here whose files we hold, read 2026-08-06

  • read product marketing context before asking questionsin 18 of 368, across 12 files
  • use lowercase with underscores for event namesin 16 of 368, across 6 files
  • track events for decisions not vanity metricsin 15 of 368, across 5 files
  • use object-action format for event namesin 15 of 368, across 8 files
  • produce a tracking plan documentin 14 of 368, across 4 files
  • Call RUBE_SEARCH_TOOLS first to get current schemasin 13 of 368, across 2 files
  • establish consistent event naming conventions before implementingin 10 of 368, across 4 files
  • Verify dimension and metric compatibility before reportingin 9 of 368, across 2 files
  • Encrypt data at rest and in transitin 9 of 368, across 3 files
  • use snake_case for event namesin 9 of 368, across 5 files
  • monitor technical health during the testin 9 of 368, across 5 files
  • use consistent property namesin 8 of 368, across 4 files

Said here and by no other author read

  • design the data model before writing SDK calls
  • collect dashboard questions before mapping events
  • write the tracking plan into the repository
  • choose a deliberate identity strategy
  • normalize developer-supplied IDs before every setID call
  • enable session tracking unless integration is stateless backend

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.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.