# Connect a mobile app to Appbase For a coding agent working **inside the customer's app repository**. The customer signs in at https://appbase.so, clicks **Copy prompt** in the empty dashboard or **Create app** dialog, and gives you the prompt. They do not need to create the app or send their repository to Appbase. ## 1. Inspect before changing anything Read this repository's agent instructions and package/build configuration. Detect React Native (Expo or bare) versus native Swift, the package manager/toolchain, existing analytics initialization and environment configuration. Preserve the app's structure and existing integrations. Do not upgrade Expo/React Native to install analytics. React Native **0.2.1** is published on npm. Native Swift **0.1.0** supports Swift 6 / iOS 15+ and is available through Swift Package Manager from https://github.com/appbasehq/appbase-swift.git. Follow `/docs/swift-sdk.md` and pin the reviewed version. Do not invent a release version or install the npm package in a native Swift app. Kotlin remains unsupported. Check `/docs/sdk-compatibility.md` for capabilities and release status. If Appbase is already configured, reuse its app IDs and collection keys. Do not create duplicates or replace keys. A new-app prompt creates **one new app**. A prompt copied from an existing app is already scoped to that app and supplies its exact name/slug; call the same creation endpoint with those values to recover its saved configuration, not create another app. It cannot claim or change another app. If keys were rotated or no saved configuration is available, use the existing local configuration or ask the owner to open App setup; never rotate keys automatically. Read these public references as needed: - https://appbase.so/docs/sdk.md — React Native types and examples - https://appbase.so/docs/swift-sdk.md — Swift types, versioned Swift Package Manager installation and native lifecycle - https://appbase.so/docs/sdk-compatibility.md — shared behaviors and current availability - https://appbase.so/docs/tracking-plan.md — metric definitions - https://appbase.so/docs/paywalls.md — presentation/checkout attribution - https://appbase.so/docs/revenuecat-setup.md — verified billing, optional after SDK setup ## 2. Authenticate and create the app Use the API URL and `apb_setup_…` token **from the customer's prompt**. The hosted API is `https://api.appbase.so`. Never send this token to another origin, put it in a URL, include it in a mobile environment variable/bundle, or print it. This is private setup access, not a collection key. If persistence is needed, first add `.appbase/` to `.gitignore` and `.dockerignore` (when present), then save the setup token in `.appbase/setup-access.json`, with mode 0600. Read it from that file at runtime; do not embed it in a shell command or source file. Never overwrite another connection silently. Keep this ignored token file for future setup checks; delete it when access is revoked or no longer needed. Do not write secrets into AGENTS.md or SKILL.md. Make authenticated JSON requests with `Authorization: Bearer `. No SDK/CLI/MCP install is needed to call this API; Node's built-in fetch is sufficient. **POST `/v1/agent/apps`** ```json { "name": "Your app's actual display name", "slug": "stable-lowercase-app-slug" } ``` Infer the app name/slug from its configuration when clear; otherwise ask the customer. Do not change the slug on a retry. A successful new-app call returns **201**. An existing-app prompt returns **200**, and retrying that same slug with the same token returns **200** with the same keys. One token creates at most one development/production pair, owned by the signed-in customer. Additional fields are rejected; ownership is always assigned from the authenticated setup token. Response: ```json { "environments": [ { "app": { "id": "UUID", "name": "My app", "slug": "my-app", "environment": "development" }, "collection_key": "ma_dev_…" }, { "app": { "id": "UUID", "name": "My app", "slug": "my-app", "environment": "production" }, "collection_key": "ma_prod_…" } ] } ``` Real responses include additional app metadata. Select entries by `environment`, not array index. Save the API URL, both app IDs and collection keys in `.appbase/project.json` (ignored, mode 0600) **before editing the app**. Reuse the app's established build/environment mechanism for SDK configuration; do not overwrite `.env` files. Collection keys are public and write-only, unlike the setup token. Errors: - **401:** access expired/revoked/invalid; ask for a fresh dashboard prompt. Do not try the token against reporting endpoints. - **409 (app exists):** use saved local configuration or the existing app's setup brief. Do not choose a random slug to bypass this or rotate keys automatically. - **409 (prompt already used):** retry its original slug. This prompt cannot create another app. - **409 (keys changed):** a dashboard rotation wins; use the new saved configuration. Old keys are not recovered. - **429:** respect Retry-After. **5xx/network failure:** retry with backoff, same token and slug. Setup tokens have no expiration and stay valid until revoked in the prompt box’s **Setup access** disclosure. They create/recover their one app and inspect its delivery counts and RevenueCat connection status. They cannot read reports, users, feedback or provider secrets, configure another app, replace keys, disable collection, or issue other setup tokens. Revoking setup access does not revoke collection keys. ## 3. Install and configure the SDK **Swift:** add `https://github.com/appbasehq/appbase-swift.git` with **Exact Version: 0.1.0** in Xcode, add the `AppbaseAnalytics` library product, and commit `Package.resolved`. Follow the Swift guide with the same collection configuration returned above. Keep one long-lived `Analytics` actor; default storage and UIKit lifecycle adapters are included. Use native onboarding/paywall/feedback APIs. Skip the JavaScript commands below. Validate physical-device behavior, app privacy disclosures and real store purchases separately from simulator results. **React Native:** install the pinned public package with the repository's existing package manager: ```sh npm install @appbasehq/react-native@0.2.1 # For Expo: select native adapters compatible with the existing Expo version. npx expo install @react-native-async-storage/async-storage @react-native-community/netinfo expo-crypto ``` For bare React Native use compatible storage/network adapters and a secure UUID generator; follow the SDK reference. Do not add Expo solely for analytics. Initialize once outside React rendering, using the actual app version and **development collection key** initially. Example for Expo: ```ts import AsyncStorage from '@react-native-async-storage/async-storage'; import NetInfo from '@react-native-community/netinfo'; import { AppState, Platform } from 'react-native'; import { randomUUID } from 'expo-crypto'; import { createReactNativeAnalytics } from '@appbasehq/react-native'; export const analyticsReady = createReactNativeAnalytics({ apiUrl: APPBASE_API_URL, // Actual values from the app's build configuration. collectionKey: APPBASE_COLLECTION_KEY, platform: Platform.OS === 'ios' ? 'ios' : 'android', appVersion: APP_VERSION, storage: AsyncStorage, generateId: randomUUID, appState: AppState, networkInfo: NetInfo, }); ``` Handle initialization/storage errors without blocking startup. Use explicit build configuration for development versus production. A release build/TestFlight build can still be a test; do not choose production only because `__DEV__` is false. Save the production configuration, but **do not send test data to it**. Both environments use the same HTTPS API. App opens are automatic with AppState. Never manually track `app_first_open` or `app_active`. Screen views are not auto-captured. ## 4. Instrument what exists First get automatic opens working. Then map the app's actual onboarding screens, paywalls and a few meaningful feature actions using the SDK reference. Do not invent screens, events, completed purchases or sample traffic to make reports look populated. - Onboarding: `onboarding({ id, version: 1, steps: [...] })`; track when each screen is visible, actual answers, and actual completion. Call `start()` to resume; `restart()` only for a deliberate restart. Persisted attempts and ordered steps matter. - Paywalls: one ID per design, numeric version, separate placement per entry point. Link onboarding context when applicable and supply known entitlement/access state. Read exact helpers; do not guess their arguments. - Identity: `identify(opaqueUserId)` after sign-in/restored session; `reset()` on logout/account switch. Do not send emails as user IDs. - Features: explicit `track(name, properties)` after real actions; stable names, useful non-sensitive metadata. - RevenueCat: keep existing billing working. Follow the connection handoff below and the [RevenueCat guide](revenuecat-setup.md) for identity linking and transaction attribution. Client checkout callbacks alone do not verify trials/payments. ### RevenueCat connection handoff If the repository already uses RevenueCat, call **GET `/v1/agent/revenuecat`** with the same setup token after creating/recovering the Appbase app. It returns only this app's connection state, owner connection URL, mappings, aggregate receipt counts and `history` import states—never provider credentials or customer records. - `authorization_required` / `reconnect_required`: give the user the returned `connect_url`. They sign in to their Appbase account and authorize RevenueCat through OAuth, selecting the intended project(s), or enter a secret v2 key directly into Appbase. Never ask them to paste a provider secret into chat or give you browser session cookies. - `selection_required`: the user selects their RevenueCat project/store app on that page. Appbase creates its own webhook, stores its secrets and maps sandbox → Development and production → Production. Test Store maps only to Development. The same page can add another store. - `connected`: continue SDK identity linking using the returned `project_id`, and pass real transaction IDs to the paywall helpers. Existing manual connections are also reported. Do not create another RevenueCat project or alter products, offerings or purchase behavior for this integration. - If the owner already selected the provider app but webhook setup was interrupted, **POST `/v1/agent/revenuecat/retry`** with `{}` retries only that saved selection. It accepts no app IDs, credentials or configuration overrides. Respect 429/backoff; do not loop indefinitely. Without an authorized selection, return the connection link to the user. - Automatic setup attempts a bounded initial history import. Inspect `history`: `completed` means the available import succeeded, not complete historical coverage. If `not_started`, `failed` or `interrupted`, **POST `/v1/agent/revenuecat/sync`** with `{}` to retry using the owner's stored access; allow up to 180 seconds. Do not start another import while `running`. A failed import does not disable the connected webhook. The sync returns completion only; no customer records reach the agent. Handle missing permissions or `sync_limit` explicitly rather than repeatedly retrying a large project. Refreshes are explicit; do not schedule recurring sync. - Check status at a reasonable interval (for example 10 seconds while actively helping, at most 15 minutes); no background polling job. A connected webhook is not verified delivery: `delivery_verified` becomes true only after an actual mapped subscription event arrives. Use a real sandbox purchase to verify the flow; never fabricate production revenue. Report separately if native/store testing is unavailable. The agent can continue independent app-code work while the user grants access. If no RevenueCat integration exists, finish core analytics and explain that billing is optional; do not add a payment system without being asked. ## 5. Verify actual delivery **GET `/v1/agent/setup`** with the same setup token returns `state` and per-environment counts: `events`, `first_opens`, `app_opens`, `last_received_at`, plus app ID/name/slug. Before creation, `state` is `waiting_for_app`. After creation it is `waiting_for_events`; after any development event it is `events_received`. This is an aggregate delivery check, not a report or proof of every tracking call's correctness. No user IDs, properties, keys or event payloads are returned. 1. Record initial counts, open the actual development app, then flush or wait for delivery. 2. Confirm first-open/foreground counts increased. Background → foreground should add app activity, not a new first open. Restart should preserve identity and first-open state. Exercise offline → reconnect when a device/simulator is available. 3. Check the production count did not increase. Do not seed synthetic users. 4. Open the dashboard with `https://appbase.so/?app=DEVELOPMENT_APP_ID&environment=development`. Verify real screen/answer/paywall events in the relevant reports when instrumented. 5. Run applicable local build/type checks. If no simulator/device is available, report that clearly and leave the customer exact launch steps. Do not simulate a successful device test with HTTP events. Keep polling bounded (at most once per 5 seconds, stop after 2 minutes and explain pending delivery). Fix errors based on diagnostics; do not disable app functionality to make analytics succeed. ## 6. Leave a useful handoff Summarize changed files, tracked actions, development/production configuration, tests actually run and remaining app-specific work. Give the dashboard link. Tell the customer where the ignored setup-token file is saved and how to revoke its access without affecting tracking. Never include the token in your summary. If the repository uses AGENTS.md, append a short Appbase section without replacing existing instructions: where initialization/configuration lives, app IDs (no keys/token), development-first rule, and https://appbase.so/docs/agent-setup.md / https://appbase.so/docs/sdk.md. This gives future agent sessions a durable starting point.