Getting Started
From zero to an authenticated request inside a Today WebView.
This walkthrough covers the common path: detect the host, make an authenticated request, and report an analytics event.
1. Create the client
import { createWebViewBridge } from '@today/webview-bridge'
export const bridge = createWebViewBridge()2. Detect the host (optional)
Most code does not need to branch — the client degrades on its own. Branch only when a feature is meaningless outside the app:
import { bridge } from './lib/bridge'
if (bridge.isAvailable()) {
// running inside a Today native WebView
}3. Make an authenticated request
The fetch helper attaches the host-provided headers and transparently retries
once on a 401 after refreshing the token. Prefer it over a bare fetch for
anything that hits an authenticated endpoint.
import { bridge } from './lib/bridge'
const res = await bridge.fetch('https://today.ai/feed/api/cards')
const data = await res.json()If you need control over the headers yourself, pull them and merge manually — see Authenticated fetch.
4. Report an analytics event
track is fire-and-forget. Do not await it; it never blocks your UI and never
throws.
import { bridge } from './lib/bridge'
bridge.track('ui_ele_click', {
page: 'today',
ele_id: card.id,
ele_name: card.title,
ele_type: 'today_card',
})The host enriches every event with its own base fields (session, user, device,
platform, timestamp) before forwarding to PostHog — you only send the
event-specific parameters. See the track contract.
Full example
import { createWebViewBridge } from '@today/webview-bridge'
const bridge = createWebViewBridge()
export async function loadTodayCards() {
const res = await bridge.fetch('https://today.ai/feed/api/cards')
if (!res.ok) {
bridge.track('today_cards_load_failed', { status: res.status })
throw new Error(`cards request failed: ${res.status}`)
}
return res.json()
}This file runs unchanged in the app (real headers, real refresh) and in a
desktop browser during development (plain fetch, no headers).