Integration guide

Integrate Boostar

Intelligent App Store / Play Store review prompts, driven by configurable remote rules. Stop asking for reviews at random — ask when a user is in a good moment, defined by rules you control and evaluated on-device.

This guide takes you from the credentials Boostar gave you to a verified, firing review prompt in your app.

What you need from Boostar

Boostar provisions your app and issues your credentials — there's nothing to self-host. You should have received:

You needLooks likeNotes
SDK key bk_… (~43 chars) One per app (iOS and Android are separate apps → separate keys). Secret — treat like a password.
API base URL https://api.boostar.app The SDK's default. You only set it explicitly to override.

Don't have these yet? Contact Boostar to get your app registered and a key issued. Keys are shown once at issue time and stored only as a hash — if you lose one, Boostar revokes it and issues a new one.

Note Your first rule is configured by Boostar during onboarding. You don't need to author rules to start testing — track() the agreed trigger event and the prompt fires.

How it works (60 seconds)

  1. The SDK registers an anonymous device (anonId) and pulls down your app's rules (cached locally).
  2. Your app calls track('some_event') as users do things.
  3. On each tracked event, the SDK evaluates the cached rules on-device (works offline). If a rule matches and its cooldown has elapsed, it fires the native review prompt (SKStoreReviewController on iOS / In-App Review on Android).
  4. Events and prompt telemetry flow back to Boostar so rules can be tuned.
Anonymous-first No PII is required. identify() is optional and only links an anonymous device to your own user id.

Core concepts

ConceptWhat it is
AppOne platform build (iOS or Android). Has a bundleId, a platform, and one or more SDK keys.
SDK keyA bk_… secret sent in the x-boostar-key header. Scoped to a single app.
DeviceAn anonymous install, keyed by anonId (SDK-generated, persisted on device).
UserOptional. Your externalId linked to a device via identify().
EventA named behavioral signal (checkout_completed, app_opened, …) with optional JSON properties.
RuleRemote config: a trigger event + conditions + prompt copy. Managed by Boostar; evaluated client-side.

Rule conditions

Rules are evaluated by the SDK on-device. The conditions your rule may use:

FieldTypeMeaning
minCountnumberRequire ≥ N total occurrences of the trigger event before firing.
requireMinSessionCountnumberRequire ≥ N app sessions.
requireMinFeedbackCountnumberRequire ≥ N feedback responses (any sentiment). An engagement gate, not a sentiment gate.
propertyFiltersobjectRequire event properties to exactly match these key/values.
cooldownDaysnumberMin days between firings of this rule.
extendCooldownOnNegativeSentimentnumberIf the most recent feedback was negative and recent, delay the prompt by this many days. A timing modifier — every user still becomes eligible eventually.
Policy — review gating is not supported Sentiment captured by askFeedback() modulates timing only, never who gets asked. Suppressing the public store prompt based on a negative response is review gating and violates App Store / Play policy. The rule engine deliberately offers no such switch.

Path A — React Native / Expo SDK

Install

terminal bash
# from your app
pnpm add @boostar-sdk/sdk-react-native
# peer deps (if not already present)
pnpm add expo expo-modules-core @react-native-async-storage/async-storage

Requires Expo ≥ 52, React Native ≥ 0.74, React ≥ 18.2. The SDK includes a native module (iOS Swift / Android Kotlin) for the system review prompt, so test on a dev build / EAS build — the native prompt won't appear in Expo Go.

Wrap your app

App.tsx tsx
import { BoostarProvider } from '@boostar-sdk/sdk-react-native';

export default function App() {
  return (
    <BoostarProvider
      apiKey="bk_…"   // your SDK key (use an env var, don't hardcode for release)
      debug           // verbose logs — turn OFF for production
    >
      <YourApp />
    </BoostarProvider>
  );
}

You normally don't set apiUrl — it defaults to https://api.boostar.app. Only override it if Boostar gave you a different endpoint.

BoostarProvider:

  • calls Boostar.initialize(...) on mount,
  • auto-mounts <BoostarFeedbackHost /> (so askFeedback works — pass mountFeedbackHost={false} to place it yourself),
  • exposes isReady via the useBoostar() hook,
  • flushes the event queue on unmount.

Track events & fire prompts

CheckoutScreen.tsx tsx
import { useBoostar } from '@boostar-sdk/sdk-react-native';

