{
  "license": "CC BY 4.0",
  "licenseUrl": "https://creativecommons.org/licenses/by/4.0/",
  "recipe": {
    "slug": "net-gross-revenue-retention",
    "title": "Calculate Net and Gross Revenue Retention",
    "testedWith": "Apache DataFusion 45.2.0",
    "lastReviewed": "2026-07-28"
  },
  "contract": {
    "tableName": "account_mrr_snapshots",
    "schema": [
      {
        "name": "timestamp_utc",
        "type": "Timestamp",
        "description": "Month-end snapshot time."
      },
      {
        "name": "account_id",
        "type": "Utf8",
        "description": "Stable billing account identifier."
      },
      {
        "name": "starting_mrr_usd",
        "type": "Float64",
        "description": "MRR at the beginning of the measurement month."
      },
      {
        "name": "ending_mrr_usd",
        "type": "Float64",
        "description": "MRR at the end of the measurement month."
      }
    ]
  },
  "query": "SELECT\n  date_trunc('month', timestamp_utc) AS month,\n  COUNT(*) AS starting_accounts,\n  SUM(starting_mrr_usd) AS starting_mrr_usd,\n  SUM(ending_mrr_usd) AS ending_mrr_usd,\n  100.0 * SUM(ending_mrr_usd)\n    / NULLIF(SUM(starting_mrr_usd), 0) AS net_revenue_retention_pct,\n  100.0 * SUM(CASE\n    WHEN ending_mrr_usd < 0.0 THEN 0.0\n    WHEN ending_mrr_usd > starting_mrr_usd THEN starting_mrr_usd\n    ELSE ending_mrr_usd\n  END) / NULLIF(SUM(starting_mrr_usd), 0)\n    AS gross_revenue_retention_pct\nFROM account_mrr_snapshots\nWHERE timestamp_utc >= now() - INTERVAL '12 months'\n  AND starting_mrr_usd > 0.0\nGROUP BY date_trunc('month', timestamp_utc)\nORDER BY month;",
  "fixtureKind": "schema-example",
  "inputRows": [
    {
      "timestamp_utc": "2026-07-28T10:00:00Z",
      "account_id": "account_1",
      "starting_mrr_usd": 12,
      "ending_mrr_usd": 12
    },
    {
      "timestamp_utc": "2026-07-28T11:00:00Z",
      "account_id": "account_2",
      "starting_mrr_usd": 37,
      "ending_mrr_usd": 37
    },
    {
      "timestamp_utc": "2026-07-28T12:00:00Z",
      "account_id": "account_3",
      "starting_mrr_usd": 24,
      "ending_mrr_usd": 24
    }
  ],
  "illustrativeInputRows": [
    {
      "timestamp_utc": "2026-07-28T10:00:00Z",
      "account_id": "account_1",
      "starting_mrr_usd": 12,
      "ending_mrr_usd": 12
    },
    {
      "timestamp_utc": "2026-07-28T11:00:00Z",
      "account_id": "account_2",
      "starting_mrr_usd": 37,
      "ending_mrr_usd": 37
    },
    {
      "timestamp_utc": "2026-07-28T12:00:00Z",
      "account_id": "account_3",
      "starting_mrr_usd": 24,
      "ending_mrr_usd": 24
    }
  ],
  "expectedOutput": {
    "columns": [
      "month",
      "starting_accounts",
      "starting_mrr_usd",
      "ending_mrr_usd",
      "net_revenue_retention_pct",
      "gross_revenue_retention_pct"
    ],
    "rows": [
      {
        "month": "2026-05",
        "starting_accounts": 418,
        "starting_mrr_usd": 126400,
        "ending_mrr_usd": 132900,
        "net_revenue_retention_pct": 105.14,
        "gross_revenue_retention_pct": 96.44
      },
      {
        "month": "2026-06",
        "starting_accounts": 431,
        "starting_mrr_usd": 132900,
        "ending_mrr_usd": 136100,
        "net_revenue_retention_pct": 102.41,
        "gross_revenue_retention_pct": 94.88
      },
      {
        "month": "2026-07",
        "starting_accounts": 446,
        "starting_mrr_usd": 136100,
        "ending_mrr_usd": 133700,
        "net_revenue_retention_pct": 98.24,
        "gross_revenue_retention_pct": 93.31
      }
    ]
  },
  "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."
  ]
}