# Forecast, CAC, finance & modeling

**Read-only tools.** Forecasts, Signal CAC, accounting data, marketing-mix-model results and deterministic report templates.

| Tool | Purpose |
|---|---|
| [`get_forecast`](#get_forecast) | Performance forecast |
| [`get_signal_cac`](#get_signal_cac) | Signal CAC by channel |
| [`get_finance`](#get_finance) | Financials (books) |
| [`get_mmm_results`](#get_mmm_results) | Marketing mix model results |
| [`run_report`](#run_report) | Run a report template |

## `get_forecast`

**Performance forecast**

Projected daily performance from the forecast mart (BigQuery AI.FORECAST fitted over recent actuals), organization-wide or filtered to one product, network, account or campaign. mode=summary (default) returns projected totals for the window; mode=daily returns the projected per-day series.
These are MODEL PROJECTIONS, not observed data: label them as forecasts, never add them into an actuals total, and do not present a projected value as something that happened. The mart holds a short FORWARD horizon (about a week ahead, rebuilt nightly), and the window simply selects which of those days come back — a window entirely in the past returns nothing, which is expected rather than a failure. Omit both dates for the default window: the last 30 complete days ending yesterday.

**Posture:** read-only · **Module gate:** `forecast` · **REST:** `POST /api/v1/tools/get_forecast`

| Argument | Type | Required | Notes |
|---|---|---|---|
| `account_id` | string | no | Filter to one ad account id. (length 1–128) |
| `ad_network_id` | string | no | Filter to one network: facebook_ads \| google_ads \| tiktok_ads \| linkedin_ads \| bingads. (length 1–64) |
| `campaign_id` | string | no | Filter to one campaign id. (length 1–128) |
| `end_date` | string | no | Window end, YYYY-MM-DD (inclusive). (pattern `^\d{4}-\d{2}-\d{2}$`) |
| `mode` | `summary` \| `daily` | no | summary (default) = projected totals for the window; daily = the projected per-day series. |
| `product_id` | string | no | Filter to one product id from the org context. (length 1–128) |
| `start_date` | string | no | Window start, YYYY-MM-DD (inclusive). Omit both dates for the default window: the last 30 complete days ending yesterday. (pattern `^\d{4}-\d{2}-\d{2}$`) |

## `get_signal_cac`

**Signal CAC by channel**

Cost per attributed conversion from Signal's spend-attribution mart, by date, attribution model, channel and campaign: spend, credited_conversions and CAC. Use it for "what does a conversion cost by channel" once Signal is tracking conversions.
Rows exist ONLY where credited conversions join ad spend on matching campaign names, so an empty result is a valid state — it means nothing has joined yet, not that CAC is zero and not that the tool failed. Because credit is split by attribution model, always state which model a CAC figure comes from. Requires the Signal spend/CAC mart: without the entitlement this returns a plan refusal rather than empty rows.

**Posture:** read-only · **Module gate:** `signal_cac` · **REST:** `POST /api/v1/tools/get_signal_cac`

| Argument | Type | Required | Notes |
|---|---|---|---|
| `start_date` | string | no | Since date, YYYY-MM-DD (default: 90 days ago). The window always runs from here through the latest available day; there is no end_date. (pattern `^\d{4}-\d{2}-\d{2}$`) |

## `get_finance`

**Financials (books)**

Financials read straight from this organization's books (QuickBooks or Xero). Pick the report: kpis (monthly revenue, COGS and margins), pl (P&L rows by account), balances (balance-sheet lines including cash), ar_aging (receivables by age bucket), ap_aging (payables by age bucket), or marketing_bridge (ad spend against revenue: MER, blended CAC, spend as a percentage of revenue).
since sets the earliest month, default 13 months back. These are ACCOUNTING numbers on the organization's own calendar and recognition rules — they will not match ad-platform revenue columns, and marketing_bridge is the intended place to compare the two. Requires the Margin module and a synced accounting connector; without it you get a plan refusal, and with no synced books an explicit "no accounting data" answer.

**Posture:** read-only · **Module gate:** `finance` · **REST:** `POST /api/v1/tools/get_finance`

| Argument | Type | Required | Notes |
|---|---|---|---|
| `report` | `kpis` \| `pl` \| `balances` \| `ar_aging` \| `ap_aging` \| `marketing_bridge` | **yes** | Which financial report to return — see the tool description for each. |
| `since` | string | no | Earliest month to include, YYYY-MM-DD (default: 13 months back). (pattern `^\d{4}-\d{2}-\d{2}$`) |

## `get_mmm_results`

**Marketing mix model results**

The latest COMPLETED marketing-mix-model run per outcome KPI and model type (Meridian and/or Robyn): model fit (R², NRMSE), per-channel contribution share, ROI and marginal ROI, plus any stored budget-optimization output. outcome_variable filters to one KPI.
These are MODEL ESTIMATES over a training window, not measured attribution: report them as modeled contribution, quote the fit statistics alongside any ROI you cite, and note the run's completed_at — a stale run describes an old media mix. Contribution shares answer "what is driving outcomes", while get_channels answers "what did each channel actually spend and produce". Requires Compass.

**Posture:** read-only · **Module gate:** `compass` · **REST:** `POST /api/v1/tools/get_mmm_results`

| Argument | Type | Required | Notes |
|---|---|---|---|
| `outcome_variable` | string | no | Filter to one outcome variable (the KPI the model was trained to explain). Omit to get every outcome the org has trained. (length 1–128) |

## `run_report`

**Run a report template**

Run a versioned, deterministic report template. The same template over the same window always produces identical tables — the fetches are fixed and the math is precomputed — so prefer this over assembling the same story by hand from several tools.
Templates: performance_recap (this period against the previous one, top campaigns, biggest movers), budget_pacing (expected vs actual spend for the current budget period), signal_attribution (credit by channel and model plus CAC), creative_review (winners and losers with attribute insights), finance_snapshot (P&L KPIs plus the marketing bridge). Each template reads the same gated tools you would call yourself: a multi-source template (performance_recap, creative_review, finance_snapshot) fills the sections it can and leaves the rest empty, while a single-source one (budget_pacing) comes back as a plain failure carrying the reason — for example "no budget period covers this date".
The payload is designed to be rendered as tables: summarize the headline movements and name the window, do not restate every row. Omit both dates for the default window: the last 30 complete days ending yesterday.

**Posture:** read-only · **Module gate:** none (needs only the agent entitlement) · **REST:** `POST /api/v1/tools/run_report`

| Argument | Type | Required | Notes |
|---|---|---|---|
| `template` | `performance_recap` \| `budget_pacing` \| `signal_attribution` \| `creative_review` \| `finance_snapshot` | **yes** | Which report template to run. |
| `end_date` | string | no | Window end, YYYY-MM-DD (inclusive). (pattern `^\d{4}-\d{2}-\d{2}$`) |
| `start_date` | string | no | Window start, YYYY-MM-DD (inclusive). Omit both dates for the default window: the last 30 complete days ending yesterday. (pattern `^\d{4}-\d{2}-\d{2}$`) |
