Skip to Content
DocsHow to useRuntime scoring

Runtime scoring

Tiaude exposes three scoring methods:

track() -> process one new event incrementally scoreUser() -> rebuild from raw events refreshScore() -> update time-sensitive scores without a new event

If you remember one rule:

track() is the fast path. scoreUser() is the source-of-truth rebuild path.

track()

Use track() when a new event arrives.

const result = scorer.track({ userId: "user_123", previousState: user.churnState ?? null, event: { name: "feature.used", timestamp: "2026-05-20T10:00:00.000Z", }, });

Use it when:

  • you are processing a new product event;
  • you have the previous saved Tiaude state;
  • events are processed in chronological order;
  • you want the cheapest runtime update.

Rules:

  • previousState may be null for the first event;
  • event timestamps must move forward;
  • now, when provided, must be greater than or equal to the event timestamp;
  • the state must belong to the same user;
  • the state must be compatible with the current config.

track() returns a full ChurnResult.

Persist result.state after every successful call.

scoreUser()

Use scoreUser() when you need to rebuild from raw events.

const result = scorer.scoreUser({ userId: "user_123", events: await loadUserEvents("user_123"), now: new Date(), });

Use it when:

  • bootstrapping an existing user;
  • the saved state is missing;
  • the saved state is invalid or corrupted;
  • events arrived out of order;
  • the scoring config changed;
  • historical events were edited or deleted;
  • you want to audit or debug a score.

Behavior:

  • sorts events by timestamp;
  • accepts an empty event history;
  • rebuilds state from zero;
  • returns the same result shape as track().

scoreUser() is the safe fallback because it does not depend on previous compact state.

refreshScore()

Use refreshScore() when no new event arrived, but time has passed.

const result = scorer.refreshScore({ userId: "user_123", previousState: user.churnState, now: new Date(), });

Use it for:

  • scheduled jobs;
  • account health dashboards;
  • CRM sync;
  • proactive churn review workflows.

Behavior:

  • consumes no new event;
  • ages time-sensitive signals;
  • preserves event counters;
  • updates computedAt;
  • recalculates score, confidence, freshness, reasons, and actions.

Important:

refreshScore() does not make stale data fresh.

Freshness is based on the latest relevant event, not only on the refresh time.

Decision model

if (newEventArrived) { return scorer.track({ userId, previousState, event, }); } if (mustRebuildFromHistory) { return scorer.scoreUser({ userId, events, }); } return scorer.refreshScore({ userId, previousState, now: new Date(), });

Timeline rule

Incremental state can move forward only.

If a state was computed at a future time, Tiaude will reject attempts to reuse it at an earlier time.

For historical debugging, use:

scorer.scoreUser({ userId, events, now: "2026-05-01T00:00:00.000Z", });

Do not use an already-advanced previousState to go backward in time.

Storage rule

To get the benefits of track(), store:

  • raw events in your database;
  • latest Tiaude state per user.

Without saved state, you lose the incremental path.

Without raw events, you lose the safe rebuild path.