function CheckoutScreen() {
  const { isReady, track, identify } = useBoostar();

  async function onPurchase() {
    // optional: link this device to your own user id
    await identify('customer_42', { plan: 'pro', signupDays: 18 });

    // track the behavioral event — may fire a review prompt if a rule matches
    await track('checkout_completed', { amount: 49.99, currency: 'USD' });
  }
}

Outside React (push handlers, services, background tasks), use the singleton directly:

pushHandler.ts ts
import Boostar from '@boostar-sdk/sdk-react-native';
await Boostar.track('subscription_renewed');

Full API surface

MethodPurpose
Boostar.initialize(config)Start the SDK. Idempotent for the same apiKey.
Boostar.identify(externalId, traits?)Link the anon device to your user id (+ optional traits).
Boostar.track(name, properties?)Record an event; evaluates rules and may fire a prompt.
Boostar.requestReview()Manually fire the native prompt, bypassing rules (still subject to OS frequency caps). Returns { requested, reason? }.
Boostar.askFeedback(options)Show a lightweight sentiment prompt (👍/👎 or 😊😐😞). Returns { dismissed, sentiment? }. Requires the feedback host mounted.
Boostar.flush()Force-push the pending event queue.
Boostar.reset()Wipe local state (anonId, counters, prompt history). Handy to re-test.
examples.ts ts
const res = await Boostar.requestReview();
// { requested: true } | { requested: false, reason: 'cooldown' | 'no-matching-rule' | ... }

const fb = await Boostar.askFeedback({
  question: 'How was checkout?',
  type: 'thumbs',          // or 'emoji'
  context: 'checkout_completed',
  cooldownDays: 7,
});
// { dismissed: false, sentiment: 'positive' } | { dismissed: true, reason: 'cooldown' | 'no-host' | ... }
Note The SDK never throws into your app — a network or storage failure degrades gracefully instead of crashing.

Path B — Raw HTTP API

Use this to smoke-test your key before touching app code, or to integrate a non-RN client. Base URL: https://api.boostar.app.

Auth Every request needs the header x-boostar-key: bk_…. Missing/invalid → 401.

POST /api/sdk/init — register a device

Call once per app start. Idempotent on (app, anonId).

init bash
curl -s -X POST https://api.boostar.app/api/sdk/init \
  -H "Content-Type: application/json" \
  -H "x-boostar-key: $BOOSTAR_KEY" \
  -d '{
    "anonId": "test-device-001",
    "osVersion": "17.5",
    "appVersion": "1.0.0",
    "locale": "en-US",
    "timezone": "America/Montevideo"
  }'
# → { "deviceId": "..." }
FieldRequiredNotes
anonId8–64 chars. Stable per install; you generate it.
osVersion appVersion≤ 32 chars.
locale2–10 chars.
timezone≤ 64 chars.

POST /api/sdk/identify — link a user

Requires a prior init for that anonId (else 404).

identify bash
curl -s -X POST https://api.boostar.app/api/sdk/identify \
  -H "Content-Type: application/json" \
  -H "x-boostar-key: $BOOSTAR_KEY" \
  -d '{
    "anonId": "test-device-001",
    "externalId": "customer_42",
    "email": "user@example.com",
    "traits": { "plan": "pro", "signupDays": 18 }
  }'
# → { "userId": "...", "deviceId": "..." }

externalId is required (1–128 chars). email must be valid if present. traits is free-form JSON.

POST /api/sdk/events — ingest events

Batch of 1–100 events. Returns 202 Accepted. Requires a prior init (else 404).

events bash
curl -s -X POST https://api.boostar.app/api/sdk/events \
  -H "Content-Type: application/json" \
  -H "x-boostar-key: $BOOSTAR_KEY" \
  -d '{
    "anonId": "test-device-001",
    "events": [
      { "name": "app_opened",         "occurredAt": "2026-06-20T12:00:00.000Z" },
      { "name": "checkout_completed", "occurredAt": "2026-06-20T12:05:00.000Z",
        "properties": { "amount": 49.99, "currency": "USD" } }
    ]
  }'
# → { "ingested": 2 }
Per-event fieldRequiredNotes
name1–80 chars.
occurredAtISO 8601 timestamp.
propertiesFree-form JSON object.

