subscriptions

Contextune.subscribe(filter, handler) lets you react to every event log addition in real time. It is framework-agnostic — the same API works in plain browser JS, React, Vue, or any other context.

1 / basic usage

Call subscribe() after init(). It returns an unsubscribe function — call it when you no longer need notifications.

subscribe and unsubscribets
import { Contextune } from '@contextune/sdk';

Contextune.init();

// Fire on every event
const unsubscribe = Contextune.subscribe('event', (entry, state) => {
  console.log('new event:', entry.name);
  console.log('log before this entry had', state.event_log.length, 'events');
});

Contextune.track('page_loaded');   // handler fires
Contextune.track('cta_clicked');   // handler fires again

// Stop listening
unsubscribe();

Contextune.track('something_else'); // handler does NOT fire

2 / filter

The first argument controls which events trigger the handler. Two forms are supported:

SubscriptionFilter
nametypedefaultexample
'event'

Fire the handler on every entry added to the log.

string literal
(entry) => boolean

Fire the handler only when the predicate returns true. Receives the incoming EventLogEntry before it is added to the log.

function
predicate filter examplests
import { Contextune } from '@contextune/sdk';

Contextune.init();

// Fire only for specific events
const unsubscribe = Contextune.subscribe(
  (entry) => entry.name === 'checkout',
  (entry, state) => {
    // entry.name is guaranteed to be 'checkout' here
    sendContextToAgent(entry, state);
  },
);

// more complex predicate
const unsubscribe2 = Contextune.subscribe(
  (entry) => entry.properties?.cart_value != null && Number(entry.properties.cart_value) > 100,
  (entry, state) => {
    triggerHighValueFlow(entry, state);
  },
);

3 / handler arguments

The handler receives two arguments: the incoming event and the full snapshot at the moment the event arrived.

SubscriptionHandler(entry, state)
nametypedefaultexample
entry

The event that just entered the log. A shallow copy — safe to store or mutate. The event_log in state does not yet contain this entry.

EventLogEntry
state

Full snapshot captured immediately before entry was added. event_log reflects the state before this event; marketing_params and device_info are present when enabled.

ContextuneSnapshot
entry vs statets
import { Contextune } from '@contextune/sdk';
import type { ContextuneSnapshot, EventLogEntry } from '@contextune/sdk';

Contextune.init();

Contextune.track('first');
Contextune.track('second');

Contextune.subscribe('event', (entry: EventLogEntry, state: ContextuneSnapshot) => {
  // entry  — the event that just arrived (NOT yet in state.event_log)
  // state  — full snapshot BEFORE this event was added

  // When 'third' fires:
  //   entry.name            === 'third'
  //   state.event_log       has ['first', 'second']
  //   state.marketing_params and state.device_info are also present

  console.log('arrived:', entry.name);
  console.log('previous events:', state.event_log.map(e => e.name));
});

Contextune.track('third');

The pre-event snapshot is useful when you need the full history context at the moment an event triggered — for example, to send both the triggering event and the preceding log to your model in a single call.

4 / react integration

Wrap subscribe() in a useEffectand return the unsubscribe function as the cleanup. This is the canonical React pattern — the subscription is tied to the component's lifecycle.

react: useContextuneEvent hooktsx
'use client';

import { useEffect } from 'react';
import { Contextune } from '@contextune/sdk';
import type { EventLogEntry, ContextuneSnapshot } from '@contextune/sdk';

export function useContextuneEvent(
  eventName: string,
  handler: (entry: EventLogEntry, state: ContextuneSnapshot) => void,
) {
  useEffect(() => {
    const unsubscribe = Contextune.subscribe(
      (e) => e.name === eventName,
      handler,
    );
    return unsubscribe; // cleanup on unmount
  }, [eventName, handler]);
}

// Usage in a component:
export function CheckoutWatcher() {
  useContextuneEvent('checkout', (entry, state) => {
    console.log('checkout with', state.event_log.length, 'prior events');
  });
  return null;
}

5 / multiple subscribers

Any number of subscribers can be active at the same time. Each runs independently — an error in one does not prevent the others from being called. Errors inside handlers are caught and silently dropped.

multiple subscribersts
import { Contextune } from '@contextune/sdk';

Contextune.init();

// Multiple independent subscribers — all fire on each matching event.
// Each subscriber is isolated: an error in one does not affect the others.

const unsubA = Contextune.subscribe('event', (entry) => {
  analyticsLayer.push(entry);
});

const unsubB = Contextune.subscribe(
  (e) => e.name.startsWith('checkout'),
  (entry, state) => {
    agentLayer.notify(entry, state);
  },
);

// Tear down individually or let destroy() clear all of them.
unsubA();
// unsubB stays active

6 / plain browser JS

No framework required. subscribe() is a plain function call on the static Contextune object.

plain browser jsts
// Works outside of any framework — plain browser JS.
// Wire it up once and let it run for the lifetime of the page.

import { Contextune } from '@contextune/sdk';

Contextune.init({ source: 'ga4' });

Contextune.subscribe('event', (entry, state) => {
  // Forward to your server-sent-events endpoint, WebSocket, etc.
  navigator.sendBeacon('/api/events', JSON.stringify({ entry, state }));
});

7 / lifecycle

destroy clears all subscribersts
import { Contextune } from '@contextune/sdk';

Contextune.init();

const unsub = Contextune.subscribe('event', (entry) => {
  console.log(entry.name);
});

// destroy() clears ALL subscribers — no need to call unsub() first.
Contextune.destroy();

// Re-init starts with an empty subscriber set.
Contextune.init();

types

subscription typests
type SubscriptionFilter = 'event' | ((entry: EventLogEntry) => boolean);

type SubscriptionHandler = (
  entry: EventLogEntry,
  state: ContextuneSnapshot,
) => void;

// Both are exported from '@contextune/sdk'
import type { SubscriptionFilter, SubscriptionHandler } from '@contextune/sdk';

For the full method signature, see API reference → subscribe.