Skip to main content
The phone↔watch protocol is defined in session.proto, package synheart.session.v1.

Envelope

Every message on the wire is a SessionMessage with a oneof payload:
Field numbers 1–9 are reserved for phone→watch; 10+ for watch→phone.

Phone → watch

StartSession

The wire ComputeProfile carries only window_sec and emit_interval_sec. The Dart-side ComputeProfile.rawEmitIntervalSec is phone-side only — it doesn’t cross the wire, because the watch derives raw cadence from enable_raw_stream + its own buffer policy.
enable_raw_stream = true activates BiosignalBatch emission from watch to phone. Default is false — raw biosignals must not transmit unless explicitly enabled.

StopSession

Halts the watch engine. The watch responds with the final SessionSummary and transitions to TERMINATED.

GetStatus

Empty message. Watch responds with SessionStatus.

Watch → phone

SessionStarted

Fired once after the watch engine reaches ACTIVE (sensors hot, first sample admitted).

SessionFrame

SessionMetrics

The Dart SDK projects this into a flat Map<String, dynamic> on SessionFrame.metrics.

BehaviorSnapshot

Mirrors the Dart BehaviorSnapshot field-for-field.

SessionSummary

Fires once at session end.

SessionError

ErrorCode enum: ERROR_PERMISSION_DENIED, ERROR_SENSOR_UNAVAILABLE, ERROR_LOW_BATTERY, ERROR_OS_TERMINATED, ERROR_INVALID_STATE. See Errors.

SessionStatus

Response to GetStatus. The Dart-side status returned by getStatus() carries only (sessionId, active, lastSeq); state is currently dropped at the SDK boundary.

BiosignalBatch

Sent as a batch to amortise transport cost. Per the streaming RFC: “Samples must be batched. No per-sample transport.”

Transport mapping

The proto is transport-agnostic; the SDK uses each platform’s native companion channel: The Dart side sees both transports through a single Flutter MethodChannel("ai.synheart.session/methods") + EventChannel("ai.synheart.session/events") pair.

Channel surface

getWatchStatus() returns the connectivity snapshot:

Ordering guarantees

  • SessionStarted precedes any frames or summary for a given session_id.
  • seq is monotonic non-decreasing within a single session id; gaps may exist if the transport drops frames.
  • SessionSummary and SessionError are mutually exclusive — the watch emits one or the other to terminate.
  • BiosignalBatch events are not ordered relative to SessionFrame events; consumers must use timestamp_ms for chronological alignment.

Privacy posture

  • Default enable_raw_stream = false. Opt-in only.
  • When raw streaming is enabled, the producing HSI block must include raw_biosignals_allowed = true.
  • No PII fields cross the wire — session_id is a host-supplied identifier (default UUID v4), window_label is free-form but documented as no-PII.
  • LifecycleSessionState semantics.
  • ProvidersWatchBiosignalProvider and how it consumes this protocol.
  • ErrorsErrorCode taxonomy.