Updated today Guides

Integrating Sahha into a React Native App

A step-by-step guide to installing, configuring, authenticating, and enabling Sahha sensors in a React Native app (including iOS/Android setup, permissions, and production readiness).

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

You have two ways to authenticate a profile:

Recommended (production)

  • Your server calls Sahha profile/register using an accountToken
  • The server returns profileToken + refreshToken to 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/appSecret available 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:

5) Permission UX decision

Sahha sensor permission prompts must be user-initiated.

Important

  • You should not call enableSensors on app start / in useEffect.
  • 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

  1. Install the Sahha RN SDK
  2. Native setup (HealthKit / Health Connect)
  3. Configure Sahha at app launch
  4. Authenticate the user profile
  5. Check sensor status → request permission via button
  6. (Optional) Post demographics to improve accuracy
  7. Query Scores / Biomarkers (and optionally Stats / Samples)
  8. 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

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


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–99
  • gender: "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);
});

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.plist contains Health usage strings
  • Confirm your permission UX calls enableSensors only 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):

(Use the App Submission page to locate the latest Sleep Score reference link as well.)