Documentation

Everything you need to set up and use Bananalytics Analytics

Quick Start

Get Bananalytics running in 5 minutes with Docker.

1. Clone & start the backend
git clone https://github.com/bananalytics-analytics/bananalytics.git
cd bananalytics/server
docker-compose up -d
2. Run database migrations
docker exec -i server-postgres-1 psql -U bananalytics -d bananalytics \
  < internal/storage/postgres/migrations/001_create_projects.up.sql
docker exec -i server-postgres-1 psql -U bananalytics -d bananalytics \
  < internal/storage/postgres/migrations/002_create_events.up.sql
docker exec -i server-postgres-1 psql -U bananalytics -d bananalytics \
  < internal/storage/postgres/migrations/003_add_key_hashes.up.sql
docker exec -i server-postgres-1 psql -U bananalytics -d bananalytics \
  < internal/storage/postgres/migrations/004_add_geo.up.sql
3. Create a project
curl -X POST http://localhost:8080/v1/projects \
  -H "Content-Type: application/json" \
  -d '{"name":"My App"}'

# Returns: { "write_key": "rk_...", "secret_key": "sk_..." }

AI Setup

Copy the prompt below and paste it into Claude Code, Cursor, Copilot, or any AI coding agent. It will integrate Bananalytics into your React Native app in one run.

Integrate Bananalytics analytics into this React Native app. Bananalytics is a self-hosted, privacy-first product analytics tool. Follow these steps exactly:

## 1. Install dependencies
...

How it works

  • Copy the prompt above into Claude Code, Cursor, or any AI coding agent
  • Replace YOUR_WRITE_KEY and YOUR_ENDPOINT with your actual values
  • Answer the AI when it asks what events you want to track
  • Done — the AI installs the SDK, adds tracking calls, and instruments your app

React Native SDK

Install the SDK in your React Native app.

Install
npm install @bananalytics/react-native
npm install @react-native-async-storage/async-storage
Initialize
import { Bananalytics } from '@bananalytics/react-native';

Bananalytics.init({
  apiKey: 'rk_your_write_key',
  endpoint: 'https://your-server.com',
  debug: true,
});
Track events
// Track custom events
Bananalytics.track('button_clicked', { button: 'signup' });

// Track screen views
Bananalytics.screen('HomeScreen');

// Identify users
Bananalytics.identify('user-123', { plan: 'pro' });

// Flush events immediately
await Bananalytics.flush();

React Provider

import { BananalyticsProvider, useBananalytics, useTrackScreen } from '@bananalytics/react-native';

function App() {
  return (
    <BananalyticsProvider config={{ apiKey: 'rk_...', endpoint: '...' }}>
      <HomeScreen />
    </BananalyticsProvider>
  );
}

function HomeScreen() {
  useTrackScreen('HomeScreen');
  const bananalytics = useBananalytics();

  return (
    <Button onPress={() => bananalytics.track('tapped')} title="Tap" />
  );
}

Configuration Options

OptionDefaultDescription
apiKeyrequiredWrite-only API key
endpointrequiredBackend URL
flushInterval30000Auto-flush interval (ms)
flushAt20Events before auto-flush
maxQueueSize1000Max events in memory
maxRetries3Retry attempts
debugfalseConsole logging
trackAppLifecycletrueAuto-track foreground/background
sessionTimeout1800000Session timeout (ms)

Event Strategy

A good event strategy is the difference between a dashboard full of noise and one that drives decisions. Here is how to instrument your app to get the most out of Bananalytics.

Core Events You Should Track

These events power the dashboard features and give you a complete picture of user behavior.

Onboarding & Activation

Measure how users get from install to value. Build funnels to find where they drop off.

app_opened

Distinguish first launch from returning users

first_open: boolean

signup_started

See which auth methods convert best

method: 'email' | 'google' | 'apple'

signup_completed

Measure signup friction. Funnel: started → completed

method, time_to_complete_ms

onboarding_step_viewed

Find which onboarding step loses users

step: number, step_name: string

onboarding_completed

Track activation rate

steps_completed: number

Core Product Usage

Track the actions that define your product's value. These power retention cohorts.

feature_used

See which features drive retention

feature: string

content_viewed

Understand what users engage with

content_id, content_type, source

search_performed

Discover unmet needs (zero-result searches)

query, results_count

item_created

Measure creation activity as engagement signal

item_type, item_id

share_tapped

Track organic virality loops

content_type, share_method

Revenue & Conversion

Track the money path. Build funnels from browse to purchase to optimize conversion.

product_viewed

Top of the purchase funnel

product_id, price, category

add_to_cart

Mid-funnel intent signal

product_id, quantity, price

checkout_started

High-intent moment — track abandonment

cart_value, item_count

purchase_completed

Revenue tracking. Compare to checkout_started for drop-off

order_id, total, currency, items

subscription_started

SaaS conversion tracking

plan, price, trial: boolean

subscription_cancelled

Understand churn reasons

plan, reason, days_active

Engagement & Retention Signals

These events feed your retention heatmap and help predict churn.

session_started

Session count per user = engagement health

(auto-tracked)

notification_received

Measure push notification effectiveness

type, campaign_id

