session.navigation runs a turn-by-turn trip. Call start() with a destination, then read live maneuvers from onUpdate() as the user moves. The phone owns the trip lifecycle; your miniapp starts it, listens, and stops it.
src/background/index.ts
Navigation needs the LOCATION permission in your manifest. The gated methods (requestPermission, start, computeRoute) check the declaration before doing anything; without it they resolve {ok: false, error} and never reach the phone.
Navigation works on both iOS and Android. Every method resolves with an {ok, error?} shape and nothing throws, so branch on result.ok rather than wrapping in try/catch.

Permission

session.navigation.hasPermission is true when LOCATION is declared in your manifest. It reports the declaration, not the OS grant: if the user denied the system location prompt, a trip still won’t get fixes. requestPermission() confirms the navigation SDK’s terms up front, before the user hits start. On platforms with a terms gate it shows the dialog; where there isn’t one it resolves immediately. It’s idempotent, and resolves a NavPermissionResult:

Starting a trip

start(options) accepts a single destination (lat / lng) or an ordered list of stops. A resolved {ok: true} means the phone accepted the request, not that a route exists yet. Watch onUpdate() and onRoute() for the route and live maneuvers.

Travel modes

mode is one of:

Live updates

onUpdate(handler) delivers a NavUpdate every time the trip changes. Discriminate on kind. It returns an unsubscribe function.
On a "maneuver", prefer instruction (the host’s verbatim text, render as-is) and nextStepRoad (the street after the turn) over the legacy toRoad field. Distance and time fields use -1 for unknown; the speed and heading fields use null.

The route polyline

onRoute(handler) fires once per route build, on the initial start and again on each reroute, with the full path. It returns an unsubscribe function.
A NavRoute carries points (the polyline as LatLng[]), optional totalDistanceMeters and totalDurationSeconds, and optional steps (ordered NavStep segments, each ending in a maneuver).

Reading current state

getState() resolves a NavState snapshot of the running trip, or null when no trip is active. It’s shaped like the streaming events, so a miniapp opened mid-trip can hydrate without waiting for the next update.

Stopping

stop() ends the active trip and detaches pivot tracking. It’s fire-and-forget and a no-op when nothing is running.

Computing a route without starting

computeRoute(options) returns route geometry without starting a trip. Use it to preview a route or show distance and time before the user commits.
It resolves {ok, error?, routes?}. Each ComputedRoute has points, totalDistanceMeters, totalDurationSeconds, an optional summary, and optional steps. The primary route is first; alternates follow.

Pivots

A pivot is a real turn along the route (a left or right), derived once per route build and re-derived on reroute. Pivots let you cue the user as a turn approaches rather than reacting to raw maneuver updates. onPivot(handler) subscribes to pivot events and returns an unsubscribe function. Each pivot fires approachingenteredexited, once per kind, then the cursor advances to the next pivot.
Three reads of the pivot list are available without subscribing: A Pivot carries its index, lat / lng, direction ("left" or "right"), the fromRoad and toRoad names (each null when the engine has none), the maneuver, the distanceAlongRouteMeters from trip start, and the radiusMeters that counts as “turning now”. Tune detection per trip with pivots on start(): radiusMeters sets the “turning now” radius and approachThresholdMeters sets the “approaching” radius. Both fall back to mode-aware defaults when omitted.

Simulation helpers

session.navigation.dev holds simulator-only helpers for testing trips, including deviate(offsetMeters), which nudges the simulated position off-route to trigger a reroute. These are dev tooling, not part of a normal trip flow.