Integrating Mitr Analytics β a guide for AI coding assistants
This page is written to be handed directly to an AI assistant (Claude,
ChatGPT, Copilot, Cursor, etc.) β pasted into a chat, or fetched by an
agent from /docs/ai-integration.md
(plain markdown, same content). Following it end-to-end, an assistant
should be able to fully wire up Mitr Analytics in a client's codebase
without back-and-forth beyond the two questions in Step 1.
Step 1 β Get the two things you need from the human
Ask (only if not already provided):
- Site ID β a UUID from the Mitr dashboard (Integration Hub β their app's card, or the "SDK Setup" step of the "Add New App" wizard).
- Platform β a website (plain HTML or a JS framework), or an app (Flutter, React Native, native iOS/macOS in Swift, native Android in Kotlin), or a backend/server (Node.js)? Every platform except plain web/no-code also needs the Secret Key from the same dashboard screen β non-web platforms have no browser Origin header to check, so the secret key authenticates every request instead.
Web snippets load the SDK from https://mitranalytics.dev/vendor/mitr.js,
the canonical URL. Events go to https://api.mitranalytics.dev/api/v1,
the SDK's built-in default β set data-base-url only to point at a
self-hosted or staging backend. You can also self-host mitr.js
from your own static assets; the API is identical either way.
If the human doesn't have a Site ID yet, tell them: "Go to your Mitr dashboard β Integration Hub β Add New App, choose your platform, and enter your domain (for a website) or bundle/package ID (for an app). You'll get a Site ID immediately, plus a Secret Key if you chose a non-web platform." You cannot generate a Site ID yourself.
Do not invent a placeholder Site ID and ship it. Use a literal placeholder like YOUR_SITE_ID until the human gives you a real one.
Step 2 β Detect the platform (if not already told)
| You find... | Platform | Go to |
|---|---|---|
pubspec.yaml with flutter: section | Flutter app | Step 3C |
package.json with "next" | Next.js | Step 3B |
package.json with "react" (no Next, no "react-native") | React SPA (web) | Step 3B |
package.json with "react-native", or an ios/android folder from RN/Expo | React Native app | Step 3D |
package.json with "vue" / "nuxt" | Vue / Nuxt | Step 3B |
Package.swift or an Xcode project, no Flutter/RN | Native iOS/macOS (Swift) | Step 3E |
build.gradle(.kts) targeting Android, no Flutter/RN | Native Android (Kotlin) | Step 3F |
package.json with no browser entry point β an API route, cron script, or CLI | Node.js backend | Step 3G |
| Email template, restrictive CSP, or an HTML-only no-code field | No-JS environment | Step 3H |
Plain .html files, no framework | Static site | Step 3A |
| WordPress/Shopify/Webflow/Squarespace/Wix (human describes it) | No-code platform | Step 3A (no-code variant) |
| No JS repo at all | Ask which of the above, or default to Step 3A no-code variant | β |
None of the mobile/desktop/server SDKs (Flutter, React Native, Swift,
Kotlin, Node) are on a public package registry yet (pub.dev, npm, Maven
Central, SPM index) β they ship from mitr-sdk-suite as
local/git/path dependencies. If the human's repo has no vendored copy,
tell them it needs to be added first.
Step 3A β Plain HTML / static site / no-code platform
Paste this once, right before </head> or the closing </body> tag:
<script src="https://mitranalytics.dev/vendor/mitr.js"
data-site-id="YOUR_SITE_ID"
async defer></script> That's it β no other code required. It auto-tracks page views, including client-side route changes if the site later adds one.
No-code platforms (WordPress, Shopify, Webflow, Squarespace, Wix): the human pastes the exact same snippet into their platform's "Custom Code" / "Header Scripts" / "Tracking Code" section β every one of these platforms has such a setting.
Do not add a secret key anywhere in web output β it isn't needed and shouldn't be embedded in public HTML/JS.
Step 3B β JS framework (React, Next.js, Vue, Nuxt, etc.)
Add the SDK once near your app's root/entry point β not to every page/component.
Plain script tag (simplest, works anywhere):
<script src="https://mitranalytics.dev/vendor/mitr.js" data-site-id="YOUR_SITE_ID" async defer></script>
Next.js (App Router) β app/layout.jsx:
'use client';
import Script from 'next/script';
export default function RootLayout({ children }) {
return (
<html><body>
{children}
<Script src="https://mitranalytics.dev/vendor/mitr.js" data-site-id="YOUR_SITE_ID" strategy="afterInteractive" />
</body></html>
);
} React SPA β call once at startup instead of a script tag, if preferred:
useEffect(() => { window.Mitr.init('YOUR_SITE_ID'); }, []); Auto-tracking already covers client-side route changes β no per-route tracking calls needed for React Router, Vue Router, or similar.
Custom events, wherever the relevant action happens:
window.Mitr.track('signup_completed', { plan: 'pro' }); On login/logout, if the app has auth:
window.Mitr.identify(user.id); // after login window.Mitr.reset(); // on logout
Full API reference: JavaScript SDK.
Step 3C β Flutter app (iOS / Android / desktop / Flutter Web)
-
Add the dependency (adjust the path to wherever this SDK lives
relative to the client's project β tell the human it needs to be
vendored locally, since it isn't on pub.dev yet):
dependencies: mitr_flutter: path: ../path/to/mitr-sdk-suite/mitr_flutterThen runflutter pub get. -
Initialize once, at the top of
main(), using both the Site ID and Secret Key from Step 1:import 'package:mitr_flutter/mitr_flutter.dart'; Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); await MitrAnalytics.instance.initialize( siteId: 'YOUR_SITE_ID', secretKey: 'YOUR_SECRET_KEY', ); runApp(const MyApp()); } -
Add automatic screen tracking to the app's router:
MaterialApp( navigatorObservers: [MitrNavigationObserver()], // ... )
(Useobservers:instead if the app usesGoRouter.) -
Custom events, wherever relevant:
await MitrAnalytics.instance.track('purchase_completed', meta: {'value': 49.99}); -
On login/logout, if the app has auth:
MitrAnalytics.instance.identify(user.id); // after login MitrAnalytics.instance.reset(); // on logout
Full API reference: Flutter SDK.
Step 3D β React Native (bare RN or Expo dev-client)
-
Add the dependency and its one required peer (not yet published to
npm β if there's no vendored copy in the repo, tell the human it
needs to be added from
mitr-sdk-suite/mitr-react-nativefirst). Needs a dev client, not Expo Go β it uses AsyncStorage, unavailable in Expo Go's managed sandbox:npm install @mitr/react-native @react-native-async-storage/async-storage
-
Initialize once near the app root, using both the Site ID and Secret Key:
import { MitrAnalytics } from '@mitr/react-native'; export const mitr = new MitrAnalytics({ siteId: 'YOUR_SITE_ID', secretKey: 'YOUR_SECRET_KEY', }); -
Automatic screen tracking, if the app uses React Navigation:
<NavigationContainer onStateChange={(state) => mitr.trackNavigationStateChange(state)}>Otherwise callmitr.pageView('ScreenName')manually wherever screens change. -
Custom events and identify/reset β same shape as the web SDK:
await mitr.track('signup_completed', { metadata: { plan: 'pro' } }); mitr.identify(user.id); // after login mitr.reset(); // on logout
Full API reference: React Native SDK.
Step 3E β Native iOS/macOS (Swift)
- Add as a Swift Package dependency (Xcode: File β Add Package Dependencies, pointing at
mitr-sdk-suite/mitr-swiftβ not yet published as a versioned/hosted package). -
Initialize once, using both the Site ID and Secret Key. Every call is
asyncβMitrAnalyticsis anactor:import MitrAnalytics let mitr = MitrAnalytics(options: .init( siteId: "YOUR_SITE_ID", secretKey: "YOUR_SECRET_KEY" )) -
Custom events, screen views, identify/reset:
await mitr.track("purchase_completed", metadata: ["value": 49.99]) await mitr.pageView("HomeScreen") await mitr.identify(user.id) // after login await mitr.reset() // on logout -
Deep-link attribution needs a manual hook (Swift has no SDK-level lifecycle hook, unlike React Native):
.onOpenURL { url in Task { await mitr.captureDeepLink(url) } }
Full API reference: Swift SDK.
Step 3F β Native Android (Kotlin)
-
Add as a local Gradle module dependency (not yet published to Maven Central):
// app/build.gradle.kts dependencies { implementation(project(":mitr-kotlin:android")) } -
Initialize once, using both the Site ID and Secret Key. Every method is
suspendβ call from a coroutine:import dev.mitranalytics.sdk.android.MitrAnalytics import dev.mitranalytics.sdk.core.MitrAnalyticsOptions val mitr = MitrAnalytics( context = applicationContext, options = MitrAnalyticsOptions(siteId = "YOUR_SITE_ID", secretKey = "YOUR_SECRET_KEY"), ) -
Custom events, screen views, identify/reset:
lifecycleScope.launch { mitr.track("purchase_completed", metadata = mapOf("value" to 49.99)) mitr.pageView("HomeScreen") mitr.identify(user.id) // after login }
Full API reference: Kotlin SDK.
Step 3G β Node.js backend (server, webhook handler, cron, CLI)
-
Install (not yet published to npm β if no vendored copy exists, add it from
mitr-sdk-suite/mitr-nodefirst):npm install @mitr/node
-
Initialize once, using both the Site ID and Secret Key:
import { MitrAnalytics } from '@mitr/node'; const mitr = new MitrAnalytics({ siteId: 'YOUR_SITE_ID', secretKey: 'YOUR_SECRET_KEY', }); - Multi-user servers: pass
userIdper call rather than callingidentify()β a server process serves many end users, unlike a browser tab or app instance:await mitr.track('checkout_completed', { userId: req.user.id, metadata: { amount: req.body.amount }, }); - Always flush before exit β nothing is persisted to disk on this SDK, unlike the browser/mobile ones:
process.on('SIGTERM', async () => { await mitr.close(); process.exit(0); });
Full API reference: Node.js SDK.
Step 3H β No-JS environment (email, restrictive CSP, HTML-only no-code field)
There's no SDK to install β the whole mechanism is one <img> tag:
<img src="https://api.mitranalytics.dev/api/v1/p.gif?sid=YOUR_SITE_ID&p=/newsletter&t=email_open"
width="1" height="1" alt="" style="display:none" /> sid is required; p (path), t
(event type), uid (pre-hashed user id), utm_*,
and meta_* (custom properties) are optional query params.
No batching, retry, or identify()/reset()
session state β a pixel fires once per load and always returns a valid
image even if the underlying event was rejected, so don't rely on it
for delivery confirmation; check the dashboard instead.
Full reference: Tracking Pixel.
Step 4 β Verify it worked
- Run the site/app and navigate around.
- In the Mitr dashboard β Integration Hub, open the app's card and click "Check connection" β it should flip from "Pending" to "Connected" within a few seconds of the first event being sent.
-
If it doesn't:
- Web: open devtools β Network tab, find the request to
.../api/v1/event. A403means the page's domain doesn't match what was registered (common cause: testing on a different domain/subdomain βlocalhostis always allowed in non-production environments). A404means the Site ID is wrong or the app was deleted. - Flutter/React Native/Swift/Kotlin (debug builds): check the console β initialization fires a diagnostic ping and logs the result immediately, most commonly flagging a wrong Secret Key.
- Node.js: pass
debug: truein the constructor options to log each batch send attempt and its result.
- Web: open devtools β Network tab, find the request to
Things to never do
- Never put a mobile/desktop/server Secret Key into web-facing HTML/JS β plain web doesn't need one and it would be publicly visible in page source.
- Never pass a raw email, name, or other PII into
track/identify/pageViewmetadata β only pass an opaque user id toidentify; every SDK hashes it before it's sent (client-side for Flutter/RN/Swift/Kotlin/web, server-side inside the process for Node). - Never call
init/initialize/new MitrAnalytics(...)more than once per app instance, and never block app/request startup waiting on it β every SDK degrades gracefully (queue locally, retry later) if the network or backend is temporarily unavailable. The one exception:@mitr/nodequeues in memory only, so always callawait mitr.close()in the shutdown handler or the final batch is lost. - Never assume a mobile/desktop/server SDK is on a public package registry β none of Flutter, React Native, Swift, Kotlin, or Node.js are published yet. They come from
mitr-sdk-suiteas local/git/path dependencies.