Scout SDK
v0.1.1Lightweight mobile signal-enrichment SDK for Brigada — install referrer, IDFA/GAID, and Universal Link deep-link resolution, forwarded as attributed install events.
npm install @onestepsocial/scout
Lightweight mobile signal-enrichment SDK for Brigada. Collects Android Install Referrer, IDFA/GAID (with app-triggered consent), and Universal Link deep-link resolution, and forwards them to Brigada as MOBILE_INSTALL events.
Scout is not a general-purpose analytics SDK — no custom event tracking, no in-app funnels. See docs/superpowers/specs/2026-07-01-scout-sdk-design.md for the full design rationale and out-of-scope list.
Install
pnpm add @onestepsocial/scout
Usage
import Scout from '@onestepsocial/scout';
// Once, at app startup.
Scout.init({ sdkKey: 'scout_live_xxx', merchantId: 'your_merchant_id' });
// After your app receives the host OS's ATT (iOS) or AD_ID (Android)
// permission result — Scout never triggers this prompt itself.
Scout.reportConsent('granted'); // or 'denied' | 'not_determined'
// Once per install/launch. Safe to call every launch — internally idempotent.
await Scout.resolveInstall();
// Optional — if your app handles its own Universal Link routing and wants
// Scout to attach the matched link's context to the install resolution.
Scout.handleUniversalLink(url);
API Reference
Scout.init(config: ScoutConfig): void
Call once at app startup, before any other Scout method.
| Field | Type | Required | Description |
|---|---|---|---|
sdkKey | string | yes | Your scout_live_... key. Scoped to MOBILE_INSTALL only — it cannot read data or send other event types. |
merchantId | string | yes | Your Brigada merchant ID. |
apiUrl | string | no | Override the Brigada ingest endpoint. Defaults to https://api.brigada.onestepsocial.com/v1. |
debug | boolean | no | Logs queue/network activity to the console. Defaults to false. |
Scout.reportConsent(status: ConsentStatus): void
Tell Scout the result of your app's own ATT (iOS) or AD_ID (Android) permission prompt. Scout never shows this prompt itself — status is one of 'granted' | 'denied' | 'not_determined'. Call this as soon as you have a result, ideally before resolveInstall().
Scout.resolveInstall(): Promise<void>
Captures install referrer / IDFA / GAID / matched Universal Link and reports a MOBILE_INSTALL event to Brigada. Safe to call on every app launch — internally idempotent per session, and retries automatically (bounded, exponential backoff) if the network call fails. Resolves once the event is queued, not once it's delivered — delivery happens in the background via the offline event queue.
Scout.handleUniversalLink(url: string): void
Optional. If your app handles its own NSUserActivity/Universal Link routing, forward the URL here so Scout can attach the matched link's context to the next resolveInstall() call.
Types
type ConsentStatus = 'granted' | 'denied' | 'not_determined';
interface ScoutConfig {
sdkKey: string;
merchantId: string;
apiUrl?: string;
debug?: boolean;
}
Required native setup
iOS
- Add
NSUserTrackingUsageDescriptiontoInfo.plistif your app uses ATT. - Add your Universal Link domain to
Associated Domainsand forwardNSUserActivitycontinuation toScout.handleUniversalLink(url)in yourAppDelegate/SceneDelegate. - Run
pod installafter installing — theOnestepsocialScoutpod (including itsPrivacyInfo.xcprivacy) is picked up automatically via CocoaPods autolinking.
Android
- No manual manifest edits required — the
com.google.android.gms.permission.AD_IDpermission is declared in Scout's own manifest and merges automatically. Remove it via atools:node="remove"manifest-merge rule if your app doesn't use advertising ID anywhere. - Register the native module in
MainApplication:
override fun getPackages(): List<ReactPackage> =
PackageList(this).packages + ScoutPackage()
Development
pnpm install
pnpm test # Jest — TS public API + offline queue
pnpm build # tsup — outputs dist/ (cjs + esm + types)
pnpm lint
Native module tests: cd android-test-harness && ./gradlew :scout-android:test (Robolectric/JUnit — android-test-harness/ is a standalone Gradle project that includes android/ as a module, since there's no full RN host app to build against yet); iOS tests: cd ios && swift test. Both suites run in CI (.github/workflows/test-scout.yml) on every push/PR touching apps/scout/**.
See DEPLOY.md for publishing.
License
MIT © OneStepSocial
Version History
0.1.1
Patch Changes
-
1fe0b12: Fix two failure-recovery bugs in install resolution:
resolveInstall()now resets its internal guard on any failure (AsyncStorage read/write, not just the native call), so a transient storage error no longer permanently blocks retry for the rest of the app session.- iOS:
resolveInstallno longer silently drops the JS promise if the native module deallocates mid-resolution — it now rejects withscout_deallocatedso callers can catch and retry instead of hanging forever.
All notable changes to @onestepsocial/scout are documented here. Format follows Keep a Changelog; versioning follows SemVer.
[0.1.0] - Unreleased
Added
- Initial v1: React Native module.
Scout.init(),Scout.reportConsent(),Scout.resolveInstall(),Scout.handleUniversalLink().- Android: Install Referrer capture, GAID (consent-gated).
- iOS: Universal Link deep-link resolution, IDFA (consent-gated).
- Offline event queue with bounded FIFO cache and retry/backoff.
- CI runs the full test matrix (JS/TS, Android Robolectric/JUnit, iOS XCTest/swift-testing) on every push/PR.
Fixed
- Android: pinned
com.facebook.react:react-androidto match the JS layer'sreact-nativeversion instead of floating to latest, which had silently drifted to a version requiring a higherminSdkVersionthan declared.