Skip to content
Shiny.NET
Commercial SupportGuaranteed response times on Shiny bug fixes, straight from the maintainers. Plans from US$1,500 a year.See the plans

Bridges

Each bridge is a set of HTTP routes under /_bridge/{name} on the app’s loopback server, plus the events it pushes to the page. Pick one to see every request — its parameters, body, response and errors — every event and its payload, and every type they carry. Those reference sections are generated from the bridges’ typed-client declarations, so they can’t drift from the library.

50bridges
444requests
114events
Hostbuilt in
/_bridge/host

The host itself: what is serving the page, which bridges it has, and a pending web app update.

2 requests0 events
Settingsbuilt in
/_bridge/settings

Key/value settings in the platform's stores.

5 requests0 events
Filesbuilt in
/_bridge/files

The page's own files, confined to named roots the app configured — data and cache by default.

12 requests1 event
files.roots
App linksbuilt in
/_bridge/links

Deep links and universal links.

2 requests1 event
app.link
App
/_bridge/app

The app and the device it runs on, over Shiny.Core: information, orientation, the screen, settings, the app store, launch at login, battery and connectivity.

15 requests6 events
app.orientationapp.screenapp.cultureapp.timezoneapp.batteryapp.connectivity
Device
/_bridge/device

The device's apps and feedback, over .NET MAUI Essentials: the browser, maps, the share sheet, haptics, vibration, keeping the screen awake and the clipboard.

12 requests0 events
Sensors
/_bridge/sensors

Motion and environment sensors — accelerometer, gyroscope, magnetometer, compass, barometer and orientation — as live events.

4 requests7 events
sensors.accelerometersensors.gyroscopesensors.magnetometersensors.compasssensors.barometersensors.orientationsensors.shake
GPS
/_bridge/gps

GPS: permission, the last and current position, and a listener whose readings arrive as events.

7 requests1 event
gps.reading
Motion activity
/_bridge/motion

Motion activity: walking, running, cycling, driving or stationary, as the OS's activity recognition reports it.

6 requests1 event
motion.activity
Reverse geocoding
/_bridge/geocoding

Reverse geocoding: the addresses at a position, from the platform geocoder (MapKit / CoreLocation on iOS and Mac Catalyst, android.location.Geocoder on Android) or, on Windows, Linux, macOS and Android devices without a geocoding backend, OpenStreetMap's Nominatim.

1 request0 events
Geofences
/_bridge/geofences

Geofences: monitor circular regions and hear when the device enters or leaves one.

7 requests1 event
geofence.status
Region geofences
/_bridge/regiongeofences

Region geofencing: GPS-driven monitoring of polygon, proximity and reverse-geocoded regions, with no platform limit on how many.

5 requests1 event
regiongeofence.change
Beacons
/_bridge/beacons

Beacons: range iBeacons in the foreground, monitor iBeacon regions in the background, scan for Eddystone frames, and advertise the device as a beacon.

18 requests3 events
beacons.rangedbeacons.regionbeacons.eddystone
Bluetooth LE
/_bridge/ble

Bluetooth LE central: scan, connect, discover services and characteristics, read, write and subscribe to notifications.

15 requests4 events
ble.scanble.statusble.notificationble.error
OBD-II
/_bridge/obd

OBD-II through one ELM327 or OBDLink adapter at a time, over Bluetooth LE or Wi-Fi: find and connect to an adapter, read values, the VIN and trouble codes, and monitor readings live.

13 requests2 events
obd.readingobd.disconnected
Receipt printers
/_bridge/printers

Receipt and label printers — ESC/POS or TSPL over Bluetooth LE or the network (raw TCP, port 9100), one at a time: find and connect to a printer, then print receipts built in the page.

7 requests1 event
printers.disconnected
OS printing
/_bridge/printing

The operating system's own printing — AirPrint on iOS and Mac Catalyst, the print dialog on Android, the spooler on Windows, CUPS on Linux and macOS — for any printer it knows.

6 requests0 events
Wi-Fi
/_bridge/wifi

Wi-Fi: scanning, the current network, joining and forgetting networks, the radio, and a hotspot.

14 requests2 events
wifi.changedwifi.hotspot
Discovery
/_bridge/discovery

Local network discovery over mDNS/Bonjour, SSDP/UPnP and WS-Discovery: search for a window, browse with results as events, resolve one, and advertise this app.

16 requests5 events
discovery.mdnsdiscovery.ssdpdiscovery.wsddiscovery.errordiscovery.stopped
Hubs
/_bridge/hubs

Universal Hubs whose logic lives in the web app — and joining other devices' hubs (clients).

23 requests5 events
hubs.streamhubs.client.hosthubs.client.pushhubs.client.statushubs.client.stream
Notifications
/_bridge/notifications

Local notifications: permission, sending now or on a schedule, repeating or on entering an area, the pending list, cancelling, the app badge and Android channels.

