API Contracts

From v1.0.0 (2026-07-24) the MMM Framework follows semantic versioning strictly. This page enumerates the surfaces that promise means something for: the public Python API, the model spec keys, the data formats, the persisted model formats, the sessions-store schema, the estimand schema, and the REST API. Each is pinned by a CI gate — a breaking change fails the build rather than shipping silently.

Reading this page. frozen surfaces only change in a major version (additions are allowed in minors and noted in the changelog). internal surfaces are documented for transparency but carry no compatibility promise. Modules flagged Experimental in the stability grid may change in minor releases.

Python API surface frozen

The lean core package (pip install mmm-framework) exposes the modeling surface; the complete reference is the Sphinx API docs. The load-bearing entry points:

ImportRole
mmm_framework.BayesianMMMThe core model: build, fit() (NUTS/numpyro; approximate map/advi/fullrank_advi/pathfinder/laplace; exact smc), predict(), sample_channel_contributions(), predict_under(), evaluate_estimands()
mmm_framework.MFFLoaderMFF long-format loading + validation → PanelDataset
mmm_framework.Dataset / load_datasetRole-tagged wide-table contract (non-MFF entry point)
mmm_framework.load_exampleBundled example datasets ("national", "geo")
mmm_framework.builders.*Fluent config builders (ModelConfigBuilder, MediaChannelConfigBuilder, …)
mmm_framework.serialization.MMMSerializerModel persistence (see persisted formats)
mmm_framework.analysisMMMAnalyzer, MarginalAnalysisResult, contribution summaries
mmm_framework.estimandsThe Estimand schema + built-in registry (see below)
mmm_framework.reporting.MMMReportGeneratorClient HTML readouts (Augur), interactive + pre-fit readouts
mmm_framework.platform.sessionsThe sessions store (projects, experiments registry, artifacts)
mmm_framework.agents.fittingbuild_model(spec, …) / build_and_fit(…) — the spec-driven build path (importable without the [agents] LLM extra)

Config enum values are contracts too — they are serialized into specs and saved configs. Frozen value sets include FitMethod (nuts, map, advi, fullrank_advi, pathfinder, laplace, smc), SaturationType (logistic, hill, michaelis_menten, tanh, none) and AdstockType (geometric, delayed, weibull, none). New values may be added in minors; existing values never disappear outside a major.

Model spec keys frozen

The agent (and any programmatic caller of build_model) describes a model as a JSON spec. The authoritative write-side contract is the consumed-paths registry (agents/fitting.py::unconsumed_spec_path / unconsumed_prior_path): a write the builder would silently ignore is rejected up front with an explanation, and every key the builder reads is registered. The registry — not any TypedDict — is the contract.

Top-level keys

KeyType / valuesMeaning
kpistrKPI variable name (required)
kpi_levelnational | geoKPI granularity
time_granularityweekly | daily | monthlyCadence (default weekly)
media_channelslist of dictsPer channel: name, adstock{type, l_max}, saturation{type}, measurement_unit (spend|impressions|clicks|other), spend_column/cpm/cpc. Written per-field (media_channels.<name>.<field>)
control_variableslist of dictsname + causal role (confounder | precision)
specificationadditive | multiplicativeFunctional form
trenddicttype: linear|piecewise|spline|gaussian_process|none + knots/changepoints
seasonalitydictFourier orders {yearly, monthly, weekly}; 0/absent = off
likelihooddict{family, link?, params?}, default normal/identity
media_prior_modecoefficient | roiDefault media-prior parameterization (agent default roi)
priorsnested dictSee prior paths
inferencedictmethod (validated), chains, draws, tune, target_accept, random_seed, metrics_draws
datasetschema dictRole-tagged (non-MFF) load path — a DatasetSchema dict
dataset_pathinternalSession-state field set by the upload / Data Studio flows, not a registry-writable spec key
estimandslistDeclared Estimand dicts, realized at fit
experiments / experiment_idslistIn-graph calibration measurements / staged registry ids
garden_ref / model_paramsdictModel Garden class reference / bespoke CONFIG_SCHEMA params
latent_factorslistStructural-model shared factors (structural specs only)
skip_quality_gateboolBypass the pre-fit data-quality gate
dag_model_type / dag_specinternalStamped by the DAG builder; not registry-writable

Prior paths

Plain (non-DAG) models:

PathShape
priors.media.<ch>.{coefficient, adstock_alpha, adstock_theta, saturation_kappa, saturation_slope}PriorConfig dict {distribution, params}
priors.media.<ch>.roi{median|mu, sigma} — LogNormal on raw ROI
priors.controls.<cv>.{coefficient, allow_negative}PriorConfig / bool
priors.intercept.{mu, sigma}, priors.media_default.{roi_mu, roi_sigma}floats
priors.seasonality.*, priors.trend.*floats (per-component scales)

