Tracking
Every tracking export of @appfunnel-dev/sdk, generated from the SDK source. Signatures and
descriptions are exact; for how these fit together, see the guides.
useTracker
hook · tracking/tracking.ts
useTracker(): { track: Tracker["track"]; }const { track } = useTracker() — emit a custom event. Auto events
(page.view, identify, purchase, …) fire on their own; this is the escape
hatch for funnel-specific events (track('quiz_skipped', { step: 3 })).
attachBus
function · tracking/bus.ts
attachBus(bus: FunnelBus): () => voidAttach the bus’s public surface to window.appfunnel (client-side only).
Returns a detach fn for unmount. No-op under SSR.
createBus
function · tracking/bus.ts
createBus(accessors: BusAccessors, now?: () => number): FunnelBusCreate a bus over the given read accessors. now is injectable for tests.
createConsoleTracker
function · tracking/tracking.ts
createConsoleTracker(opts?: { silent?: boolean; }): TrackerA no-op-ish tracker that logs to the console — for tests, preview, local dev.
createPlatformTracker
function · tracking/platformTracker.ts
createPlatformTracker(cfg: PlatformTrackingConfig): TrackerintegrationEvents
function · tracking/catalog.ts
integrationEvents(): CatalogEvent[]The events that feed integrations — the set a pixel loader subscribes to.
isIntegrationEvent
function · tracking/catalog.ts
isIntegrationEvent(name: string): booleanTrue if the event is forwarded to ad/integration pixels.
isTrackableEvent
function · tracking/catalog.ts
isTrackableEvent(name: string): booleanTrue if the event is persisted to analytics.
newEventId
function · tracking/tracking.ts
newEventId(): stringwithBus
function · tracking/bus.ts
withBus(tracker: Tracker, bus: FunnelBus): TrackerWrap a Tracker so every emitted taxonomy event is mirrored to the bus
(the client sink). identify() also surfaces a user.registered to the bus, so
pixels see registration. The server sink (the inner tracker → CAPI) is untouched.
AppfunnelBusApi
interface · tracking/bus.ts
export interface AppfunnelBusApi extends BusAccessors {
on(event: string, cb: Listener): () => void
off(event: string, cb: Listener): void
readonly events: BusEvent[]
goTo(pageKey: string): boolean
goNext(): void
goPrevious(): void
navigation(): NavSnapshot | null
}The public window.appfunnel surface — subscribe + read + drive the flow. It never
exposes emit (no forged events) or setVariable (state stays funnel-owned).
BusAccessors
interface · tracking/bus.ts
export interface BusAccessors {
getVariable: (key: string) => unknown
getVariables: () => Record<string, unknown>
getCurrentPageId: () => string | null
getCustomerId: () => string | null
getVisitorId: () => string | null
}Read accessors the bus exposes — wired to the funnel’s store + context.
BusEvent
interface · tracking/bus.ts
export interface BusEvent {
event: string
data: unknown
ts: number
}EventMeta
interface · tracking/catalog.ts
export interface EventMeta {
sources: EventSource[]
tracking: boolean
integrations: boolean
runtime?: boolean
}FunnelBus
interface · tracking/bus.ts
export interface FunnelBus extends AppfunnelBusApi {
emit(event: string, data?: unknown): void
registerNavigation(nav: NavigationControl): () => void
}The internal bus: the public AppfunnelBusApi plus emit (SDK-only) and
now injection. The SDK holds this; only AppfunnelBusApi reaches window.
FunnelEventDataMap
interface · tracking/tracking.ts
export interface FunnelEventDataMap {
'funnel.start': Record<string, never>
'page.view': { pageId: string; pageKey?: string; pageName?: string; isInitial?: boolean; eventId?: string }
'page.exit': { pageId: string; durationMs: number; activeMs: number }
'user.registered': { email: string; eventId?: string }
'checkout.start': { productId?: string; priceId?: string; productName?: string; surface?: string; amount?: number; currency?: string; eventId?: string }
'checkout.payment_added': { productId?: string; amount?: number; currency?: string; eventId?: string }
'checkout.failed': { category: string; declineCode?: string; productId?: string; intent?: string }
'checkout.already_purchased': { productId?: string; declineCode?: string; intent?: string }
'purchase.complete': { amount?: number; currency?: string; email?: string; eventId?: string; productId?: string }
'subscription.created': { amount?: number; currency?: string; productId?: string; eventId?: string }
'experi…PlatformTrackingConfig
interface · tracking/platformTracker.ts
export interface PlatformTrackingConfig {
apiBase: string
campaignId?: string | null
preview?: {
buildId: string
token: string
} | null
funnelId: string
visitorId?: string | null
sessionId?: string | null
experiment?: { id: string; variant: string; version?: number }
route?: { id: string }
mode?: 'test' | 'live'
}Tracker
interface · tracking/tracking.ts
export interface Tracker {
getSessionId?(): string | null
track<E extends KnownFunnelEvent>(event: E, data?: FunnelEventDataMap[E], userData?: Record<string, unknown>): void
track(event: string, data?: Record<string, unknown>, userData?: Record<string, unknown>): void
identify(email: string, eventId?: string): void
setVariables?(variables: Record<string, unknown>): void
setAcquisition?(acquisition: Acquisition): void
}The surface the runtime needs from a tracker. A subset of FunnelTracker, so
the real one satisfies it directly; the console tracker is a trivial stub.
CatalogEvent
type · tracking/catalog.ts
export type CatalogEvent = keyof typeof EVENT_CATALOGA catalog event name.
EventSource
type · tracking/catalog.ts
export type EventSource = 'frontend' | 'backend'The event catalog — the single source of truth for which events exist and
how each is routed (analytics vs. integrations vs. runtime-only). Pairs with the
typed payloads in ./tracking.FunnelEventDataMap.
This replaces the duplication the monorepo flags (shared/events/catalog.ts is
“documentation, not imported” — the taxonomy is re-declared in
admin/lib/funnel/tracking.ts, each admin/lib/integrations/* EVENT_MAP, and
api/src/integrations/constants). In v2 the SDK owns the taxonomy (it emits
it), so the bus, the pixel loaders, and the platform all import this.
KnownFunnelEvent
type · tracking/tracking.ts
export type KnownFunnelEvent = keyof FunnelEventDataMapEVENT_CATALOG
const · tracking/catalog.ts
EVENT_CATALOG: { readonly 'funnel.start': { readonly sources: ["frontend"]; readonly tracking: false; readonly integrations: false; readonly runtime: true; }; readonly 'experiment.exposure': { readonly sources: ["frontend"]; readonly tracking: true; readonly integrations: false; }; readonly 'page.view': { readonly sources: ["frontend"]; readonly tracking: true; readonly integrations: true; }; readonly 'page.exit': { readonly sources: ["frontend"]; readonly tracking: true; readonly integrations: false; }; readonly 'user.registered': { readonly sources: ["frontend"]; readonly tracking: true; readonly integrations: true; }; readonly 'checkout.start': { readonly sources: ["frontend"]; readonly tracking: true; readonly integrations: true; }; readonly 'checkout.payment_added': { readonly sources: ["frontend"]; readonly tracking: true; readonly integrations: true; }; readonly 'checkout.failed': { readonly sources: ["frontend"]; readonly tracking: true; readonly integrations: false; }; readonl…Every event the system knows, with its routing metadata. Keys are the taxonomy
names (a superset of FunnelEventDataMap: it also lists the backend-originated
monetary events the platform pushes to the client bus for pixel firing).