11 requests2 events
notification.entrynotification.received
Push
/_bridge/push

Push notifications: registration, the token, tags, and what arrives.

5 requests4 events
push.tokenpush.unregisteredpush.receivedpush.entry
HTTP transfers
/_bridge/transfers

Background HTTP transfers that keep going while the app is suspended: downloads into a file root and uploads from one.

7 requests4 events
transfer.progresstransfer.completedtransfer.failedtransfer.cancelled
Wearables
/_bridge/wearables

The companion app on a paired Apple Watch or Wear OS device.

8 requests6 events
wearables.statuswearables.messagewearables.contextwearables.transferwearables.filewearables.completed
Live activities
/_bridge/liveactivities

Live activities: iOS/iPadOS Live Activities on the Lock Screen and in the Dynamic Island, and Android 16 Live Updates (an ordinary ongoing notification on older Android).

7 requests4 events
liveactivities.startedliveactivities.stateliveactivities.tokenliveactivities.starttoken
In-app purchases
/_bridge/purchases

In-app purchases — consumables, non-consumables and auto-renewable subscriptions — through the App Store (StoreKit 2) on iOS and Google Play Billing on Android.

8 requests1 event
purchases.updated
Maps
/_bridge/maps

Vector maps the page draws with MapLibre or any other vector-tile renderer.

12 requests1 event
maps.download
Directions
/_bridge/directions

Turn-by-turn directions.

3 requests0 events
Health
/_bridge/health

HealthKit on iOS and Health Connect on Android: request access per type, read samples, write them, and watch a type for new readings.

6 requests2 events
health.readinghealth.stopped
Speech
/_bridge/speech

On-device speech recognition and text-to-speech.

10 requests6 events
speech.partialspeech.resultspeech.keywordspeech.endedspeech.spokenspeech.error
Screen recorder
/_bridge/screenrecorder

Records the device's screen to a video filed into a file root.

7 requests2 events
screenrecorder.statusscreenrecorder.ended
Share target
/_bridge/sharetarget

Content other apps shared to this one: text, a link, files.

3 requests1 event
sharetarget.received
Contacts
/_bridge/contacts

The device's address book: search and page through contacts, read one, create, update and delete, and fetch photos.

8 requests0 events
Calendar
/_bridge/calendar

The device's calendars, from the page.

8 requests0 events
Photos
/_bridge/photos

Photos, two ways.

6 requests0 events
Folders
/_bridge/folders

Folders the user picks with the platform's own picker.

3 requests0 events
Database
/_bridge/database

A database client on the device: SQLite files in the app's file roots, opened where they lie so a 4GB file costs a page of rows rather than 4GB; and database servers a driver the app registered can reach, which a page cannot open a socket to.

23 requests0 events
Tray icon
/_bridge/tray

System tray and menu bar icons on Windows, macOS and Linux: create and update them, give them menus, show notifications, animate them, and hear clicks and menu choices.

12 requests2 events
tray.clicktray.menu
Quick entry
/_bridge/quickentry

Quick entry: a prompt window that opens over other applications — from a global hotkey, a tray click or the page — in the style of Spotlight or a desktop assistant.

11 requests6 events
quickentry.submittedquickentry.suggestionquickentry.cancelledquickentry.microphonequickentry.openedquickentry.closed
Device camera
/_bridge/camera

The device's own camera, driven from wherever the page is — a laptop across the room from a phone on a mount.

8 requests1 event
camera.status
Pi camera
/_bridge/rpicamera

A Raspberry Pi camera through libcamera: the cameras attached, JPEG snapshots, photographs written into a file root, and sensor controls.

6 requests0 events
AgeSignals
/_bridge/agesignals

The user's age range as the app store shares it — Apple Declared Age Range (iOS and macOS 26+) and Google Play Age Signals — for app store accountability laws such as Texas SB 2420, and for any app that gates features by age.

3 requests0 events
AiConversation
/_bridge/ai

Conversations with the app's AI model: persisted sessions that stream their replies as events, ask typed questions, remember things across sessions, and can be talked to through the voice loop.

23 requests18 events
ai.turn.startedai.deltaai.toolai.questionsai.turn.endedai.messageai.stateai.usageai.titleai.compactedai.memoryai.errorai.voice.stateai.voice.transcriptai.voice.speakingai.voice.interruptedai.voice.levelai.voice.error
Audio
/_bridge/audio

The device's speakers and microphone: play clips, read and set the system volume, record the microphone to WAV, list audio routes, and route the microphone live to the output.

15 requests8 events
audio.playback.endedaudio.playback.levelaudio.volumeaudio.recording.levelaudio.recording.endedaudio.devicesaudio.monitor.levelaudio.monitor.ended
Barcodes
/_bridge/barcodes

Scans barcodes with the device's camera: the device shows its own scanner, full screen, and the codes it reads come back to the page.

