Appearance
Insights
Behavioral analytics computed from your event stream — included on every plan. Dashboard → Insights, or the API.
Funnels
Sequential conversion between 2–5 events, ordered and windowed — step N counts subscribers who did it after their first step-N−1 event, within the window:
GET /v1/apps/:appId/insights/funnel?steps=signup,activate,purchase&window=72&days=30Cohort retention
Weekly acquisition cohorts × weekly activity — the classic retention grid:
GET /v1/apps/:appId/insights/cohorts?weeks=8RFM segments
Recency / Frequency / Monetary scoring over 90 days of activity (monetary = meta.amount on purchase events), quintile-scored and mapped to the classic segment names — Champions, Loyal, Promising, At risk, Hibernating, Needs attention:
GET /v1/apps/:appId/insights/rfm?monetaryEvent=purchaseCampaign-level measurement
- Funnel per campaign:
GET /campaigns/:id/stats - A/B variants + winner:
GET /campaigns/:id/variants - Conversion attribution:
GET /campaigns/:id/outcomes?event=purchase&window=24 - Journey stats:
GET /journeys/:id/stats
Predictive bands
The scheduler refreshes two auto-managed tags hourly from the last 90 days of events:
| Tag | Bands | Meaning |
|---|---|---|
_churn_risk | low / medium / high | Last activity within 7 days / 7–21 days / silent 21+ days |
_engagement | high / medium / low | ≥8 opens+clicks in 90d / 2–7 / ≤1 |
Subscribers with no activity at all get no bands — you can't churn from a relationship that never started.
Because they're ordinary tags, they work everywhere tags do with no new syntax: segment filters ({ key: "_churn_risk", op: "eq", value: "high" } targets a win-back campaign), journey condition branches, and Liquid. GET /v1/apps/:id/insights/predictive returns the band distribution, also shown on the dashboard's Insights page. The banding is transparent statistics, not a black box — thresholds are documented above and will evolve behind the same tags.
Warehouse export (BigQuery)
Postgres stays the system of record; set EVENTS_BIGQUERY_DATASET + EVENTS_BIGQUERY_PROJECT (or GOOGLE_CLOUD_PROJECT) on the ingest and worker services and every event — SDK beacons, custom events, delivery receipts — is also streamed to BigQuery dataset.events (columns: app_id, type, subscriber_id, device_id, campaign_id, ts, meta). Mirroring is best-effort by design: a warehouse outage never fails ingestion.
Backfill history with pnpm backfill-bigquery [--app id] [--days 90]. Create the table first:
sql
CREATE TABLE engage.events (
app_id STRING, type STRING, subscriber_id STRING, device_id STRING,
campaign_id STRING, ts TIMESTAMP, meta STRING
) PARTITION BY DATE(ts);