Skip to main content

What this SDK does

Synheart Core connects an Android app to the native Synheart Runtime. It:
  • collects user-consented wearable, phone, and behavior signals;
  • sends those signals to the on-device runtime;
  • exposes Human State Interface (HSI) 1.3 output as Kotlin Flows;
  • optionally stores, synchronizes, and uploads derived state.
The Kotlin library handles platform concerns only: Health Connect and BLE collection, Android Keystore, EncryptedSharedPreferences, Flow streams, and Jetpack integration. Human-state computation, storage, device authentication, and sync run in the separately installed native runtime.
HSI is the public output. Most applications should use Synheart.onStateUpdate for typed values or Synheart.onHSIUpdate for the canonical JSON. Internal HSV types do not cross the public SDK boundary.

Requirements

The library module declares minSdk 24, but it depends on ai.synheart:syni, whose manifest declares 26. A library never runs the full manifest merge, so the conflict appears only when an application builds against it. Set your app to 26.

Installation

1. Add the dependency

2. Install the native runtime

The library alone loads no runtime and produces no HSI. Install and authenticate the CLI once, then provision the runtime from your application root:
Commit the generated synheart.lock, which pins each artifact by SHA-256. Other developers and CI restore the pinned artifacts with synheart sync. The CLI writes the binaries to <app-module>/synheart/vendor/runtime/android/jniLibs/<abi>/. AGP reads only src/main/jniLibs, so point your module at the vendored path:
Without the jniLibs.srcDirs entry the APK builds cleanly and ships no runtime. Every FFI call then degrades to “native library not loaded” with no build-time error.

3. Declare permissions

Declare only the permissions required by the sources you enable:
Health Connect also requires package queries, a permission-rationale intent filter, and a permission-usage activity alias. Copy the applicable declarations from the example manifest.

4. Verify before writing integration code

isRuntimeAvailable == false means the library was not bundled. A non-empty missingSymbols means the runtime loaded but predates this SDK release, so the features behind those symbols return null or false rather than throwing.

Start here

The minimum integration has five steps:
  1. Initialize the SDK.
  2. Ask the user for consent.
  3. Subscribe to HSI output.
  4. Start a collection session.
  5. Stop the session and release resources.
allowUnsignedCapabilities = true bypasses production capability verification. Never enable it in a release build.

Initialization

SynheartConfig.validate() requires a non-empty appId, a non-empty subjectId, a subjectId without |, and PrivacyConfig(allowResearch = true) when using research mode. It throws SynheartCoreError before the runtime is touched. subjectId is the identity everything attaches to — HSI uploads, baselines, consent tokens, device identity. A value that changes per launch looks like a new person every time and baselines never mature. Passing userId to initialize() does not populate it; set it on the config. initialize() is idempotent: a second call returns without throwing. Guard with Synheart.isInitialized if you need to know. It does not start collection — call startSession() once consent is ready, or pass autoStart = true when consent is already complete.

Modes

When the signed-in identity changes, keep the runtime identity aligned without reinitializing:

Running without cloud credentials

The SDK is usable with no platform account. Omit CloudConfig and DeviceAuthConfig, and everything on-device works: collection, HSI computation, consent, local storage, and baselines. Nothing leaves the device and no attestation is attempted. HSI upload, cross-device sync, research-study enrolment, and server-issued capability tokens are unavailable in this configuration. Consent and capability are different:
  • Capability says what the application is authorized to use.
  • Consent says what the user allows.
Channels are named with camelCase wire strings: biosignals, behavior, phoneContext (motion is a long-standing alias), cloudUpload, vendorSync, research, syni, focusEstimation, and emotionEstimation. The runtime keys the same channels in snake_case; the SDK translates.
Or install your own consent screen and let the SDK apply the profile the user picks — it ships none of its own:
hasConsent() reports whether a channel is enforceable right now, not what the user chose. Once a cloud consent client is configured, the runtime denies every channel until the consent service issues a token. Gate uploads with hasConsent(); render UI from consentEffectiveStateTyped().
A missing default consent profile on the platform app is the most common cause of “consent is on but nothing uploads”. See Consent System for profiles and JWT lifecycle.

Feature activation and sessions

A feature collects data only when all four of these hold:
Miss one and the feature stays silent — there is no error, because none of these is a failure. Synheart.isFeatureOperational(feature) collapses all four into one answer; check it first when data is not arriving. Declaring wearConfig, phoneConfig, or behaviorConfig activates that feature. Features may also be activated explicitly:
Activation is intersected with deviceRole.supportedFeatures, so a DeviceRole.WATCH build cannot activate behavior or phone context even if the config declares them. A host that declares none of the three module configs collects nothing.
Start and stop the full lifecycle with startSession() / stopSession(), or control individual collectors:
Module-level methods still enforce consent and capability gates.

Reading HSI

Typed state

HSIState carries subjectId, timestampMs, hsi, modalities, tiers, rawJson, and parseError. The axes are the five physiological and fused ones (focus, arousal, capacity, sleep, stress) plus three digital ones (focusQuality, interruptionPressure, interactionMode). Axis values are null when there is not enough input. Missing means unknown; it does not mean zero. On a phone with no biosignal source the canonical axes arrive at confidence: 0 — expected, not a fault. Use state.modalities to tell “nothing was collected” from “signal arrived, just not physiological”, and HSIAxes.hasDigital to check whether the window carried a digital axis. Synheart.currentHSIState reuses the same parse, so it is cheap to read repeatedly.

