Introduction
The typed, promise-based message channel between web content and its host application.
The Today WebView Bridge is the contract that lets Today web content talk to its host application, whether it is rendered inside a native WebView or the Electron desktop app shell. It is a single, typed, promise-based message channel. Web code calls into host-provided capabilities (analytics, request headers, token refresh); the host answers asynchronously.
The npm package @todayai-labs/webview-bridge is the web-side client for this channel.
iOS, macOS (WKWebView), Android, Windows (WebView2 or Electron), and Linux
(Electron) hosts implement the matching host side.
Why a bridge exists
A native WebView can only decorate the document navigation request — on iOS,
that is the one WKNavigationDelegate.decidePolicyFor(navigationAction) callback
that fires for the top-level page load. Once the page is running, the fetch /
XHR requests a single-page app issues are subresource requests: they never
pass through that callback, and there is no public WebView API to rewrite their
headers.
So the host cannot transparently inject an Authorization header into the web
app's own requests. The bridge inverts the flow: instead of the host pushing
headers onto requests it cannot see, the web asks the host for the headers and
attaches them itself. The headers and refreshToken capabilities exist
entirely to serve this pattern; track rides the same channel for analytics.
If a same-origin auth cookie were already in the WebView's jar, no bridge would be needed for the request path — the WebView attaches same-origin cookies to
fetch/XHR(and to dynamicimport()) automatically. The bridge exists because the browser won't carry aBearerheader on its own, and because the cookie has to be put in the jar in the first place. The/embedcanvas uses the bridge for exactly that:establishSessionhas the native host mint theembed_bearercookie — so the bridge is what establishes cookie auth, after which the WebView carries it automatically. See Authenticated Fetch.
How to read these docs
- Web SDK — the reference for the web side: how to install
@todayai-labs/webview-bridge, the full client API, the authenticated-fetch pattern, and the TypeScript surface. Start here if you are writing web code. - Contract — the transport-agnostic specification: the message envelope, every capability's request and result shape, error semantics, and versioning rules. This is the source of truth that every platform implements against.
- Native Reference — non-normative guidance for host implementers: how iOS, macOS, Android, Windows, and Linux satisfy the contract. Read this if you are building the host side.
At a glance
| Capability | Direction | Await | Purpose |
|---|---|---|---|
track | web → native | no | Fire-and-forget analytics, forwarded to PostHog |
headers | web → native | yes | Fetch the headers web must attach to its own fetch / XHR |
refreshToken | web → native | yes | Force a token refresh after a 401 |
establishSession | web → native | yes | Host mints the session cookie natively; bearer never reaches JS |
Alongside these message capabilities, the SDK exposes
platform ('ios' | 'android' | 'linux' | 'macos' | 'windows' | 'web')
— resolved synchronously, not over the channel, because it is static and
needed before first paint.
The runtime channel is named todayWebViewBridge. On iOS it is reached through
window.webkit.messageHandlers.todayWebViewBridge; Android, Windows, and Linux
present an injected window.todayWebViewBridge object. The SDK hides this
difference — application code only ever touches the
@todayai-labs/webview-bridge client.