{
  "license": "CC BY 4.0",
  "licenseUrl": "https://creativecommons.org/licenses/by/4.0/",
  "recipe": {
    "slug": "churn-and-reactivation",
    "title": "Measure Churn and Reactivation by Cohort",
    "testedWith": "Apache DataFusion 45.2.0",
    "lastReviewed": "2026-07-27"
  },
  "contract": {
    "tableName": "product_events",
    "schema": [
      {
        "name": "timestamp_utc",
        "type": "Timestamp",
        "description": "Meaningful activity time."
      },
      {
        "name": "team_id",
        "type": "Utf8",
        "description": "Account identifier."
      },
      {
        "name": "event_name",
        "type": "Utf8",
        "description": "Qualifying product activity."
      }
    ]
  },
  "query": "WITH monthly AS (\n  SELECT DISTINCT\n    team_id,\n    date_trunc('month', timestamp_utc) AS activity_month\n  FROM product_events\n  WHERE timestamp_utc >= now() - INTERVAL '180 days'\n    AND event_name IN ('feature_used', 'query_run', 'dashboard_viewed')\n),\ntransitions AS (\n  SELECT\n    activity_month,\n    team_id,\n    LAG(activity_month) OVER (\n      PARTITION BY team_id ORDER BY activity_month\n    ) AS previous_active_month\n  FROM monthly\n)\nSELECT\n  activity_month,\n  SUM(CASE\n    WHEN previous_active_month = activity_month - INTERVAL '1 month' THEN 1 ELSE 0\n  END) AS retained_teams,\n  SUM(CASE\n    WHEN previous_active_month < activity_month - INTERVAL '1 month' THEN 1 ELSE 0\n  END) AS reactivated_teams\nFROM transitions\nGROUP BY activity_month\nORDER BY activity_month;",
  "fixtureKind": "schema-example",
  "inputRows": [
    {
      "timestamp_utc": "2026-07-28T10:00:00Z",
      "team_id": "team_id_example_1",
      "event_name": "workflow_completed"
    },
    {
      "timestamp_utc": "2026-07-28T11:00:00Z",
      "team_id": "team_id_example_2",
      "event_name": "workflow_completed"
    },
    {
      "timestamp_utc": "2026-07-28T12:00:00Z",
      "team_id": "team_id_example_3",
      "event_name": "workflow_completed"
    }
  ],
  "illustrativeInputRows": [
    {
      "timestamp_utc": "2026-07-28T10:00:00Z",
      "team_id": "team_id_example_1",
      "event_name": "workflow_completed"
    },
    {
      "timestamp_utc": "2026-07-28T11:00:00Z",
      "team_id": "team_id_example_2",
      "event_name": "workflow_completed"
    },
    {
      "timestamp_utc": "2026-07-28T12:00:00Z",
      "team_id": "team_id_example_3",
      "event_name": "workflow_completed"
    }
  ],
  "expectedOutput": {
    "columns": [
      "activity_month",
      "retained_teams",
      "reactivated_teams"
    ],
    "rows": [
      {
        "activity_month": "2026-05",
        "retained_teams": 442,
        "reactivated_teams": 38
      },
      {
        "activity_month": "2026-06",
        "retained_teams": 468,
        "reactivated_teams": 51
      },
      {
        "activity_month": "2026-07",
        "retained_teams": 492,
        "reactivated_teams": 64
      }
    ]
  },
  "notes": [
    "The compact input rows demonstrate field names and JSON types; they are not claimed to generate the displayed aggregate by themselves.",
    "The expected output is deterministic synthetic review data for the documented output shape.",
    "Adapt time windows, thresholds, identities, and business definitions before operational use."
  ]
}