Canonical JSON

Synheart.currentState and HSIState.rawJson hold the same JSON. See HSI in Synheart Core for mapping details.

Raw source streams

Raw streams are consent-gated. Collect them in a lifecycle-aware scope.

Behavior capture

Android has no view-tree hook, so the SDK ships no gesture detector: nothing is observed unless the host forwards its touches.
recordTouchEvent derives taps and scrolls, including the part that is easy to get wrong: recording per ACTION_MOVE floods the aggregator. Interaction alone produces the HSI digital axes, so behavior works on hardware with no wearable.

Wearable permissions

Health Connect gates reads behind a runtime prompt, so a manifest declaration alone leaves the source polling an empty store — every sample arrives carrying nothing, which is indistinguishable from a paired-but-silent wearable.

Watch sessions

Added in 0.2.0. The phone drives a session on a paired Wear OS watch over the Wearable Data Layer; the watch owns the session and computes the metrics. Watch heart rate is forwarded into the runtime, so HSI has physiology on a phone with no sensor of its own.
Session types come from ai.synheart:synheart-session, which this SDK re-exports. Subscribe to watchSessionEvents before sending the start command so the watch’s first event cannot arrive before anything is listening.
A WatchStatus(supported = false) means the device has no watch transport at all; a null status means the SDK was never initialized. The companion app must share the phone app’s applicationId and signature — the Data Layer delivers only between apps that match on both.
For the watch-to-phone artifact contract, see Edge.

Production device authentication

The runtime registers a hardware-backed ECDSA identity in the Android Keystore and signs supported requests. No shared secret is required.
DeviceAuthConfig.packageName is the installed bundle id, which Play Integrity verifies against. It is not SynheartConfig.appId, the platform-issued application identifier. Passing the latter fails attestation.
Registration is triggered by cloud-upload consent, not by initialize():
Use reregisterDeviceAuth() as an explicit repair action when the server has lost or revoked an otherwise valid local identity. See Synheart Auth for the device-identity flow.

Endpoints

The SDK ships no built-in API host — a host baked into the library becomes the destination for any build that forgot to name one, including forks and self-hosted deployments. Resolution order, first non-empty wins:
  1. ApiEndpoints.baseUrlOverride and the per-service overrides
  2. the synheart.baseUrl system property
  3. the SYNHEART_BASE_URL environment variable
With none set, no origin is passed to the runtime and the runtime applies its own default — correct for a local-only build, which makes no network calls at all. Check apiBaseUrlConfigured(config) before enabling anything cloud-bound.
Prefer setting the one base URL over a per-service override. Overriding auth alone points device registration at one environment while consent and ingest stay on another, which surfaces as authentication failures with no obvious cause.
The values must be origins. The runtime appends service paths.

Cloud upload

Cloud upload requires CloudConfig with an orgId, DeviceAuthConfig in production, cloudUpload consent, an issued consent token, and a registered device. The runtime then uploads on its own cadence; no host code moves data.
Drive user-facing copy from cloudSyncStatus or lastIngestSuccessAtMs. uploadQueueLength fluctuates on every flush tick and is a diagnostic, not UI. See Cloud Protocol for wire-level details.

Errors and diagnostics

SynheartCoreError carries a stable machine-readable code. Branch on the code rather than the message.
Common codes: ERR_NOT_CONFIGURED, ERR_INVALID_MODE, ERR_RESEARCH_NOT_ALLOWED, ERR_SESSION_ACTIVE, ERR_NO_ACTIVE_SESSION, ERR_STORAGE_DISABLED, ERR_SYNC_DISABLED. Cloud operations throw a CloudConnectorException subtype (ConsentRequiredError, TokenExpiredError, RateLimitExceededError, SchemaValidationError, NetworkError, …). Sync calls throw SyncNativeException so the runtime’s reason survives:
Branch on the is* helpers rather than retryable alone — retryable says whether to try again, not whether it can ever work. Check runtime health with Synheart.runtimeDiagnostics(), the native runtime’s version with Synheart.runtimeVersion, and this SDK’s own version with SYNHEART_CORE_VERSION. To see why an integration stalled, route the runtime’s own logs somewhere:
Without a filter the runtime logs nowhere, and the silence reads as “nothing happened” rather than “you never asked to be told”.

Upgrading to 0.2.0

0.2.0 also adds watch sessions, a real Health Connect and BLE wear source, requestWearPermissions(), HSIState.modalities / tiers, and runtimeLogEnvFilter. See the changelog for the full list, including the four device-verified bug fixes.

Advanced APIs

The library also provides local session storage and retention, cross-device sync and baseline transfer, sleep/recovery/readiness scoring, Health Connect backfill, watch-to-phone EdgeIngest, lab protocols and research enrolment, secondary SynheartInstance runtime handles, and Syni integration. Start with the lifecycle above. Integrate advanced APIs only when your product needs them:

Architecture

Runtime, modules, and data flow

Consent System

Consent channels and JWT lifecycle

Scoring Models

Sleep, recovery, and readiness

Edge

Watch-to-phone artifacts and ingest

Testing

The SDK repository includes a runnable example app and a Wear OS companion. It runs without cloud credentials, so it needs no platform account — only the installed native runtime:

Android example application

Initialization, consent, sessions, HSI, watch integration, and runtime diagnostics

Resources