Expo / React Native SDK
@kilden-io/expo is the Kilden client SDK for Expo and React Native apps: event capture, screen tracking, persisted identity and feature flags. It is the mobile counterpart of the web SDK — same semantics, adapted to React Native (AsyncStorage instead of localStorage, a background flush instead of sendBeacon). For browsers use @kilden/sdk; for your backend use a server SDK.
Autocapture, session replay and plugins are web-only today; this SDK does not include them.
Install
Section titled “Install”npx expo install @kilden-io/expo expo-crypto @react-native-async-storage/async-storageBare React Native (no Expo) works too: every platform binding is a soft dependency. Without expo-crypto the SDK falls back to any global crypto.getRandomValues polyfill (e.g. react-native-get-random-values); without AsyncStorage it warns and keeps identity in memory, so ids don’t survive an app restart.
First event
Section titled “First event”import kilden from "@kilden-io/expo";
kilden.init("YOUR_WRITE_KEY");
kilden.track("order_completed", { revenue: 99.9, currency: "CLP" });kilden.screen("Checkout", { step: 2 });The default export is a singleton — call init once and import it anywhere. Calls made before init are buffered and replayed in order; flag reads before init resolve to their default. If you need isolated clients (tests, multiple projects), use createClient(writeKey, options).
The key rule
Section titled “The key rule”Use your project’s public write key (wk_…). Never ship a secret key (sk_…) in an app: anything inside a bundle can be extracted, and a leaked secret key lets anyone write verified server events into your project. The constructor rejects secret keys outright — the mirror image of the server SDKs, which reject public ones. See trust levels for the full model.
The client surface
Section titled “The client surface”| Method | What it does |
|---|---|
init(writeKey, options?) |
Initialize the singleton. See Configuration |
track(event, properties?, options?) |
Queue an event. options takes an explicit timestamp and uuid |
screen(name, properties?, options?) |
Record a screen view: a $screen event carrying $screen_name |
identify(distinctId, traits?, options?) |
Tie the anonymous user to your user id. See Identity |
alias(aliasId) |
Link an additional known id to the current identity (emits $alias) |
reset() |
Log out: fresh anonymous id, identity token and flag cache cleared |
getDistinctId() |
Current distinct id. Async — it waits for storage hydration |
setIdentityToken(token) |
Set or clear the identity verification JWT |
isFeatureEnabled(key, options?) / getFeatureFlag(key, options?) |
Feature flags, evaluated remotely. Async — see Feature flags |
flush() |
Force-send the queue |
close() |
Final flush with a 10-second deadline, then the client goes inert |
The guarantees match the web SDK: the public API never throws after construction, your data is never mutated, and every event carries a client-generated UUID v7 so retries are idempotent. Wire format, retry policy and payload behavior follow kilden-sdk-spec — the same authority the server SDKs are tested against.
Identity
Section titled “Identity”Users start anonymous: an anon_ + UUID v7 id, generated on first launch and persisted in AsyncStorage. When they log in:
identify links the anonymous history to your user id, with the same person-graph semantics as the web. On logout:
kilden.reset();reset rotates to a fresh anonymous identity and drops the identity token and the flag cache. Skip it and the next login on that device gets linked to the previous user’s anonymous trail.
Unlike the web SDK, this SDK also exposes alias(aliasId): it emits $alias, linking another known id to the current identity without changing local state. Most apps only ever need identify.
Identity verification
Section titled “Identity verification”Client events are unverified by default — anyone can extract your public write key from the app bundle. To get verified: true events (required by the messenger, respected by campaign triggers), your backend mints a short-lived JWT for the logged-in user and the SDK carries it with every batch:
kilden.init("YOUR_WRITE_KEY", { getIdentityToken: async () => { const response = await fetch("https://your-backend.example/kilden/identity", { method: "POST", headers: { Authorization: `Bearer ${sessionToken}` }, // your app's session token }); if (!response.ok) return null; const { token } = await response.json(); return token; },});The SDK refreshes the token about 60 seconds before it expires and once on a 401. The server SDKs give you the signing endpoint in three lines — kilden/laravel ships POST /kilden/identity ready-made. Full model: identity verification.
Feature flags
Section titled “Feature flags”Remote evaluation against /decide, cached for 30 seconds per identity:
if (await kilden.isFeatureEnabled("new_checkout")) { renderNewCheckout();}
const variant = await kilden.getFeatureFlag("checkout_test", { personProperties: { plan: "pro" }, // this evaluation only; bypasses the cache default: "control", // returned when Kilden cannot answer});Flag reads never throw and never retry — a late flag answer is useless, so a failed read returns your default (false if you didn’t set one). identify() and reset() invalidate the cache, so values converge to the new identity right away. The first read of each flag emits a $feature_flag_called exposure event. Targeting and bucketing semantics are in the feature flags guide.
Batching and lifecycle
Section titled “Batching and lifecycle”Events queue in memory and flush every 5 seconds, at 20 queued events, and when the app leaves the foreground (via AppState — React Native has no sendBeacon, so the background flush is the mobile replacement). Delivery retries with exponential backoff and honors Retry-After. The queue is bounded at 10,000 events; at the cap the new event is dropped, never history.
By default the queue lives in memory: events still queued when the OS kills the app are lost (React Native exposes no “app will terminate” signal — the background flush is the last reliable hook). Set persistQueue: true to write the queue through to AsyncStorage instead: undelivered events are restored and sent on the next launch. Delivery is at-least-once — Kilden dedups by event uuid, so a batch that was in flight during a kill never double-counts. Call flush() at natural checkpoints, and close() if your app has a real shutdown path — it drains the queue with a 10-second deadline and turns the client inert.
Configuration
Section titled “Configuration”All options with their defaults:
kilden.init("YOUR_WRITE_KEY", { apiHost: "https://ingest.kilden.io", flushAt: 20, // queue length that triggers a flush flushIntervalMs: 5000, // periodic flush maxQueueSize: 10000, // hard cap; at the cap the NEW event is dropped requestTimeoutMs: 10000, // per HTTP request debug: false, // verbose logging + $-prefix warnings enabled: true, // false turns the whole client into a no-op persistQueue: false, // true = the queue survives the OS killing the app (restored and delivered on the next launch)});The rest are injection points and identity plumbing:
| Option | What it does |
|---|---|
identityToken |
Initial identity JWT, if you already have one at init |
getIdentityToken |
Async token fetcher, called 60 s before expiry and on 401. See identity verification |
context |
() => Properties merged into every event as extra system properties (explicit event properties win) |
storage |
KeyValueStorage override; defaults to AsyncStorage |
transport |
Transport override; defaults to fetch |
getRandomValues |
Entropy override; defaults to expo-crypto, then the global crypto.getRandomValues |
appState |
App lifecycle source override; defaults to React Native’s AppState |
The injection points exist so the core runs anywhere — bare React Native, Expo Web, tests in Node. By default every event carries device context: $os, $os_version, $device_type, $screen_width, $screen_height, plus $lib and $lib_version.
Where to next
Section titled “Where to next”- Identifying users — what
identifydoes to the person graph. - Identity verification — the trust model behind the token.
- Feature flags — targeting, rollouts, deterministic bucketing.
- kilden-sdk-expo on GitHub — source, changelog, discussions.