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.
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: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:
3. Declare permissions
Declare only the permissions required by the sources you enable: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:- Initialize the SDK.
- Ask the user for consent.
- Subscribe to HSI output.
- Start a collection session.
- Stop the session and release resources.
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. OmitCloudConfig 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
Consent and capability are different:- Capability says what the application is authorized to use.
- Consent says what the user allows.
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.
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.startSession() / stopSession(), or
control individual collectors:
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
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.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.Production device authentication
initialize():
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:ApiEndpoints.baseUrlOverrideand the per-service overrides- the
synheart.baseUrlsystem property - the
SYNHEART_BASE_URLenvironment variable
apiBaseUrlConfigured(config) before enabling anything cloud-bound.
The values must be origins. The runtime appends service paths.
Cloud upload
Cloud upload requiresCloudConfig 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.
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.
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:
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:
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-phoneEdgeIngest, 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
Android example application
Initialization, consent, sessions, HSI, watch integration, and runtime diagnostics