Sahha React Native Integration Guide
This guide covers a full Sahha integration for React Native (iOS + Android): install → native setup → configure → authenticate profiles securely → request user permission via sensors → read Scores, Biomarkers, Stats, and Samples.
What you need before you start
1) A Sahha account + project
You’ll need:
- A Sahha account
- A Sahha project created in the Dashboard
- Access to your API keys / tokens (for sandbox + production)
Dashboard: https://app.sahha.ai/
Docs overview: https://docs.sahha.ai/
2) A plan for External IDs (critical)
Every Sahha profile is identified by an externalId you provide.
Rules of thumb
- Must be unique per user
- Keep it stable across device changes (same user logs in on a new phone → use same
externalId) - Do not use PII (email/username). Prefer an anonymous UUID.
User profile doc: https://docs.sahha.ai/docs/connect/sdk/user-profiles
3) A secure authentication architecture (recommended)
You have two ways to authenticate a profile:
Recommended (production)
- Your server calls Sahha
profile/registerusing an accountToken - The server returns
profileToken+refreshTokento the app - The app calls
Sahha.authenticateToken(profileToken, refreshToken, ...)
Not recommended (prototype only)
- The app calls
Sahha.authenticate(appId, appSecret, externalId, ...) - This requires having
appId/appSecretavailable to the client (don’t do this in production)
User profile doc (SDK + API approach): https://docs.sahha.ai/docs/connect/sdk/user-profiles
4) Platform tooling
iOS
- Xcode + CocoaPods
- Apple Health (HealthKit) capability setup
Android
- Android build toolchain
- Health Connect permissions and availability considerations
Setup docs:
- iOS setup: https://docs.sahha.ai/docs/connect/sdk/setup/ios
- Android setup: https://docs.sahha.ai/docs/connect/sdk/setup/android
5) Permission UX decision
Sahha sensor permission prompts must be user-initiated.
Important
- You should not call
enableSensorson app start / inuseEffect. - You should call it from a user action (a button press), otherwise it’s a bad UX and can risk store rejection.
Sensors doc: https://docs.sahha.ai/docs/connect/sdk/sensors
High-level integration flow
- Install the Sahha RN SDK
- Native setup (HealthKit / Health Connect)
- Configure Sahha at app launch
- Authenticate the user profile
- Check sensor status → request permission via button
- (Optional) Post demographics to improve accuracy
- Query Scores / Biomarkers (and optionally Stats / Samples)
- Production checklist (environment switch + store submission notes)
Step 1 — Install the React Native SDK
npm i sahha-react-native
Docs: https://docs.sahha.ai/docs/connect/sdk/install/react-native
Repo: https://github.com/sahha-ai/sahha-react-native
Step 2 — Native setup
iOS (Apple Health / HealthKit)
Follow: https://docs.sahha.ai/docs/connect/sdk/setup/ios
Typical requirements:
- Enable HealthKit
- Enable Background Delivery (so passive health data can be collected)
- Add usage strings in
Info.plist:Privacy - Health Share Usage Description- (Often needed)
Privacy - Health Update Usage Description
Example copy:
“We use Apple Health data to generate personalized insights and experiences.”
Android (Health Connect)
Follow: https://docs.sahha.ai/docs/connect/sdk/setup/android
You must declare Health Connect permissions in:
android/app/src/main/AndroidManifest.xml
Example (steps + sleep):
<uses-permission android:name="android.permission.health.READ_STEPS" />
<uses-permission android:name="android.permission.health.READ_SLEEP" />
Health Connect guidelines + 30-day read restriction: https://docs.sahha.ai/docs/connect/sdk/user-permission/android-health-connect
Using Expo?
You can’t use the Sahha native SDK inside Expo Go. You’ll need a development build.
Expo install doc: https://docs.sahha.ai/docs/connect/sdk/install/expo
Step 3 — Configure Sahha at app launch
Sahha must be configured immediately on app launch.
Create src/sahha/configure.ts:
import { Sahha, SahhaEnvironment } from 'sahha-react-native';
export function configureSahha(): Promise<boolean> {
return new Promise((resolve, reject) => {
const settings = {
environment: SahhaEnvironment.sandbox, // use production only for public release
};
Sahha.configure(settings, (error: string, success: boolean) => {
if (error) return reject(new Error(error));
resolve(success);
});
});
}
Call it once near the root of your app (e.g., in your App bootstrap).
Environment guidance: https://docs.sahha.ai/docs/connect/sdk/configure
Android notifications: Sahha supports notification customization on Android (including React Native Android).
See: https://docs.sahha.ai/docs/connect/sdk/configure
Step 4 — Authenticate the user profile
Option A (Recommended): Authenticate via your server, then authenticateToken
Server → Sahha
- Endpoint:
POST /oauth/profile/register - Header:
Authorization: Account <YOUR_ACCOUNT_TOKEN> - Body:
{ "externalId": "..." } - Response includes
profileToken+refreshToken
Docs: https://docs.sahha.ai/docs/connect/sdk/user-profiles
Example: Node/Express “register profile” endpoint
import express from 'express';
const app = express();
app.use(express.json());
app.post('/api/sahha/register-profile', async (req, res) => {
const { externalId } = req.body;
if (!externalId || typeof externalId !== 'string') {
return res.status(400).json({ error: 'externalId is required' });
}
// Choose base URL depending on environment.
// The docs link to sandbox API docs; confirm base URLs in your Sahha project/API docs.
const baseUrl = process.env.SAHHA_BASE_URL ?? 'https://sandbox-api.sahha.ai';
const resp = await fetch(`${baseUrl}/oauth/profile/register`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Account ${process.env.SAHHA_ACCOUNT_TOKEN}`,
},
body: JSON.stringify({ externalId }),
});
if (!resp.ok) {
const text = await resp.text();
return res.status(resp.status).send(text);
}
const data = await resp.json();
// Expect: { profileToken, refreshToken, expiresIn, tokenType }
return res.json({
profileToken: data.profileToken,
refreshToken: data.refreshToken,
expiresIn: data.expiresIn,
});
});
App → Sahha SDK using returned tokens:
import { Sahha } from 'sahha-react-native';
export function authenticateWithTokens(profileToken: string, refreshToken: string): Promise<boolean> {
return new Promise((resolve, reject) => {
Sahha.authenticateToken(profileToken, refreshToken, (error: string, success: boolean) => {
if (error) return reject(new Error(error));
resolve(success);
});
});
}
Option B (Prototype only): Authenticate directly with appId/appSecret
import { Sahha } from 'sahha-react-native';
Sahha.authenticate(APP_ID, APP_SECRET, EXTERNAL_ID, (error: string, success: boolean) => {
if (error) console.error(error);
console.log('authenticated?', success);
});
Do not embed APP_ID / APP_SECRET in client code for production.
Docs: https://docs.sahha.ai/docs/connect/sdk/user-profiles
Step 5 — Check sensor status and request permission (UX-critical)
Key rule: call enableSensors from a button press
Sahha shows system permission UI, so you should only call it in response to user intent.
Sensors doc: https://docs.sahha.ai/docs/connect/sdk/sensors
Recommended pattern: a dedicated “Connect Health Data” screen
import React, { useMemo, useState } from 'react';
import { View, Text, Button } from 'react-native';
import {
Sahha,
SahhaSensor,
SahhaSensorStatus,
} from 'sahha-react-native';
export function ConnectHealthScreen() {
const sensors = useMemo(
() => [SahhaSensor.steps, SahhaSensor.sleep],
[]
);
const [status, setStatus] = useState<SahhaSensorStatus | null>(null);
const [error, setError] = useState<string | null>(null);
const refreshStatus = () => {
Sahha.getSensorStatus(sensors, (err: string, value: SahhaSensorStatus) => {
if (err) return setError(err);
setStatus(value);
});
};
const requestPermission = () => {
setError(null);
Sahha.enableSensors(sensors, (err: string, value: SahhaSensorStatus) => {
if (err) return setError(err);
setStatus(value);
});
};
const openSettings = () => {
Sahha.openAppSettings();
};
return (
<View style={{ padding: 16, gap: 12 }}>
<Text style={{ fontSize: 18, fontWeight: '600' }}>Connect health data</Text>
<Button title="Check status" onPress={refreshStatus} />
<Button title="Enable sensors" onPress={requestPermission} />
{status === SahhaSensorStatus.enabled && (
<Text>✅ Sensors enabled</Text>
)}
{status === SahhaSensorStatus.pending && (
<Text>
Permission not granted yet. Tap “Enable sensors” to continue.
</Text>
)}
{status === SahhaSensorStatus.disabled && (
<>
<Text>
Permission was previously denied or disabled. Open settings to enable.
</Text>
<Button title="Open settings" onPress={openSettings} />
</>
)}
{status === SahhaSensorStatus.unavailable && (
<Text>
Sensors unavailable on this device (or Health data source not supported).
</Text>
)}
{error && <Text style={{ color: 'red' }}>{error}</Text>}
</View>
);
}
iOS sleep sensor (important)
For iOS, the user must enable Sleep in the Health app before enabling the sleep sensor.
Sensors doc: https://docs.sahha.ai/docs/connect/sdk/sensors
Practical UX tip:
- If status is
pending, show a “Set up Sleep in Apple Health” explainer screen first. - Then let the user press the “Enable sensors” button.
iOS permission changes can terminate your app
If the user enables/disables permissions from device settings while your app is in the background, iOS can force terminate your app.
Sensors doc: https://docs.sahha.ai/docs/connect/sdk/sensors
Plan for:
- Relaunch handling
- Resuming onboarding after restart
Step 6 — (Optional) Post demographics to improve accuracy
Demographics are optional but can improve analysis accuracy.
Docs: https://docs.sahha.ai/docs/connect/sdk/demographics
Basic constraints (per docs):
age: integer 1–99gender:"male" | "female" | "gender diverse"country/birthCountry: ISO-2 codes (e.g."us","au")birthDate:"YYYY-MM-DD"
import { Sahha } from 'sahha-react-native';
const demographic = {
age: 28,
gender: 'male',
country: 'nz',
birthDate: '1997-01-20',
};
Sahha.postDemographic(demographic, (error: string, success: boolean) => {
if (error) console.error(error);
console.log('posted demographic?', success);
});
Step 7 — Fetch Sahha outputs
Scores
Scores are returned as a JSON string (JSON array). You’ll typically query a time range (e.g., last 7 days) and parse the result.
import { Sahha, SahhaScoreType } from 'sahha-react-native';
const start = new Date();
const end = new Date();
start.setDate(start.getDate() - 7);
const scoreTypes = [
SahhaScoreType.activity,
SahhaScoreType.sleep,
SahhaScoreType.wellbeing,
];
Sahha.getScores(scoreTypes, start.getTime(), end.getTime(), (error: string, value: string) => {
if (error) return console.error(error);
const scores = JSON.parse(value); // array
console.log(scores);
});
Docs: https://docs.sahha.ai/docs/connect/sdk/scores
Biomarkers
Biomarkers also return a JSON array string.
import {
Sahha,
SahhaBiomarkerCategory,
SahhaBiomarkerType,
} from 'sahha-react-native';
const start = new Date();
const end = new Date();
start.setDate(start.getDate() - 7);
const categories = [
SahhaBiomarkerCategory.activity,
SahhaBiomarkerCategory.sleep,
SahhaBiomarkerCategory.vitals,
];
const types = [
SahhaBiomarkerType.steps,
SahhaBiomarkerType.sleep_in_bed_duration,
SahhaBiomarkerType.heart_rate_resting,
];
Sahha.getBiomarkers(categories, types, start.getTime(), end.getTime(), (error: string, value: string) => {
if (error) return console.error(error);
const biomarkers = JSON.parse(value);
console.log(biomarkers);
});
Docs: https://docs.sahha.ai/docs/connect/sdk/biomarkers
Stats (aggregated sensor data)
import { Sahha, SahhaSensor } from 'sahha-react-native';
const start = new Date();
const end = new Date();
start.setDate(start.getDate() - 7);
Sahha.getStats(SahhaSensor.steps, start.getTime(), end.getTime(), (error: string, value: string) => {
if (error) return console.error(error);
console.log(JSON.parse(value));
});
Docs: https://docs.sahha.ai/docs/connect/sdk/stats
Samples (raw/realtime sensor samples)
import { Sahha, SahhaSensor } from 'sahha-react-native';
const start = new Date();
const end = new Date();
start.setDate(start.getDate() - 7);
Sahha.getSamples(SahhaSensor.steps, start.getTime(), end.getTime(), (error: string, value: string) => {
if (error) return console.error(error);
console.log(JSON.parse(value));
});
Docs: https://docs.sahha.ai/docs/connect/sdk/samples
Step 8 — Logging out / clearing a profile
import { Sahha } from 'sahha-react-native';
Sahha.deauthenticate((error: string, success: boolean) => {
if (error) console.error(error);
console.log('deauthenticated?', success);
});
Recommended production structure
A small “Sahha service” wrapper
Avoid sprinkling callbacks throughout UI components. Wrap SDK calls into promise-based functions.
import {
Sahha,
SahhaEnvironment,
SahhaSensor,
SahhaSensorStatus,
SahhaScoreType,
} from 'sahha-react-native';
export const SahhaService = {
configure: () =>
new Promise<boolean>((resolve, reject) => {
Sahha.configure({ environment: SahhaEnvironment.sandbox }, (err, ok) => {
if (err) return reject(new Error(err));
resolve(ok);
});
}),
isAuthenticated: () =>
new Promise<boolean>((resolve, reject) => {
Sahha.isAuthenticated((err, ok) => {
if (err) return reject(new Error(err));
resolve(ok);
});
}),
authenticateToken: (profileToken: string, refreshToken: string) =>
new Promise<boolean>((resolve, reject) => {
Sahha.authenticateToken(profileToken, refreshToken, (err, ok) => {
if (err) return reject(new Error(err));
resolve(ok);
});
}),
getSensorStatus: (sensors: SahhaSensor[]) =>
new Promise<SahhaSensorStatus>((resolve, reject) => {
Sahha.getSensorStatus(sensors, (err, status) => {
if (err) return reject(new Error(err));
resolve(status);
});
}),
enableSensors: (sensors: SahhaSensor[]) =>
new Promise<SahhaSensorStatus>((resolve, reject) => {
Sahha.enableSensors(sensors, (err, status) => {
if (err) return reject(new Error(err));
resolve(status);
});
}),
getScores: (types: SahhaScoreType[], start: number, end: number) =>
new Promise<any[]>((resolve, reject) => {
Sahha.getScores(types, start, end, (err, json) => {
if (err) return reject(new Error(err));
resolve(JSON.parse(json));
});
}),
};
Troubleshooting checklist
“Nothing is coming through”
- Confirm
Sahha.configure(...)runs successfully on launch - Confirm profile is authenticated:
Sahha.isAuthenticated(...) - Confirm sensor status is
enabled - Remember: some outputs are computed on a daily cadence, so you may not see meaningful scores instantly
iOS HealthKit permission issues
- Check HealthKit capability is enabled
- Ensure
Info.plistcontains Health usage strings - Confirm your permission UX calls
enableSensorsonly via a user action
Android Health Connect issues
- Verify device compatibility + Health Connect availability
- Confirm the permissions in the manifest match the sensors you request
- Remember the 30-day read restriction relative to the first successful permission grant
- Uninstall/reinstall resets the start date
Android guidelines: https://docs.sahha.ai/docs/connect/sdk/user-permission/android-health-connect
Production launch checklist
1) Switch environment to production
When you release publicly:
- Set
environment: SahhaEnvironment.production
Config docs: https://docs.sahha.ai/docs/connect/sdk/configure
2) Use the correct API base URL for production
If you use the server-based token flow, ensure your server calls the production API base URL when your app uses production environment.
User profiles doc: https://docs.sahha.ai/docs/connect/sdk/user-profiles
3) Never ship secrets in the client
Do not embed:
- account tokens
- app secrets
- anything that can be used to authenticate arbitrary profiles
4) Store submission: include scientific references in-app
Sahha recommends exposing scientific references when presenting health scores for compliance and transparency.
App submission doc: https://docs.sahha.ai/docs/connect/sdk/app-store-submission
Example links (place these inside your app’s “About scores” UI):
- Activity Score reference: https://docs.sahha.ai/docs/connect/sdk/app-store-submission/scientific-references/score-activity
(Use the App Submission page to locate the latest Sleep Score reference link as well.)