DAG-routed extension models (nested_mmm, structural_nested_mmm, multivariate_mmm, combined_mmm) reject the plain groups and accept priors.mediator.<name>.*, priors.outcome.<name>.* and priors.cross_effect.<src>__<tgt>.* instead — names typo-checked against the DAG's variables.

Data contracts frozen

MFF long format

One row per (dimension-tuple, variable). Eight required columns (names remappable per config): Period, Geography, Product, Campaign, Outlet, Creative, VariableName, VariableValue. Values are parsed forgivingly (currency symbols, parenthesized negatives, both decimal conventions, null tokens); anything unparseable is a hard MFFValidationError with samples. Weekly cadence anchors the completeness grid to the start date's own weekday; gaps warn at load and block at fit via the data-quality gate (unless skip_quality_gate). See Data Requirements.

Role-tagged Dataset

The wide-table entry point: one tidy table + a DatasetSchema (schema_version "1.0") binding columns to DatasetRoles — target, predictor, control, indicator, group, time, offset, weight, trials, auxiliary. The first four map 1:1 onto the MFF roles; the rest serve non-MMM model families. Both role enums are frozen — they are serialized inside saved configs.

Persisted model formats frozen

MMMSerializer (format version 1.1) writes an atomic directory:

FileContent
metadata.jsonSelf-description: model version + format_version, shapes, channel/control names, model_kind, fit_method + approximate, declared estimands (+ schema version), experiments, garden_ref/model_class_qualname, dataset schema. Extended models add model_flavor: "extended"
configs.jsonFull ModelConfig/TrendConfig/MFFConfig dumps (JSON-readable ground truth)
scaling_params.jsony_mean/y_std/media_max/control scaling
trace.nc(.gz)ArviZ posterior (netcdf; h5netcdf engine)
model.pklExtended flavor only (cloudpickle, graph/trace stripped)

run_metadata.json (written per fit by the agent's build_and_fit) additionally embeds the full normalized spec, diagnostics, metrics and paths. internal — it contains absolute local paths and is not a shareable bundle.

Sessions store internal schema, stable semantics

Platform state lives in one SQLite file (MMM_SESSIONS_DB, default package-local under mmm_framework/platform/): 22 platform tables (sessions, artifacts, projects, experiments, run_metrics, garden models, preferences, budget plans, learning programs, …) plus 5 auth tables (organizations, members, tokens, invites, resets). The schema may gain columns in minors (additive migrations only; downgrade unsupported); the semantics exposed through the REST API are the contract.

Experiment lifecycle

The experiment registry's state machine is frozen: draft → planned → running → completed → calibrated, with abandoned reachable from any active state (and legacy terminal cancelled). Illegal transitions are rejected (HTTP 409); every hop and readout write appends to an audit history; changing a calibrated readout requires overwrite_calibrated=true.

Estimand schema frozen

Estimand (schema version 1.0) is a declarative, serializable counterfactual quantity: reduce( op( quantity | intervention, baseline ) ) / normalizer, realized as mean + HDI with a pinned Realization (point rule + HDI method) that keeps every number bit-stable against its pre-1.0 legacy implementation. The built-in names are contracts (specs and saved models reference them): contribution_roi, counterfactual_roi, marginal_roas, contribution, plus demonstrators awareness_lift and cost_per_conversion and the parametric constructors (latent_scalar, fit_index, factor_loading). Note contribution_roi (in-graph) and counterfactual_roi (zero-out counterfactual) are different numbers by design.

REST API frozen

The server package (mmm-framework-server) exposes 165 paths / 200 operations, fully documented on the REST API reference (generated from the live OpenAPI schema; machine-readable at shared/openapi.json). The (method, path) set is snapshot-pinned: removals or renames are major-version events.

The CI gates

Every contract above is enforced by a test:

  • tests/test_api_contracts.py — REST route snapshot (tests/contracts/rest_routes.json), public Python symbols, consumed spec/prior paths, frozen enum values, built-in estimand names.
  • tests/test_lean_imports.py — the lean-core invariant: the whole core import surface works with the web/LLM stacks import-blocked.
  • tests/test_estimands.py — bit-stability of estimand arithmetic vs the legacy implementations.
  • tests/test_docs_snippets.py — every Python snippet on this site references real APIs.

Stability caveats

Related: Architecture & flow diagrams · REST API reference · Versioning & changelog · Getting started