Today WebView Bridge
Web SDK

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

lib/bridge.ts
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

today-cards.ts
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).

On this page