GET /api/sdk/config — fetch your rules

The fastest way to confirm your key is live and your rule exists:

config bash
curl -s https://api.boostar.app/api/sdk/config \
  -H "x-boostar-key: $BOOSTAR_KEY"
# → { "appId": "...", "rules": [ { "id", "trigger", "conditions", "prompt", "cooldownDays", "priority" }, ... ] }

Only enabled rules are returned, sorted by descending priority. Matching happens on the client — this endpoint just serves config.

Test your integration

Work through this top to bottom. Each step has an expected result so you know exactly where a problem is.

Step 1 — Confirm your key and rule (no app needed)

terminal bash
export BOOSTAR_KEY="bk_…"
curl -s https://api.boostar.app/api/sdk/config -H "x-boostar-key: $BOOSTAR_KEY"
  • ✅ Expected: JSON with your appId and at least one rule. Note the rule's trigger and conditions — that's what you'll satisfy to make the prompt fire.
  • 401 → wrong/missing key. empty rules → ask Boostar to configure your first rule.

Step 2 — Confirm ingestion end-to-end (no app needed)

Run init then events from the curl examples above with the same anonId.

  • ✅ Expected: init returns a deviceId; events returns { "ingested": N }.
  • 404 on events → you skipped init for that anonId.

Step 3 — Wire the SDK into a build

Add BoostarProvider with debug enabled, on a dev/EAS build (not Expo Go). On launch, the debug logs should show the SDK starting and an anonId: [Boostar] started with anonId <…>

  • No logs / "called before initialize" → the provider isn't mounted above the code calling Boostar.*.

Step 4 — Force a prompt (fastest visual check)

requestReview() bypasses the rule engine, so it's the quickest way to confirm the native prompt is wired:

check.ts ts
const res = await Boostar.requestReview();
console.log(res); // expect { requested: true }
  • ✅ iOS Simulator / first run: the system rating sheet appears.
Real devices / repeat runs The OS may legitimately not render the sheet even though requested: true — Apple and Google cap how often it shows (≈3×/year on iOS) and never tell the app. requested: true means your wiring is correct; the OS decides display. This is expected, not a bug.

Step 5 — Fire via a rule (the real path)

Satisfy your rule's conditions, then track() its trigger. For the typical onboarding rule (trigger: checkout_completed, needs ≥5 feedback interactions first):

fire.ts ts
// 1. satisfy requireMinFeedbackCount (call 5×, cooldownDays:0 so testing isn't throttled)
for (let i = 0; i < 5; i++) {
  await Boostar.askFeedback({ question: 'How was it?', type: 'thumbs', context: 'demo', cooldownDays: 0 });
}
// 2. fire the trigger
await Boostar.track('checkout_completed', { amount: 49.99, currency: 'USD' });
  • ✅ Expected (debug logs): firing rule <id> (trigger: checkout_completed), then the native prompt (subject to Step 4's OS caveat).
  • no prompt: <reason> in logs tells you which condition failed:
Log reasonFix
no-matching-ruleTrigger name doesn't match any rule. Check Step 1's trigger.
event-count-thresholdHaven't hit minCount for the trigger yet.
session-thresholdNeed more app sessions (requireMinSessionCount).
feedback-count-thresholdNeed more askFeedback responses first.
property-mismatchEvent properties don't satisfy propertyFilters.
cooldownDays / cooldownAlready fired recently — see Step 6.

Step 6 — Re-test cleanly

Cooldowns persist locally, so a rule won't re-fire during a session of testing. Reset between runs:

reset.ts ts
await Boostar.reset(); // clears anonId, counters, and prompt history

Step 7 — Confirm Boostar received your data

Ask your Boostar contact to confirm your test events and prompt telemetry are landing for your app (the SDK emits boostar.review_prompt_requested whenever a prompt fires). This closes the loop that on-device behavior reached the backend.

Error reference

StatusMeaning
401 UnauthorizedMissing or invalid x-boostar-key.
404 Not Foundidentify/events called before init for that anonId.
400 Bad RequestValidation failure — payloads are strictly whitelisted; unknown fields are rejected.
408 / 429 / 5xxTransient. The SDK retries automatically; raw clients should back off and retry.

Production checklist

Ready to ship?

Get your SDK key and your first rule configured.

Talk to Boostar