Add Events

Insert synthetic events derived from existing events or a SQL query.

Exactly one of source_events, sql, or churn must be provided. The new event rows are appended to the eventstream; original rows are kept.

Usage

stream.add_events("session_start", source_events=["login", "app_open"])
stream.add_events("churned", churn={"inactivity_days": 30})
stream.add_events("churned", churn={"inactivity_days": 30, "active_events": ["purchase"]})

How it works

New events are marked synthetic and inserted at the same timestamp as the row that produced them, ordered immediately after it. Nothing is removed.

source_events

One synthetic event per occurrence of any listed source event — not one per path. This is the usual way to give several equivalent events a single name you can anchor on later.

Before:

  • u1: login → browse → login → app_open
  • u2: browse → app_open
stream.add_events("session_start", source_events=["login", "app_open"])

After — u1 gets three session_start events, because it has three matching source events:

  • u1: login → session_start → browse → login → session_start → app_open → session_start
  • u2: browse → app_open → session_start

churn

Marks the point where a path goes quiet: a synthetic event is inserted after the last activity that is followed by a gap of at least inactivity_days. Reaching the end of the observation window counts as such a gap, so paths that simply stop also get the event.

Before — u1 stops after 2 January, while u2 comes back after two months:

user_ideventtimestamp
u1purchase2024-01-01
u1browse2024-01-02
u2browse2024-01-01
u2browse2024-03-01
stream.add_events("churned", churn={"inactivity_days": 30})

After — u1 churns at the end of its own activity, u2 churns mid-path, at the event that preceded the long gap:

user_ideventtimestamp
u1purchase2024-01-01
u1browse2024-01-02
u1churned2024-01-02
u2browse2024-01-01
u2churned2024-01-01
u2browse2024-03-01

Pass active_events to count only meaningful actions as activity — with churn={"inactivity_days": 30, "active_events": ["purchase"]} a user who keeps browsing but stops buying still churns.

sql

For anything the two modes above don't cover, sql takes a DuckDB SELECT over the eventstream table alias returning rows in the eventstream schema; each returned row becomes one synthetic event.

Parameters

ParameterTypeDescription
namestrName of the synthetic event to create.
source_eventslist of str, optionalList of existing event names. Every occurrence of any of them gets a synthetic event at the same timestamp, ordered right after the source row — a path with three matching events gets three synthetic events.
sqlstr, optionalDuckDB SQL SELECT statement that reads from the eventstream table alias and returns rows in the eventstream schema. Each returned row is added as a new synthetic event.
churndict, optionalCreates a churn event after a period of inactivity.

Churn keys

  • inactivity_days (int or float, required) — gap in days after which a churn event is inserted.
  • active_events (list of str, optional) — only these events count as activity; defaults to all events.