notification_tapped

Tap rate = notification quality signal

type, campaign_id

rating_prompted

Optimize when to ask for reviews

days_since_install

rating_submitted

Track app store rating health

stars, days_since_install

Errors & Friction

Track where users hit walls. These often reveal the biggest conversion opportunities.

error_occurred

Surface bugs that affect real users

error_code, screen, message

payment_failed

Lost revenue you can recover

error_type, retry_count

form_abandoned

Find the field that kills your form

form_name, last_field_filled

permission_denied

Users refusing permissions = feature blockers

permission_type

Using Events with Dashboard Features

Dashboard FeatureEvents to TrackInsight You Get
Funnelssignup_started → signup_completed → first_purchaseWhere users drop off in your conversion flow
RetentionAny recurring action (session_started, feature_used)How many users come back on day 1, 7, 30
Live ViewAll events in real-timeVerify tracking works, monitor launches & campaigns
GeographyAll events (geo is extracted from IP)Where your users are, localization priorities
Sessionssession_started + any user-identified eventsDebug individual user journeys

Best Practices

  • Use past tense for event namespurchase_completed not purchase. It is clear that the action happened.
  • Use snake_case consistently — Bananalytics groups events by name. Mixed casing creates duplicates.
  • Keep properties flat{ price: 49.99, currency: "USD" } not { payment: { price: 49.99 } }. Easier to query.
  • Call identify() early — As soon as the user logs in. This links anonymous events to a real user for session tracking.
  • Track screens with screen() — It auto-creates screen_view events, which powers the top events dashboard and retention.
  • Start with 10-15 events max — You can always add more. Too many events early on create noise and make dashboards hard to read.
Complete example: e-commerce app
// After user logs in
Bananalytics.identify('user-123', { plan: 'free' });

// Screen views (auto-tracked with useTrackScreen hook)
Bananalytics.screen('HomeScreen');
Bananalytics.screen('ProductScreen');

// Core conversion funnel
Bananalytics.track('product_viewed', {
  product_id: 'prod_abc', price: 49.99, category: 'shoes'
});
Bananalytics.track('add_to_cart', {
  product_id: 'prod_abc', quantity: 1, price: 49.99
});
Bananalytics.track('checkout_started', {
  cart_value: 49.99, item_count: 1
});
Bananalytics.track('purchase_completed', {
  order_id: 'ord_xyz', total: 49.99, currency: 'USD'
});

// Engagement signals
Bananalytics.track('search_performed', {
  query: 'running shoes', results_count: 24
});
Bananalytics.track('share_tapped', {
  content_type: 'product', share_method: 'instagram'
});

// Error tracking
Bananalytics.track('payment_failed', {
  error_type: 'card_declined', retry_count: 0
});

API Reference

Include your API key in every request:

curl -H "Authorization: Bearer sk_your_secret_key" http://localhost:8080/v1/query/events
rk_* — Write Key (ingestion)sk_* — Secret Key (queries)

Error Codes

StatusCodeDescription
400BAD_REQUESTInvalid request body or parameters
400VALIDATION_FAILEDEvent validation failed
401UNAUTHORIZEDMissing or invalid API key
413PAYLOAD_TOO_LARGERequest body exceeds 5MB
429RATE_LIMITEDToo many requests
500INTERNAL_ERRORServer error

Self-Hosting

Deploy on any VPS with Docker. Recommended: Hetzner CX22 (~$4/month).

Production deployment
# SSH into your server
ssh root@your-server-ip

# Install Docker
curl -fsSL https://get.docker.com | sh

# Clone and start
git clone https://github.com/bananalytics-analytics/bananalytics.git
cd bananalytics/server

# Set your domain (Caddy auto-provisions SSL)
echo "ROCHADE_DOMAIN=analytics.yourdomain.com" > .env
echo "ROCHADE_CORS_ORIGINS=https://yourdomain.com" >> .env

# Start everything (Postgres + Go server + Caddy HTTPS)
docker-compose up -d

Point your domain's DNS A-record to your server IP. Caddy handles SSL automatically via Let's Encrypt.

Configuration

VariableDefaultDescription
ROCHADE_PORT8080HTTP server port
ROCHADE_DB_DSNrequiredPostgreSQL connection string
ROCHADE_LOG_LEVELinfodebug, info, warn, error
ROCHADE_RATE_LIMIT_RPM1000Requests/min per API key
ROCHADE_IP_RATE_LIMIT_RPM300Requests/min per IP
ROCHADE_CORS_ORIGINS*Allowed origins
ROCHADE_DB_MAX_CONNS25Max DB connections
ROCHADE_GEOIP_DBPath to GeoLite2-City.mmdb
ROCHADE_DOMAINlocalhostDomain for Caddy HTTPS

Privacy & Compliance

Bananalytics is designed with privacy in mind. All data stays on your infrastructure — no third-party services, no data sharing.

  • Self-hosted: Data never leaves your server
  • Opt-out support: Built-in consent management in the SDK
  • PII sanitization: Auto-strips email, phone, SSN from auto-captured events
  • No cookies: Uses device storage, not browser cookies
  • GDPR-friendly: You control the data, you handle deletion requests