@sendoracloud/sdk-react-native
v0.12.0
Published
Sendora Cloud React Native + Expo SDK — analytics, identity, push-token registration, auth. Expo Go compatible, no native modules beyond AsyncStorage.
Downloads
1,996
Maintainers
Readme
@sendoracloud/sdk-react-native
Official React Native + Expo SDK for SendoraCloud. Works in Expo Go, managed dev clients, and bare React Native apps.
📖 First time on React Native? Read the 5-minute quickstart — it covers the Hermes CSPRNG polyfill, anonymous-first auth, push token receipts, and the demo end-to-end push send.
Install
npx expo install @sendoracloud/sdk-react-native @react-native-async-storage/async-storage
npm install react-native-get-random-values@react-native-async-storage/async-storage is a required peer dependency — used to persist the anonymous device id across app restarts.
react-native-get-random-values is a required runtime polyfill — Hermes (and JavaScriptCore) doesn't expose crypto.getRandomValues reliably, and Sendora refuses to mint anonymous IDs from Math.random. Add this as the first import in your entry file (index.js or index.tsx):
// MUST be the first import in your entry file
import "react-native-get-random-values";If you forget, init() throws with a paste-ready remediation block — it never fails silently.
Quick start
import SendoraCloud from "@sendoracloud/sdk-react-native";
// App.tsx or wherever you initialise app-level services
await SendoraCloud.init({
publicKey: "pk_live_...", // from Settings → API Keys in the SendoraCloud dashboard
orgId: "YOUR_ORG_UUID",
});
// Identify the signed-in user
SendoraCloud.identify("user-123", {
email: "[email protected]",
name: "Ada Lovelace",
});
// Track a product event
SendoraCloud.track("signup.completed", { plan: "growth" });
// Track screen views (RN equivalent of page views)
SendoraCloud.screen("Pricing");
// Register the device push token (get it from expo-notifications)
import * as Notifications from "expo-notifications";
const { data } = await Notifications.getExpoPushTokenAsync();
const receipt = await SendoraCloud.registerPushToken({
token: data,
platform: Platform.OS as "ios" | "android",
// bundleId is OPTIONAL but strongly recommended for multi-env apps:
// Sendora uses it to route to the right APNs / FCM credentials so a
// dev token never receives a prod push.
bundleId: "com.yourcompany.app",
});
// Returns { tokenId, created } — log it for debugging fan-out issues.
console.log("registered:", receipt.tokenId, "new?", receipt.created);
// On logout
SendoraCloud.reset();API
| Method | Description |
| --- | --- |
| init(config) | Initialize the SDK. Call once at app startup. |
| identify(userId, traits?) | Associate a user id (+ optional traits) with the device. |
| track(event, props?) | Fire a custom product event. |
| screen(name, props?) | Track a screen view. |
| registerPushToken(reg) | Register an APNs or FCM push token. |
| reset() | Clear identity + rotate anonymous id. Call on logout. |
| getAnonymousId() | Stable device id (persisted across restarts). |
| getUserId() | Currently identified user id, or null. |
Config
{
publicKey: string; // Required. Must start with "pk_".
orgId?: string; // Your workspace UUID.
apiUrl?: string; // Defaults to https://api.sendoracloud.com
environment?: "prod" | "staging" | "dev"; // Routing hint.
projectId?: string; // Optional project scoping.
consentedByDefault?: boolean; // Defaults to true.
/**
* Auto-collect lifecycle events. Default true. Object form:
* { appOpen?, sessionStart?, appBackground? } for granular control.
* - appOpen — `app.opened` once per init.
* - sessionStart — `session.start` once per session (idle >sessionIdleMs).
* - appBackground — AppState 'change' fires `app.foregrounded` /
* `app.backgrounded`.
* Note: `screen.viewed` is NOT auto — call `SendoraCloud.screen(name)`
* from your navigation listener (RN has no canonical router).
*/
autoTrack?: boolean | { appOpen?: boolean; sessionStart?: boolean; appBackground?: boolean };
sessionIdleMs?: number; // Idle threshold for session expiry. Default 30 min.
}Stability
This SDK is on the 0.10.x line. Patch bumps (0.10.x → 0.10.y) are backwards-compatible; minor bumps may include breaking changes — always read the CHANGELOG before upgrading. The next major (1.0) is targeted once the HttpOnly-cookie SSR auth + auto-trait extraction features have soaked in production for two consecutive months without a schema change. Once 1.0 ships, semver is strict.
JWT verification (custom backend)
If your backend verifies Sendora-issued access tokens directly (rather than calling Sendora APIs that re-verify on every hit), use the OIDC-standard JWKS auto-discovery pattern — never hardcode the JWKS URL:
import { createRemoteJWKSet, jwtVerify, decodeJwt } from "jose";
const JWKS_BY_ISS = new Map<string, ReturnType<typeof createRemoteJWKSet>>();
function getJwks(iss: string) {
if (!JWKS_BY_ISS.has(iss)) {
JWKS_BY_ISS.set(iss, createRemoteJWKSet(new URL(`${iss}/.well-known/jwks.json`)));
}
return JWKS_BY_ISS.get(iss)!;
}
const claims = decodeJwt(token);
const { payload } = await jwtVerify(token, getJwks(claims.iss as string));The iss claim is a per-org Sendora URL (e.g. https://api.sendoracloud.com/api/v1/auth-service/<orgId>); appending /.well-known/jwks.json resolves on the same host. This means a customer can rotate signing keys without your code redeploying. Don't copy a hardcoded JWKS URL from a tutorial — it'll break in 12 months when an org rotates.
Security
- Secret keys refused on the client. The SDK only accepts
pk_(live|test)_<alphanumerics>keys —sk_*(any case) or malformed values throw at init. Secret keys must stay on your backend. - HTTPS enforced outside local dev. The SDK rejects non-
https:apiUrlvalues unless the hostname islocalhost/127.0.0.1andenvironmentis not"prod". - AsyncStorage backs the anonymous id so a single user has one identity across app restarts, but never across devices.
reset()awaits the storage writes and clears every SDK-owned key. - Use opaque userIds, not emails. Whatever you pass to
identify(userId)persists to on-device AsyncStorage in plaintext. Use your internal user UUID, not the user's email. Emails can go intraits: { email }— traits aren't persisted to disk, only sent with the identify event. - Push token + metadata capped at 4 KB each to prevent a buggy or hostile host app from flooding the backend.
License
MIT
