Fictura
Welcome

SDK Quickstart

SDK 0.8.0

Install one package and call Growth.init. Events, error tracking, entitlements and experiment assignment all start on that one call — paywalls are one of the things you can do next.

Install.

One npm package, plus the two Expo modules the SDK uses for a stable device id.

Initialize.

Call Growth.init() once, early — before your first screen renders. apiBase is your Fictura host and apiKey is the SDK app key from Dashboard → Setup.

Wrap your app.

GrowthProvider mounts screen tracking, tap capture, and the cancel-survey modal. Screen and tap tracking run when the event-tracking tool is on; the survey modal when the cancel-survey tool is on.

Install
npm install @fictura/sdk
npx expo install expo-secure-store expo-application
App.tsx
import { Growth, GrowthProvider } from "@fictura/sdk";

Growth.init({
  apiBase: process.env.EXPO_PUBLIC_GROWTH_API_BASE!,
  apiKey: process.env.EXPO_PUBLIC_GROWTH_API_KEY!,   // gk_… from Dashboard → Setup
  getAccountId: async () => auth.currentUserId ?? null,
});

export default function App() {
  return <GrowthProvider>{/* your app */}</GrowthProvider>;
}

That's the integration

Four things are now running. None of them needs another line of setup, and none of them needs an app release to change afterwards.

What you getWhere it goes
Eventsfirst_open, app_open, screen_viewed, element_tapped and friends, captured automaticallyEvent dictionary
ErrorsCrashes, unhandled rejections, and anything you already console.errorError tracking
EntitlementsGrowth.isSubscribed() — one answer, whether they paid through a store or the webClient-side gating
ExperimentsVariant assignment, decided server-side and delivered with your configA/B experiments

Everything else is one line, placed by hand

What's automatic above is purely structural — screens, taps. The SDK has no way to know that a tap means "user finished onboarding" or "generation succeeded"; that's your business logic, and only you know where it happens in your code. So every milestone that actually matters to your funnel is a manual call, placed once, at the moment it happens:

Anywhere in your app
import { Growth, logEvent } from "@fictura/sdk";

// name, status ("success" | "failure" | "info"), and any properties you want on it.
logEvent("generation_success", "success", { model: "large" });

// Something that went wrong on a path you care about.
try { await generate(); } catch (e) { Growth.captureError(e); }

This isn't "add logEvent everywhere" — it's one call per milestone in your funnel, not one per button. A typical app has 5–15 of these total: signup, onboarding done, your value moment, purchase. Place each one the first time you build that flow, and the event name you choose here is also what you'll type as an experiment's primary metric later — same string, same event.

Everything is server-driven

The SDK fetches config at launch and on every foreground, cached on device. Which paywall shows, which events are captured, which experiment a user is in, and whether error capture is on at all — each is decided in the dashboard and reaches installed apps on their next refresh. See SDK keys & config refresh.

Rendering a paywall

When you're ready to sell, GrowthPaywall renders whatever is live for its placement — a native template, a block design, or a served HTML design — and falls back to your fallback node when nothing is configured.

Paywall.tsx
import { GrowthPaywall } from "@fictura/sdk";

<GrowthPaywall
  placement="default"
  onClose={() => router.back()}
  onPurchaseComplete={(packageId) => unlock(packageId)}
/>;

To sell anything, hand the SDK a purchases adapter wrapping your store SDK (most apps wrap react-native-purchases) — the SDK never imports it directly. Without an adapter, paywalls render with no packages and nothing can be purchased.

On this page