api reference
The SDK exposes one object, Contextune, with seven methods: init, getSnapshot, track, page, subscribe, clear, and destroy. Every method is safe to call at any time, and none of them throws.
import
import { Contextune } from '@contextune/sdk';
// Public types — import only what you reference.
import type {
ContextuneOptions,
ContextuneInstance,
ContextuneSource,
SubscriptionFilter,
SubscriptionHandler,
ContextuneSnapshot,
EventLogEntry,
MarketingParamsBlock,
DeviceInfoBlock,
SnapshotFormat,
} from '@contextune/sdk';1 / Contextune.init(options?)
Initialise the SDK. Idempotent — calling twice returns the existing instance unchanged. In network-intercept mode (when source is set), wires fetch, sendBeacon, and XHR patches. In direct-call mode (no source), installs no patches.
init(options?: ContextuneOptions): ContextuneInstance1 / parameters
| name | type | default | example |
|---|---|---|---|
optionsOptional configuration. See the Configuration page for every field. The minimal call is Contextune.init(). | ContextuneOptions | {} | { source: 'ga4' } |
2 / returns
A ContextuneInstance with getSnapshot(), track(), page(), subscribe(), and destroy()methods. You typically don't need to hold this reference — the static Contextune façade delegates to the same singleton.
3 / example
Contextune.init(); // direct-call mode (no network intercept)
Contextune.init({ source: 'ga4' }); // network-intercept mode — taps GA4 traffic
Contextune.init({ // custom event log size
eventLogSize: 100,
});If init() is called a second time, it returns the existing instance with no changes. Call destroy() first to apply different options.
2 / Contextune.getSnapshot(options?)
Return a fresh structured snapshot of the user's current behavioural profile. Synchronous, side-effect-free, never throws. Returns null before init() and after destroy().
getSnapshot(): ContextuneSnapshot | null
getSnapshot(options: { format: 'js' }): ContextuneSnapshot | null
getSnapshot(options: { format: 'json' | 'toon' }): string | null1 / parameters
| name | type | default | example |
|---|---|---|---|
options.formatOutput format. 'js' returns a plain object. 'json' returns a pretty-printed JSON string. 'toon' returns a compact human-readable text suitable for injecting inline into a system prompt. | 'js' | 'json' | 'toon' | 'js' | 'json' |
2 / returns
A ContextuneSnapshot object (or formatted string), or null if not yet initialised.
interface ContextuneSnapshot {
event_log: EventLogEntry[];
marketing_params?: MarketingParamsBlock;
device_info?: DeviceInfoBlock;
}3 / example
const snapshot = Contextune.getSnapshot();
if (!snapshot) return; // not yet initialised
// last three events
const recent = snapshot.event_log.slice(-3);
// hand the whole thing to your model
const systemPrompt = `...
<user_behavioural_context>
${JSON.stringify(snapshot, null, 2)}
</user_behavioural_context>`;
// or as toon format for compact inline injection
const toon = Contextune.getSnapshot({ format: 'toon' });Snapshot reads are cheap — call getSnapshot()every time you invoke the model. Don't cache it; a stale snapshot is worse than no snapshot.
3 / Contextune.track(name, properties?)
Emit a structured custom event. The event lands in event_log and goes through the same sanitisation as every other event. No-op before init() and after destroy() — never crashes the host page.
track(name: string, properties?: Record<string, unknown>): void1 / parameters
| name | type | default | example |
|---|---|---|---|
nameEvent name. Sanitised and length-capped before it lands in the log. | string | — | 'filter-dropdown:applied' |
propertiesOptional event payload. Recursively sanitised; sensitive keys redacted, URL-shaped values scrubbed, depth bounded at 4. | Record<string, unknown> | undefined | { facet: 'size', value: 'M' } |
2 / returns
void. Fire-and-forget.
3 / example
Contextune.track('filter-dropdown:applied', { facet: 'size', value: 'M' });
Contextune.track('checkout-form:submitted', { cart_value: 129 });
// no properties is fine
Contextune.track('searched');Custom events work in both modes. See Custom events for when to use them.
4 / Contextune.page(url?)
Emit a page_view event. Convenience wrapper around track('page_view', { url }). No-op before init() and after destroy().
page(url?: string): void1 / parameters
| name | type | default | example |
|---|---|---|---|
urlOptional URL to record with the page view. Routed through the URL sanitiser — sensitive query params become [REDACTED], UUIDs in the path become [id]. | string | undefined | '/checkout' |
2 / example
// SPA route change handler
router.on('change', (url) => {
Contextune.page(url);
});
// or without a URL
Contextune.page();5 / Contextune.subscribe(filter, handler)
Register a callback that fires whenever a matching event enters the log. Returns an unsubscribe function. Framework-agnostic — works in plain browser JS, React, Vue, or any other context.
subscribe(filter: SubscriptionFilter, handler: SubscriptionHandler): () => void1 / parameters
| name | type | default | example |
|---|---|---|---|
filterControls which events trigger the handler. 'event' fires on every entry. A predicate function (entry) => boolean fires only when it returns true. | SubscriptionFilter | — | 'event' |
handlerCalled with (entry, state) where entry is the new event and state is the full snapshot captured immediately before the event was added. Errors in the handler are caught and isolated. | SubscriptionHandler | — | (entry, state) => console.log(entry.name) |
2 / handler arguments
| name | type | default | example |
|---|---|---|---|
entryThe event that just entered the log. A shallow copy — safe to store. | EventLogEntry | — | { name: 'checkout', elapsed_s: 42 } |
stateFull snapshot captured before entry was added. The event_log does not yet contain entry — this is the pre-event state. | ContextuneSnapshot | — | { event_log: [...] } |
3 / returns
An unsubscribe () => void function. Call it to stop receiving notifications.
4 / example
// every event
const unsub = Contextune.subscribe('event', (entry, state) => {
console.log(entry.name, 'log had', state.event_log.length, 'events before this');
});
// specific event by name
const unsub2 = Contextune.subscribe(
(e) => e.name === 'checkout',
(entry, state) => sendToAgent(entry, state),
);
// stop listening
unsub();
unsub2();See Subscriptions for full usage patterns including React integration and multiple subscriber examples.
6 / Contextune.clear()
Reset the event log while keeping the SDK running. Empties the log in memory and its persisted copy (under ct-session), but leaves everything else intact: transport patches stay installed, subscribers stay registered, marketing_params and device_info keep being captured, and the session clock (elapsed_s) keeps counting from the original session start. No-op before init().
clear(): void1 / example
// start a fresh behavioural log without tearing down the SDK
Contextune.clear();
// e.g. when a user starts a new flow or you want to drop pre-login events
onLogin(() => Contextune.clear());Use clear() to reset the log; use destroy() to shut the SDK down entirely. Unlike destroy(), clear() does not remove patches, subscribers, or the session cookie.
7 / Contextune.destroy()
Full teardown. Removes the transport patches, clears the buffer and all subscribers, and purges persisted state — under ct-session it removes the localStorage entry and expires the session cookie, so the profile cannot be recovered by a later init(). After destroy(), getSnapshot() returns null and track() is a no-op.
destroy(): void1 / example
// deliberate end-of-session — ends the session and erases the stored profile
onLogout(() => Contextune.destroy());Most apps never call destroy(): init() is mount-once and clear()handles resetting the log. Reach for it only to deliberately end a session and wipe the stored profile (logout, account switch). Don't wire it to component unmount — a root-mounted SDK should live for the page's lifetime, and under ct-session an unmount destroy() would erase a log meant to survive reloads. (To reconfigure with different options, call destroy() then init() again.)
8 / public types
All public types are exported from @contextune/sdk. The most useful:
interface ContextuneOptions {
source?: 'ga4' | 'segment' | 'mixpanel';
eventLogSize?: number;
persistence?: 'navigation' | 'ct-session';
context?: {
extras?: {
marketing?: boolean; // default true
device?: boolean; // default true
};
};
tracking?: {
scroll?: boolean; // emit 'scroll' at depth milestones (default off)
rageClicks?: boolean; // emit 'rage_click' on 3+ fast clicks (default off)
};
}
interface ContextuneInstance {
getSnapshot(options?: { format?: SnapshotFormat }): ContextuneSnapshot | string;
track(name: string, properties?: Record<string, unknown>): void;
page(url?: string): void;
subscribe(filter: SubscriptionFilter, handler: SubscriptionHandler): () => void;
clear(): void;
destroy(): void;
}
type SubscriptionFilter = 'event' | ((entry: EventLogEntry) => boolean);
type SubscriptionHandler = (entry: EventLogEntry, state: ContextuneSnapshot) => void;For the full per-block shape, see Configuration.