Skip to main content

What this SDK does

Synheart Core connects a Flutter 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 Dart streams;
  • optionally stores, synchronizes, and uploads derived state.
The Flutter package coordinates platform integrations. 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 data does not cross the public SDK boundary.

Requirements

  • Dart >=3.8.0 <4.0.0
  • Flutter >=3.32.0
  • iOS 15 or later
  • Android API 24 or later
  • A Synheart Runtime binary installed in the host application
The example Flutter app targets Android API 28 or later. Individual wearable providers can impose additional OS, hardware, or licensing requirements.

Installation

1. Add the Flutter package

Or add the current release to pubspec.yaml:

2. Install the native runtimes

The package alone loads no runtime and produces no HSI. Native artifacts are provisioned by the Synheart CLI, not by pub. Install and authenticate the CLI once:
From your Flutter application root:
synheart install syni is not optional on iOS. synheart_core depends on the syni package, whose iOS pod links its own vendored framework and fails pod install when it is absent — even if your application never uses Syni.
Commit the generated synheart.lock, which pins each artifact by SHA-256. Other developers and CI can restore the pinned artifacts with:
The CLI installs runtime artifacts under:
  • iOS: synheart/vendor/runtime/ios/SynheartCoreRuntime.xcframework/
  • iOS: synheart/vendor/syni-runtime/SyniRuntime.xcframework/
  • Android: synheart/vendor/runtime/android/jniLibs/<abi>/

3. Configure the platforms

iOS

Set SYNHEART_APP_ROOT near the top of your application’s ios/Podfile:
Treat this as required rather than as a monorepo workaround. The Synheart podspecs symlink their vendored framework from <app>/synheart/vendor/ during pod install. Without it they walk up from the pod’s own source directory, which either fails to find an application root (pods resolved from pub-cache) or reaches a different project that happens to have a synheart/vendor/ directory — linking a runtime your synheart.lock does not pin. Enable the HealthKit capability when using health data. Add usage descriptions to ios/Runner/Info.plist:
Use permission copy that accurately describes your application.

Android

Declare only the permissions required by the sources you enable:
Behavior notification access also requires the listener service:
Health Connect requires package queries, a permission-rationale intent, and a privacy-policy activity alias. Copy the applicable declarations from the Flutter example manifest. On Android 14 and later, use FlutterFragmentActivity:
Build your application module against a current compileSdk. The package compiles against 36, matching Flutter’s default and the sibling AndroidX dependencies. The minimum supported Android version is unchanged at API 24.

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.
The following example uses unsigned capabilities for local development:
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 that does not contain |;
  • PrivacyConfig(allowResearch: true) when using research mode.
Passing userId: to initialize() does not populate config.subjectId. Set subjectId on the config itself. Initialization is idempotent. Concurrent callers share the in-progress initialization, and calls made after initialization are no-ops. By default, initialization does not collect data. Call startSession() after consent is ready. Use autoStart: true only when consent has already been completed.

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. The runtime consent-form flow needs a ConsentConfig to stamp submissions:
HSI upload, cross-device sync, research-study enrolment, and server-issued capability tokens are unavailable in this configuration. Consent still persists locally; the runtime is offline-first and reconciles with the cloud profile only when cloud upload is enabled. Consent and capability are different:
  • Capability says what the application is authorized to use.
  • Consent says what the user allows.
Standard collection features are operational only when activation, consent, capability, and an active session all permit them. Syni also has its own model installation and execution lifecycle; see Syni for Flutter. The seven consent channels are:
  • biosignals
  • phoneContext
  • behavior
  • cloudUpload
  • vendorSync
  • research
  • syni
Observe consent changes:
Revoke one channel or all consent:
hasConsent() reports whether a channel is currently enforceable, not what the user chose. Once cloud is configured, the runtime denies every channel until a consent token is issued. For hosted consent profiles and JWT lifecycle, see Consent System.

Feature activation and sessions

Module config enables the corresponding module. Features may also be activated explicitly:
Available features are: Start and stop the full collection lifecycle:
startSession() requires both halves of a pair: a collection feature enabled in SynheartConfig (wearConfig / behaviorConfig / phoneConfig) and its matching consent granted (biosignals / behavior / phoneContext). Neither alone is enough, and cloudUpload, vendorSync, research, and syni never qualify — they govern what happens to data once gathered. A session that could collect nothing throws a StateError instead of starting.
Or control individual collectors:
Module-level methods still enforce consent and capability gates.

Reading HSI

Typed state

Most Flutter applications should use:
The typed state contains:
Axis values can be null when there is not enough input or when an older payload does not contain that reading. Missing means unknown; it does not mean zero. The digital axes come from interaction, so they resolve on hardware with no biosignal source. Use HSIAxes.hasDigital to check whether the window carried any of them. onStateUpdate parses each HSI window once and shares the result across subscribers. Synheart.currentHSIState reuses the same parse, so it is cheap to read repeatedly.

