# Keren Analytics — Telemetry Contract > Plug-and-play analytics over Azure Application Insights telemetry. This contract tells coding agents which signals to emit, how to name custom dimensions, and which config values make telemetry render well. Contract version: `50d2888a2f2f` · Machine-readable: https://keren.run/.well-known/telemetry-contract.json Instrument your app's Azure Application Insights so these signals are present, name custom dimensions as below, and Keren renders the full dashboard with no manual mapping. ## Privacy - Keren stores only aggregates — no raw log rows, no PII. Telemetry you emit is queried in place via KQL; pseudonymize user ids and never send PII. ## Signals (readiness points) Score out of 120. Aim for grade A (>=90%). ### Page Views — 20 pts (required) - Lands in: `pageViews` table - How: Application Insights JS SDK: enable enableAutoRouteTracking for SPAs, or call trackPageView() on each route/page load. - Verify (KQL): `pageViews | summarize count() by bin(timestamp, 1h)` ### Backend Requests — 15 pts (required) - Lands in: `requests` table - How: Server SDK request auto-collection (applicationinsights for Node, Microsoft.ApplicationInsights for .NET, the Java agent, opencensus-ext-azure for Python). - Verify (KQL): `requests | summarize count() by bin(timestamp, 1h)` ### Session Tracking — 15 pts (required) - Lands in: `session_Id` - How: Auto-generated by the JS SDK — do not set disableSessionTracking. Server-only telemetry has no sessions; add a frontend SDK. - Verify (KQL): `pageViews | where isnotempty(session_Id) | count` ### User Identity — 15 pts (recommended) - Lands in: `user_AuthenticatedId` - How: Call setAuthenticatedUserContext(hashedId) right after login on both frontend and backend. Use a stable pseudonymous id — never raw email. - Verify (KQL): `pageViews | where isnotempty(user_AuthenticatedId) | summarize dcount(user_AuthenticatedId) by bin(timestamp, 1d)` ### Frontend Perf — 15 pts (optional) - Lands in: `browserTimings` table - How: JS SDK with enableAutoRouteTracking + autoTrackPageVisitTime; keep sampling moderate so timings aren't filtered out. - Verify (KQL): `browserTimings | summarize count() by bin(timestamp, 1h)` ### Device & Browser — 10 pts (recommended) - Lands in: `client_Browser`, `client_OS`, `client_Type` - How: Auto-collected by the JS SDK from the User-Agent header. - Verify (KQL): `pageViews | where isnotempty(client_Browser) | count` ### Geo Location — 10 pts (optional) - Lands in: `client_CountryOrRegion` - How: Server-side IP geo-enrichment by Azure. Behind a proxy/CDN, forward X-Forwarded-For so the client IP (not the proxy's) is resolved. - Verify (KQL): `pageViews | where isnotempty(client_CountryOrRegion) | summarize count() by client_CountryOrRegion` ### Dependencies — 10 pts (recommended) - Lands in: `dependencies` table - How: Enable dependency auto-collection in the server SDK config. - Verify (KQL): `dependencies | summarize count() by type` ### Exceptions — 10 pts (recommended) - Lands in: `exceptions` table - How: Server SDK exception auto-collection plus trackException() in a global error handler. - Verify (KQL): `exceptions | summarize count() by bin(timestamp, 1h)` ## Custom dimension naming Built-in fields are detected automatically. If you must use a custom dimension, use a recognized name so it auto-maps: - **userId** → recommended `userId` (also: userId, user_id, uid, userid, userHash, user_hash…) - **sessionId** → recommended `sessionId` (also: sessionId, session_id, sid, sessionKey, session_key, visitId…) - **pagePath** → recommended `page` (also: page, pagePath, page_path, pageName, page_name, pageRoute…) - **referrer** → recommended `refUri` (also: refUri, referrer, ref_uri, referrerUrl, referrer_url, referer…) - **browser** → recommended `browser` (also: browser, client_browser, userBrowser, user_browser, ua_browser, browserName…) - **os** → recommended `os` (also: os, client_os, operatingSystem, operating_system, platform, ua_os…) - **device** → recommended `device` (also: device, deviceType, device_type, client_type, formFactor, form_factor…) ## Config best practices - **Connection string from env, never hardcoded** — Read the App Insights connection string from APPLICATIONINSIGHTS_CONNECTION_STRING. Never commit it. The browser SDK uses a write-only ingestion key — safe to expose to the page. - **Initialize the SDK first** — Set up the server SDK before the web framework loads (first import) so the HTTP stack is patched and requests/dependencies are auto-collected. - **No PII in telemetry** — Never emit email, full name, raw IP, or tokens. Pseudonymize user ids (hash). Keren scrubs known PII patterns from samples, but the contract is: don't send it. - **Exclude health-check endpoints** — Filter probe routes (e.g. /healthz) out of request telemetry so they don't drown real traffic in the Technical view. - **Flush on shutdown** — Flush pending telemetry on SIGTERM so the last batch of events isn't lost when the container stops. - **Set cloud_RoleName per service** — Tag each service with a distinct cloud_RoleName so multi-service topologies stay distinguishable in the dashboard. - **Sample, but not away your conversions** — Use adaptive sampling to control cost, but keep it moderate enough that low-volume conversion events and browser timings survive. ## SDK per stack - React (SPA): `@microsoft/applicationinsights-react-js` - Angular (SPA): `@microsoft/applicationinsights-web` - Vue.js (SPA): `@microsoft/applicationinsights-web` - Node.js: `applicationinsights` - .NET / ASP.NET: `Microsoft.ApplicationInsights` - Python: `opencensus-ext-azure` - Java / Spring: `applicationinsights-agent` - Web Application: `@microsoft/applicationinsights-web` ## Ready-to-paste prompts One prompt per signal — paste into Cursor / Copilot / Claude Code to close a gap. Full text in the JSON contract under `recipes`. - Page View Tracking (`pageViews`) - Backend Request Tracking (`requests`) - User Identity (`userId`) - User Identity (degraded) (`userIdDegraded`) - Session Tracking (`sessionId`) - Device & Browser Info (`userAgent`) - Geo Enrichment (`geo`) - Frontend Performance (`browserTimings`)