Commercial Event Ledger (CEL)
The single source of truth for all metric computation in the GASP Standard.
Every metric in this standard — revenue, retention, efficiency, operational — derives from atomic commercial events. The CEL is the governed layer where those events live. If an event is not in the CEL, it does not exist for metric purposes. If a metric cannot be traced to CEL events, it is not a governed metric.
Why the CEL Exists
SaaS companies typically compute metrics from derived tables: MRR snapshots, billing system exports, CRM rollups. These derived sources introduce drift. Two teams querying the same concept from different derived tables get different numbers. The CEL eliminates this by establishing a single event-level layer from which all metrics are computed.
The CEL is not a product. It is a conceptual standard for how commercial events should be captured, stored, and governed. Implement it in your warehouse, your lakehouse, or your operational database. The schema is the contract.
Design Principles
-
Immutable events. Once recorded, a CEL event is never modified. Corrections are new events (e.g., a
Revenue_Eventof typeadjustment). This preserves auditability and enables point-in-time reconstruction. -
No derived data upstream. The CEL contains atomic facts. MRR, ARR, NRR, and every other metric are computed from CEL events, never stored in the CEL. Derived values live downstream.
-
Daily atomic grain. Every event has a date. The day is the smallest unit. Weekly, monthly, quarterly, and annual views are aggregations of daily events.
-
One event, one truth. Each commercial event is recorded once. If a subscription renewal generates both a
Revenue_Eventand aContract_Event, those are two distinct events — not one event with two types. -
Source-system agnostic. The CEL defines what to capture, not where it comes from. Events may originate in billing systems, CRMs, HRIS, or manual entries. The CEL schema is the normalization layer.
Event Classes
The CEL defines five event classes. Every commercial event in a subscription SaaS business falls into one of these.
Revenue_Event
A change in recurring revenue. This is the primary input for MRR, ARR, NRR, GRR, and revenue churn metrics.
| Field | Type | Description |
|---|---|---|
event_id | string | Unique identifier |
event_date | date | Date the event occurred |
customer_id | string | Customer identifier |
subscription_id | string | Subscription identifier |
event_type | enum | new, expansion, contraction, churn, reactivation, renewal |
mrr_change | decimal | Change in MRR (positive for new/expansion/reactivation, negative for contraction/churn) |
currency | string | ISO 4217 currency code |
prior_mrr | decimal | MRR before this event |
new_mrr | decimal | MRR after this event |
Cost_Event
An incurred cost attributable to a commercial function. Primary input for CAC, Gross Margin, LTV, and efficiency metrics.
| Field | Type | Description |
|---|---|---|
event_id | string | Unique identifier |
event_date | date | Date the cost was incurred |
cost_centre | string | Department or team |
amount | decimal | Cost amount |
currency | string | ISO 4217 currency code |
cost_type | enum | personnel, software, services, infrastructure, other |
customer_id | string (nullable) | Customer if directly attributable; null for shared costs |
description | string | Human-readable description |
Contract_Event
A contractual milestone. Primary input for bookings, renewal rate, and contract-level metrics.
| Field | Type | Description |
|---|---|---|
event_id | string | Unique identifier |
event_date | date | Date of contractual action |
customer_id | string | Customer identifier |
subscription_id | string | Subscription identifier |
event_type | enum | new_contract, renewal, amendment, cancellation |
contract_start | date | Contract start date |
contract_end | date | Contract end date |
tcv | decimal | Total contract value |
acv | decimal | Annual contract value |
currency | string | ISO 4217 currency code |
Amendment_Event
A mid-term change to an existing subscription. Distinct from Contract_Event because amendments happen during a contract term, not at boundaries. Primary input for expansion/contraction classification.
| Field | Type | Description |
|---|---|---|
event_id | string | Unique identifier |
event_date | date | Date of amendment |
customer_id | string | Customer identifier |
subscription_id | string | Subscription identifier |
amendment_type | enum | upgrade, downgrade, add_on, seat_change, pricing_change |
mrr_change | decimal | Change in MRR resulting from amendment |
prior_plan | string | Plan/tier before amendment |
new_plan | string | Plan/tier after amendment |
prior_quantity | integer (nullable) | Seat/unit count before (if applicable) |
new_quantity | integer (nullable) | Seat/unit count after (if applicable) |
Lifecycle_State_Change
A change in customer lifecycle status. Primary input for health scores, onboarding metrics, and churn prediction.
| Field | Type | Description |
|---|---|---|
event_id | string | Unique identifier |
event_date | date | Date of state change |
customer_id | string | Customer identifier |
prior_state | string | Previous lifecycle state |
new_state | string | New lifecycle state |
state_category | enum | activation, onboarding_complete, healthy, at_risk, churned, reactivated |
trigger | string | What caused the state change (e.g., “health score drop below 40”, “90-day inactivity”) |
Example Events
These worked examples show how real-world commercial actions map to CEL events.
1. Subscription Activation
A new customer signs up for a $500/mo plan.
{
"event_class": "Revenue_Event",
"event_id": "rev-001",
"event_date": "2026-02-01",
"customer_id": "cust-1042",
"subscription_id": "sub-2001",
"event_type": "new",
"mrr_change": 500.00,
"currency": "USD",
"prior_mrr": 0.00,
"new_mrr": 500.00
}
2. Usage Commit Realisation
An existing customer’s usage commitment triggers an additional $200/mo.
{
"event_class": "Revenue_Event",
"event_id": "rev-002",
"event_date": "2026-02-15",
"customer_id": "cust-0783",
"subscription_id": "sub-1450",
"event_type": "expansion",
"mrr_change": 200.00,
"currency": "USD",
"prior_mrr": 1000.00,
"new_mrr": 1200.00
}
3. Mid-Term Expansion (Seat Add)
Customer adds 10 seats at $50/seat/mo mid-contract.
{
"event_class": "Amendment_Event",
"event_id": "amd-001",
"event_date": "2026-02-10",
"customer_id": "cust-0512",
"subscription_id": "sub-0890",
"amendment_type": "seat_change",
"mrr_change": 500.00,
"prior_plan": "Professional",
"new_plan": "Professional",
"prior_quantity": 20,
"new_quantity": 30
}
4. Cross-Module Expansion
Customer on the Analytics module purchases the Automation module.
{
"event_class": "Amendment_Event",
"event_id": "amd-002",
"event_date": "2026-03-01",
"customer_id": "cust-0512",
"subscription_id": "sub-0891",
"amendment_type": "add_on",
"mrr_change": 800.00,
"prior_plan": "Analytics Pro",
"new_plan": "Analytics Pro + Automation",
"prior_quantity": null,
"new_quantity": null
}
5. Reactivation
A previously churned customer returns with a new subscription.
{
"event_class": "Revenue_Event",
"event_id": "rev-003",
"event_date": "2026-02-20",
"customer_id": "cust-0291",
"subscription_id": "sub-2050",
"event_type": "reactivation",
"mrr_change": 750.00,
"currency": "USD",
"prior_mrr": 0.00,
"new_mrr": 750.00
}
6. Onboarding Engineering Cost
Implementation team spends 40 hours onboarding a new enterprise customer.
{
"event_class": "Cost_Event",
"event_id": "cost-001",
"event_date": "2026-02-05",
"cost_centre": "Implementation",
"amount": 6000.00,
"currency": "USD",
"cost_type": "personnel",
"customer_id": "cust-1042",
"description": "40 hrs implementation engineering @ $150/hr"
}
7. Implementation Labour (Shared)
Marketing spend on demand generation — not attributable to a single customer.
{
"event_class": "Cost_Event",
"event_id": "cost-002",
"event_date": "2026-02-28",
"cost_centre": "Marketing",
"amount": 45000.00,
"currency": "USD",
"cost_type": "services",
"customer_id": null,
"description": "February demand generation campaigns"
}
8. Retention Success Servicing
CS team effort to retain an at-risk account.
{
"event_class": "Cost_Event",
"event_id": "cost-003",
"event_date": "2026-02-12",
"cost_centre": "Customer Success",
"amount": 2400.00,
"currency": "USD",
"cost_type": "personnel",
"customer_id": "cust-0783",
"description": "16 hrs success engineering for at-risk retention"
}
9. Contract Renewal
Customer renews for another 12-month term.
{
"event_class": "Contract_Event",
"event_id": "con-001",
"event_date": "2026-03-01",
"customer_id": "cust-0350",
"subscription_id": "sub-0670",
"event_type": "renewal",
"contract_start": "2026-03-01",
"contract_end": "2027-02-28",
"tcv": 24000.00,
"acv": 24000.00,
"currency": "USD"
}
10. Health Score Drop
Customer’s health score deteriorates, triggering an at-risk state.
{
"event_class": "Lifecycle_State_Change",
"event_id": "lsc-001",
"event_date": "2026-02-18",
"customer_id": "cust-0783",
"prior_state": "healthy",
"new_state": "at_risk",
"state_category": "at_risk",
"trigger": "Health score dropped from 72 to 35"
}
Example SQL
A minimal warehouse implementation. This is illustrative, not normative — adapt to your warehouse dialect and tooling.
Schema
CREATE TABLE cel_revenue_events (
event_id VARCHAR(64) PRIMARY KEY,
event_date DATE NOT NULL,
customer_id VARCHAR(64) NOT NULL,
subscription_id VARCHAR(64) NOT NULL,
event_type VARCHAR(20) NOT NULL, -- new, expansion, contraction, churn, reactivation, renewal
mrr_change DECIMAL(12,2) NOT NULL,
currency CHAR(3) NOT NULL DEFAULT 'USD',
prior_mrr DECIMAL(12,2) NOT NULL,
new_mrr DECIMAL(12,2) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE cel_cost_events (
event_id VARCHAR(64) PRIMARY KEY,
event_date DATE NOT NULL,
cost_centre VARCHAR(64) NOT NULL,
amount DECIMAL(12,2) NOT NULL,
currency CHAR(3) NOT NULL DEFAULT 'USD',
cost_type VARCHAR(20) NOT NULL,
customer_id VARCHAR(64),
description TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Querying: MRR at End of Month
SELECT
DATE_TRUNC('month', event_date) AS month,
SUM(mrr_change) AS net_mrr_change
FROM cel_revenue_events
WHERE event_date <= '2026-02-28'
GROUP BY 1
ORDER BY 1;
Querying: New MRR in February
SELECT SUM(mrr_change) AS new_mrr
FROM cel_revenue_events
WHERE event_type = 'new'
AND event_date BETWEEN '2026-02-01' AND '2026-02-28';
Relationship to Existing Data Model
The GASP Standard defines a three-level entity hierarchy in Definitions:
| Entity | CEL Relationship |
|---|---|
| Customer | customer_id on all event classes. A customer churns when their last active subscription generates a churn Revenue_Event. |
| Subscription | subscription_id on Revenue_Event, Contract_Event, and Amendment_Event. MRR lives here. |
| License | Not directly in the CEL. Licenses are derived from subscriptions. Seat counts appear on Amendment_Event as prior_quantity / new_quantity. |
The CEL does not replace the entity model. It records events that happen to entities. The entity model defines the nouns; the CEL defines the verbs.
Scope Boundaries
The CEL covers:
- All recurring revenue changes (new, expansion, contraction, churn, reactivation, renewal)
- Costs attributable to commercial functions (S&M, COGS, implementation, support)
- Contractual milestones (new, renewal, amendment, cancellation)
- Mid-term subscription changes
- Customer lifecycle state transitions
The CEL does not cover:
- Product usage events (page views, feature clicks, API calls) — these belong in a product analytics layer
- GAAP revenue recognition events — these belong in the accounting ledger
- HR events (hiring, termination) — unless directly costed as
Cost_Event - Infrastructure events (deployments, incidents) — these belong in engineering observability
The CEL is scoped to the same domain as the rest of the GASP Standard: subscription-based SaaS with recurring revenue. Usage-based pricing, marketplace transactions, and services-only revenue are out of scope.
What Comes Next
The CEL provides the atomic events. The Attribution Taxonomy Layer (ATL) provides the tags that make dual-lens metrics possible — classifying each event by lifecycle stage, expansion type, and cost function. Together, CEL + ATL form the foundation for Operating (O) and Market (M) metric forms — giving ops teams their internal economic truth and boards the investor-comparable numbers, computed from the same events.
GASP Standard v1 · Last updated