Your product stays yours.
Install marketing first and optional analytics second. Your website, web app and mobile backend stay yours. Public discovery is optional and requires an approved offer.
Marketing first. Analytics separately.
Your workspace includes a personalised installation guide for HTML, WordPress, web apps and native backends. It uses your selected property's domain and actual public key. marketing.js displays relevant approved offers in an explicit block; tracker.js measures consented events separately. Neither creates visitors or automatically registers third-party accounts.
Get my installation guide ↗Download the WordPress plugin ZIP ↗. Optional IndexNow notifications follow public content changes after explicit activation; search engines decide indexing. Native apps use their public landing site for SEO and the existing authenticated backend relay for analytics.
1. Connect your property
Create an account, verify your email and add your website or app backend origin under Connected properties. Accept the DPA, verify domain ownership through DNS and copy the public site key. A server key is displayed for five minutes after generation. It belongs only on your backend.
Connect a property ↗2. Websites & web apps
Add this to your own HTML or shared app layout. Each distinct origin needs its own property. The tracker waits for explicit consent; connect these commands to your existing consent interface. The example below does not grant consent automatically.
<script>
window.growth = window.growth || function () {
(window.growth.q = window.growth.q || []).push(arguments);
};
</script>
<script defer src="https://rookglint.bid/tracker.js"
data-site="YOUR_PUBLIC_SITE_KEY"></script>
// After the visitor allows analytics:
window.growth('consent', true);
// On refusal or withdrawal:
window.growth('consent', false);
// Track a meaningful action after consent:
window.growth('event', 'signup');In React/Next.js, load the script once in your shared layout. Browser history navigation is tracked after consent; hash-only routes require named events. Add your Growth host to script-src and connect-src in your website CSP. Your checkout and content remain where they are. The tracker does not publish campaigns or modify your pages.
3. Backend & revenue events
Check analytics permission on your backend before forwarding events. Use random consented session IDs, not email or account IDs. Re-use the same random event ID when retrying. Reported revenue is EUR cents. Purchase events should follow your own verified payment notification or completed-order logic.
// Runs only on YOUR backend. Never in a browser or mobile binary.
if (analyticsConsentIsValid) {
await fetch('https://rookglint.bid/api/collect/server', {
method: 'POST',
headers: {
Authorization: 'Bearer ' + process.env.GROWTH_SERVER_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
eventId: stableOrderEventId,
sessionId: consentedSessionId,
name: 'purchase', path: '/checkout/success',
source: 'newsletter', campaign: 'autumn_launch',
device: 'native', revenue: 4900, currency: 'EUR', consent: true
})
});
}Fields: site (browser only), eventId, sessionId, name, path, source, campaign, device, consent and server-only revenue/currency/country (ISO alpha-2). Unknown fields are rejected. Event/session IDs use 16–80 / 16–100 alphanumeric or hyphen characters. Event names use lowercase letters, numbers and underscores, up to 64 characters. Paths begin with /; query strings and fragments are discarded and obvious identifiers are redacted. Free-form visitor properties are not accepted. Accepted events return 202, denied browser origins 403 and invalid server keys 401. Duplicate event IDs are idempotent per property.
4. iOS · via your backend
A local Swift package is available in sdk/swift. Add it as a local package in Xcode and import GrowthClient. This Swift example sends an event to an endpoint you implement on your backend. That endpoint applies your app authentication, checks consent and forwards validated fields with the Growth server key. Keep that key out of the iOS app.
import GrowthClient
// Your relay stores the Growth server key; the app never receives it.
let analytics = try GrowthClient(
relayURL: URL(string: "https://YOUR_BACKEND.example/analytics")!
)
// Connect to your app's actual analytics consent choice:
await analytics.setConsent(true) // only after the user allows analytics
let event = try await analytics.makeEvent(name: "pageview", path: "/screens/home")
try await analytics.send(event, authorization: "Bearer " + appAccessToken)
// A manual retry reuses event. Re-check permission before retrying.
// On refusal / withdrawal:
await analytics.setConsent(false)The package uses a random memory-only session, validates events, removes query strings, refuses redirects and stores no cookies or Growth credentials. Revocation blocks new requests; an event already in flight cannot be recalled. Swift compilation and consent checks ran on macOS; integration in an iOS simulator/device remains required.
Rotate session IDs on withdrawal, expiry or account change. Do not send IDFA, device identifiers, emails or account IDs. Configure your app’s privacy disclosures for the actual integration; this sample does not certify App Store or legal compliance.
5. Android · the same contract
Download the Java 8 JAR, add it to your app's libs directory and use your normal authenticated relay. Add Android INTERNET permission. This synchronous method must run off the main thread. The SDK has no dependencies, queue or automatic retries. A JVM compilation and checks passed; Android device/emulator integration remains app-specific. Apps with a global Java CookieHandler must use a cookie-free networking setup for this client.
import com.flockaro.analytics.GrowthClient
val analytics = GrowthClient("https://YOUR_BACKEND.example/analytics")
// Only after your actual consent interface grants permission:
analytics.setConsent(true)
val event = analytics.prepare("pageview", "/screens/home")
Thread {
try { analytics.send(event, "Bearer " + appAccessToken) }
catch (error: Exception) { /* Report failure without logging payloads. */ }
}.start()
// On withdrawal: stop locally and ask your backend to revoke event.sessionId.
analytics.setConsent(false)6. Stripe revenue and workspace billing
In Connected properties, save the signing secret for a Stripe snapshot webhook subscribed to payment_intent.succeeded and charge.refunded. A random, consented session reference belongs in PaymentIntent metadata: growth_session and growth_consent=true. For Stripe Checkout, set payment_intent_data.metadata on your own backend after checking permission. window.growth('session') returns the current consented browser reference, or null. Never trust a client permission flag without your own backend check.
Signed EUR payment notifications deduplicate by payment reference; cumulative refunds are applied without counting a retry twice. Test payments are stored separately and excluded from live totals. Fees, disputes and other currencies are not reconciled. No card details, email or full payment body are stored in analytics.
For your app's withdrawal endpoint, authenticate the request and call GrowthServer.revokeSession(sessionId), or POST the session reference to /api/consent/revoke with your server key. The browser tracker sends its own current-session withdrawal request. Stored events and payment analytics for that session are erased and blocked. An offline or failed withdrawal must be retried through your backend or operator; already-sent requests cannot be recalled.
Workspace subscription billing is a separate integration in Billing. It stays off until the operator configures a real Stripe price, API key and billing webhook secret. No tariff is invented. Hosted checkout presents the actual price; one pending session is reused. Account erasure closes pending checkout and cancels recorded subscriptions first.
7. Team access and growth automation
Owners can add verified existing accounts as Viewer or Editor. Viewers read/export analytics; editors also manage goals, funnels and experiments. Credentials, membership, billing and deletion stay owner-controlled.
Growth Lab creates tracked links, channel drafts and session-goal measurements on your domain. Optional automatic category matches refresh partner widgets after owner opt-in. Google Ads automation uses the owner's encrypted OAuth credentials and an existing reviewed paused campaign. Launch needs a password, typed LAUNCH, budget acknowledgement and a live production supervisor. Time/spend checks can request pausing; provider delays and outages mean no guaranteed spending cap. No real advertising is activated by installing the SDK.
6. Privacy, verification & operations
Add the DNS TXT record shown in your property. Collection stays off until ownership is verified; loopback testing is permitted only when Growth itself runs locally. Browser requests must match the configured origin exactly. Public site keys cannot prevent fabricated browser events; reported revenue must use the authenticated backend API.
Wire consent on each customer website/app. The platform’s Privacy button does not control another domain. The browser tracker respects Do Not Track and Global Privacy Control, stops sending on withdrawal and removes its session value. It does not use cookies, fingerprinting or cross-site identifiers; consented sessionStorage still requires the relevant permission.
The standard pnpm start runner applies retention at startup and hourly. Custom deployments must run pnpm privacy:prune at least daily. Review events and paths for personal data, configure HTTPS, hosting contracts and actual operator details before public use. Single-page sessions and observed activity span are explicit measurements, not guaranteed equivalents to another vendor’s bounce or time-on-site definitions.
Current limits: native device integration and real provider activation still need validation. Country data require a trusted configured edge or customer backend; there is no local IP database. Stripe analytics cover consented EUR PaymentIntents/refunds. Google Ads controls existing campaigns; ad creation, Meta and marketing email delivery are not included. Audience growth is not guaranteed.
Read the Data Processing Agreement ↗