Canonical JSON

Use the raw stream when forwarding or validating the exact HSI 1.3 envelope:
HSIState.rawJson preserves the same JSON. See HSI in Synheart Core for mapping details.

Raw source streams

Raw streams are consent-gated. Always cancel subscriptions with their owning widget, provider, or service. Per-session buffers are also available through Synheart.getSessionHsiWindows() and Synheart.getSessionWearSamples(). Both are capped ring buffers — see maxSessionHsiWindows and maxSessionWearSamples.

Production device authentication

Production applications should configure hardware-backed device identity:
The runtime registers a hardware-backed identity and signs supported requests. No shared HMAC secret is required for normal production upload.
DeviceAuthConfig.packageName is the installed bundle id, which Play Integrity and App Attest verify 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(). Configuring DeviceAuthConfig only makes it possible. Device registration is deferred until a cloud-bound action needs it:
Use Synheart.reregisterDeviceAuth() as an explicit repair action if the server has lost or revoked an otherwise valid local device identity.

Endpoints

The platform origin is https://api.synheart.ai, but the SDK does not bake it in. SYNHEART_BASE_URL is empty unless you set it, and with nothing configured the native runtime applies its own default. Set it explicitly on any build that talks to the cloud:
Optional service-specific overrides:
  • SYNHEART_AUTH_BASE_URL
  • SYNHEART_CONSENT_BASE_URL
  • SYNHEART_INGEST_BASE_URL
Set SYNHEART_BASE_URL rather than relying on a per-service override alone. An override moves one service; the rest keep resolving through SYNHEART_BASE_URL. Overriding auth by itself 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:
  1. CloudConfig;
  2. DeviceAuthConfig in production;
  3. cloudUpload consent;
  4. an issued consent token and a registered device.
The runtime uploads on its own. It subscribes to the engine’s HSI broadcast, enqueues each closed window, and POSTs on CloudConfig.uploadInterval. No host code is required to move data. Inspect the queue and user-facing status:
Use cloudSyncStatus for user interface copy. Queue depth is more useful for diagnostics than for end users. If HSI is produced but nothing uploads, the cause is almost always a closed consent gate — lastUploadError names it. See Cloud Protocol for wire-level details.

Syni chat and sessions

Runtime 0.21.0 and later expose device-signed, non-streaming Syni service calls. Configure SYNHEART_BASE_URL and register the device before calling them. The SDK keeps the active session id after each successful turn, so subsequent calls continue the same conversation until startNewSession().
Chat calls are serialized because sending is non-idempotent and order matters. If a timeout produces a SyniServiceException with deliveryUnknown == true, inspect the session messages before offering a retry; resending immediately could duplicate a turn. On an older native runtime isAvailable is false and the rest of the SDK continues to work. On-device model installation and streaming chat are covered in Syni for Flutter.

Errors and diagnostics

Configuration validation throws SynheartError with a stable code. Lifecycle preconditions commonly throw StateError; invalid arguments throw ArgumentError; native sync failures throw SyncNativeException.
Check runtime health:
missingSymbols lists optional native symbols the loaded runtime does not export. Empty is the healthy state. Anything in it means the vendored runtime predates this SDK release, so the features behind those symbols are disabled and return null, -1, or an empty list rather than throwing. The list fills lazily, so check it after exercising the features you depend on, or pass probeAll: true for a full audit. If isAvailable is false, or symbols are missing:
Then inspect the native linker output and verify the runtime exists under synheart/vendor/runtime/.

Threading

Network-touching calls — syncNow(), flushUploads(), fetchCloudHsiWindows(), requestDataDeletion(), device registration, and the consent mutations — run on a background isolate and return a Future. The per-sample push calls (pushWearHr, pushRr, pushAccel, tick, ingestBatch) are synchronous and safe on the UI isolate at sensor rates. Diagnostic getters are cheap synchronous FFI reads, but they are still FFI calls: do not poll them per frame.

Testing

The SDK repository includes a complete example application. It runs without cloud credentials, so it needs no platform account — only the installed native artifacts:

Flutter example application

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

Upgrading

Notable changes since 0.10: The bundle-secret configuration fields — SynheartConfig.capabilityToken and .capabilitySecret, CloudConfig.apiKey, and ConsentConfig.appApiKey — are deprecated and pending removal. The native runtime dropped bundle-secret configuration as a security fix, so these values are no longer forwarded to it. Use deviceAuthConfig instead, and never ship an API key or signing secret inside an application bundle. See the changelog for the full list.

Advanced APIs

The package also provides:
  • local session storage and retention;
  • cross-device sync and baseline transfer;
  • sleep, recovery, readiness, resilience, and breathing models;
  • Apple Health and Health Connect backfill;
  • watch-to-phone EdgeIngest;
  • lab protocols and research-study enrolment;
  • secondary SynheartInstance runtime handles;
  • Syni adaptive AI 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

Health Backfill

Historical HealthKit and Health Connect import

Resources