3 requests1 event
barcodes.detected
Biometrics
/_bridge/biometrics

Verifies the device owner with the platform's own biometric prompt: Touch ID, Face ID and Optic ID on iOS, Mac Catalyst and macOS, fingerprint, face and iris on Android, and Windows Hello.

2 requests0 events
CallDirectory
/_bridge/calldirectory

Call blocking and caller identification from a list of numbers the app owns: iOS loads it through a Call Directory extension, and Android screens calls against it.

9 requests0 events
GameCenter
/_bridge/games

Achievements, leaderboards and friends through Apple Game Center (iOS, Mac Catalyst, macOS) and Google Play Games (Android).

13 requests1 event
games.player
Nfc
/_bridge/nfc

Reads and writes NDEF NFC tags: URLs, text, MIME payloads, smart posters and Android app records.

9 requests2 events
nfc.tagnfc.scan
Vpn
/_bridge/vpn

The app's one profile in the operating system's built-in IKEv2 VPN client.

5 requests1 event
vpn.status

The routes are the wire protocol. A page doesn’t build them by hand: every bridge has a typed client, in C# for Blazor and in TypeScript for everything else, generated from one declaration so the two can’t drift — see Typed clients.

@inject IAppBridge App
@inject IGpsBridge Gps
var info = await App.GetInfoAsync();
await using var readings = await Gps.OnReadingAsync(reading => { position = reading; return Task.CompletedTask; });
import { AppBridge, GpsBridge } from "@shinyorg/appdevicebridge";
const info = await new AppBridge().getInfo();
const stop = await new GpsBridge().onReading(reading => console.log(reading.latitude));

GET /_bridge/host lists every bridge and whether this platform supports it. A bridge with no implementation on a platform answers 501, so a page can grey it out instead of failing. The sample on macOS, where contacts, GPS, geofences and health aren’t available:

The Blazor sample on macOS: 19 of 26 bridges, with contacts, geofences, gps and health marked not on this platform

Every event reaches the page over one Server-Sent Events stream, GET /_bridge/events, and the page names the events it wants on it. The typed clients do this for you: each On…Async / on… subscription adds its event to the stream, and disposing the last subscription to an event drops it.

const events = new EventSource("/_bridge/events?topics=gps.reading,app.battery");
events.addEventListener("bridge.stream", e => streamId = JSON.parse(e.data).id);
events.addEventListener("gps.reading", e => show(JSON.parse(e.data)));
// later, without reconnecting
await fetch(`/_bridge/events/${streamId}`, { method: "PUT", body: JSON.stringify({ topics: ["gps.reading"] }) });
  • Only what’s listened to runs. Behind each event is an async stream on the host that hooks the native source when a page asks for the event and unhooks it when the page drops the event, disconnects, or the source fails. Nothing stays attached to the battery, the network or a sensor for a page that’s gone.
  • Sessions follow their listeners. A BLE scan or characteristic subscription, a Health listener, an OBD monitor, a Discovery browse, dictation and each sensor stop once nothing listens to their events any more. Starting one of the first five without a listener answers 409 not_listening, so subscribe first. The client methods resolve once the host is delivering the event, so await the subscription and then start. GPS and motion listeners keep running without a page, because background.js receives their readings too.
  • bridge.stream comes first on every connection, including an automatic reconnect, and carries the id that PUT /_bridge/events/{id} with { "topics": [...] } takes. It replaces the stream’s topics, and names nothing maps yet are kept for when something does. At most 64.
  • bridge.error { "event", "message" } means an event’s source failed, such as a feature the device lacks, and that event was dropped from the stream. Naming it again retries.
  • One connection. The WebView talks HTTP/1.1 to the loopback server, with about six connections per origin shared by everything the page does, so every event shares one stream rather than holding a connection each.
  • Best effort. Nothing is buffered while a page isn’t listening, and a page more than a few hundred events behind loses the oldest. A page that closes without a word is noticed at the next write; an idle stream writes a heartbeat every EventStreamHeartbeat (15 s) so that happens promptly.

Errors return { "code": "...", "message": "..." }. The clients throw BridgeException (C#) or BridgeError (TypeScript) carrying the status and code, so pages can switch on either. Each request in a bridge’s reference lists the statuses it can answer with; the ones every bridge shares:

Status Means
400 The body or a parameter is invalid — a missing field, a value out of range.
404 The thing the route names doesn’t exist: a file, a region id, a transfer, a contact.
409 The device is in the wrong state for it — another session is running, nothing is connected, or not_listening: a session that streams events was started before the page subscribed to them.
501 Not on this platform. GET /_bridge/host reports which bridges are supported, so a page can grey a feature out up front instead.
try {
status = await new BluetoothLEBridge().getStatus();
} catch (e) {
if (e instanceof BridgeError && e.isNotSupported) hideBluetooth(); // 501
else throw e;
}