# Savings report

> A signed statement of what an agent would save by moving to the cheapest model variant that still passes, priced from measured cost.

> **TL;DR:** On your agent's manage page, **Generate report** compares the agent you registered with the cheapest variant that passes at least as well, prices both from measured cost, and projects a monthly saving. The report is signed by badgeIA, downloads as JSON, CSV or PDF, and anyone you share it with can check the JSON file against that signature at `/verify/report/<report id>`.

A saving you can't back up is a guess. The savings report only counts cost badgeIA could actually see, and it labels where each figure came from.

Generate and download [#generate-and-download]

1. Open your agent, then **Manage**.
2. Find the **Savings report** card and choose **Generate report**.
3. Download it as **JSON**, **CSV** or **PDF**, or choose **Copy verify link**.

The card is on the manage page. On the Model Sweep chart, the owner also sees a **Download savings report** link that jumps to the same card. Both appear only when the feature is switched on for the deployment. While it is off, the card does not show.

What it compares [#what-it-compares]

* **Current** is the agent you registered: the root of the variant group. The payload calls it `baseline`.
* **Cheapest passing** is the variant with the lowest measured cost whose pass rate and pass^k (the share of tasks passed on every repeat) are at least the current agent's, within the report's tolerance. A variant must also cost strictly less than the current agent. If no variant qualifies, the report says "No cheaper variant passes yet" and shows no saving.

For each side you see pass rate, pass^k, latency (p50 and p95), cost per run and how many runs were priced.

The settings [#the-settings]

| Setting   | Default | What it does                                                                                                                                   |
| --------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| Window    | 30 days | How far back the run count reaches. Range 1 to 365 days.                                                                                       |
| Tolerance | 0       | How far a variant may fall below the current agent on pass rate and pass^k and still count as passing. `0` means no drop at all. Range 0 to 1. |
| k         | 3       | The repeat count for pass^k. Range 1 to 10.                                                                                                    |

**The pass^k gate.** A variant must also match the current agent's pass^k at `k`, within the tolerance. This only applies when the current agent has a pass^k figure at `k`, which needs tasks run enough times. If it doesn't, the report says `pass_k_gate: "unavailable"` and chooses on pass rate alone. Check that field before you quote a saving.

Where each cost figure comes from [#where-each-cost-figure-comes-from]

| Label            | Meaning                                                                         | Counts toward the saving                                |
| ---------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------- |
| metered          | Paid and measured by badgeIA                                                    | Yes                                                     |
| traced           | Priced by badgeIA from token usage in the agent's own traces                    | Yes                                                     |
| traced (partial) | Same traces, but some calls could not be priced, so the figure is an undercount | No cost. The run still counts for pass rate and latency |
| claimed          | Reported by the agent itself, not verified                                      | No. Shown as "claimed" only                             |

A variant with only claimed cost shows no measured cost and can never be the cheapest. If the current agent has no measured cost, the card says "No measured cost yet" and asks you to send traces.

Simulated runs, and runs that are neither traced nor metered, are left out of every figure. The report counts how many it left out.

The projection [#the-projection]

```text
projected monthly saving =
  (current cost per run - cheapest cost per run) x observed monthly runs
```

Observed monthly runs is how many eligible runs the current agent had in the report window, scaled to a month. It is a projection from your own recent traffic, not a forecast of future volume.

Figures for someone who can't edit the agent are rounded to two significant figures.

What is in the report [#what-is-in-the-report]

The JSON file (and the `payload` field of the API response) carries these fields. The signature covers all of them.

| Field                                     | Meaning                                                                                                                                                                                               |
| ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `report_version`                          | Always `1` for this format.                                                                                                                                                                           |
| `agent_id`                                | The agent you generated the report for.                                                                                                                                                               |
| `root_agent_id`                           | The baseline: the root of the variant group.                                                                                                                                                          |
| `generated_at`                            | When the report was generated.                                                                                                                                                                        |
| `window_days`, `pass_rate_tolerance`, `k` | The settings used.                                                                                                                                                                                    |
| `projection`                              | `owner` for full precision, `public` for rounded figures.                                                                                                                                             |
| `pass_k_gate`                             | `applied` or `unavailable`, as above.                                                                                                                                                                 |
| `baseline`                                | The current agent: `agent_id` and `agent_name`.                                                                                                                                                       |
| `variants`                                | One entry per variant: `pass_rate`, `pass_k`, `latency_p50_ms`, `latency_p95_ms`, `measured_cost_per_run_usd`, `measured_run_count`, `claimed_cost_per_run_usd`, `cost_basis`, `simulated_run_count`. |
| `cheapest_passing`                        | The cheapest passing variant, or null.                                                                                                                                                                |
| `observed_monthly_runs`                   | Eligible runs in the window, scaled to 30 days.                                                                                                                                                       |
| `projected_monthly_saving_usd`            | The projected saving, or null when there is no cheapest variant or no measured baseline cost.                                                                                                         |
| `excluded.simulated_runs`                 | Runs left out of every figure.                                                                                                                                                                        |

The CSV has one row per variant. Its `role` column reads `baseline`, `variant` or `cheapest_passing`. Text cells that start with `=`, `+`, `-`, `@` or a tab are written with a leading apostrophe, so spreadsheet programs show them as text.

Verify a report [#verify-a-report]

There are two different checks, and the verify page labels them so you cannot mistake one for the other.

* **Stored copy.** `/verify/report/<report id>` shows that badgeIA issued a report with this id for this agent at this time, and that the signed copy badgeIA holds is intact. The fingerprint (SHA-256) on that page identifies the signed copy. For a public agent the page also shows the headline figures from that copy. The heading reads **Stored copy: signature valid**, or **Stored copy: signature not valid**, or **Stored copy: we can't check this report**, with a green, red or amber mark to match. It does not tell you anything about a PDF, CSV or JSON file someone is holding.
* **Your file.** To check a file you were sent, paste or upload the **JSON** download into the **Your file** box on that page. badgeIA checks the signature on exactly what you provide and tells you whether it is valid, which agent it is for and when it was generated. A file that was edited fails: any changed figure, name, date or agent. Opening the file in a JSON tool and saving it again is not an edit: spacing, key order and whole numbers written as `0` instead of `0.0` do not affect the check. The PDF and CSV are for reading; only the JSON file carries the signature.

Once you have checked a file, its verdict is repeated in a strip directly under the page heading, so the stored copy's heading cannot be read as a verdict on your file: **Your file: signature valid** in green, or **Your file: signature not valid** in red. A file always gets one of those two: a file that cannot be confirmed, for any reason, is not valid, and the box says why. When your file's verdict differs from the stored copy's, the page says so in words. If the stored copy is valid and your file is not, it reads "The file you uploaded does not match the stored copy." A file over 1 MB is refused as too large; a savings report is far smaller. A stored copy that is valid next to a file that is not valid means the file was changed or was not issued by badgeIA; do not rely on the figures in it.

If you only have the file, or a link a supplier sent you, start at [/verify](/verify): under "Checking a savings report?" you can paste the report id or its verify link, or check the file directly in the **Your file** box. (That section appears once savings reports are switched on for your deployment.)

If the report on record names a signing key badgeIA does not publish, the **Stored copy** heading reads **we can't check this report** instead of "valid" or "not valid". That means nothing was checked. We cannot tell whether it was altered, and it is not a confirmation either. Before you rely on it, ask whoever sent you the file, or [ask badgeIA support](mailto:support@badgeia.com), to confirm it.

The **Your file** box is stricter, because the key id inside a file is written by whoever holds the file. A file that names a key badgeIA does not publish is reported as not valid, with the reason "This file names a signing key badgeIA does not publish". Do not rely on it. In the API the report-link case is `status: "unverifiable"` with `valid: null`; `valid: false` is a signature that was checked and failed, or a file that cannot be confirmed.

If the file is valid but is for a different agent, or is a different signed report from the one stored under the id in the link, the page says so and shows the verdict in amber.

API [#api]

All routes need the owner or a workspace member's session, except the verify route. All return 404 while the feature is off.

```bash
# generate, sign and store a report
curl -X POST -b "badge_session=$SESSION" \
  "https://badgeia.com/api/v1/agents/$AGENT_ID/savings-report"

# the latest stored report
curl -b "badge_session=$SESSION" \
  "https://badgeia.com/api/v1/agents/$AGENT_ID/savings-report"

# download a stored report: format is json, csv or pdf
curl -b "badge_session=$SESSION" \
  "https://badgeia.com/api/v1/savings-reports/$REPORT_ID?format=csv" \
  -o "agent-$AGENT_ID-savings.csv"

# public: is the stored copy under this id intact
curl "https://badgeia.com/api/v1/verify/report/$REPORT_ID"

# public: check a downloaded JSON file
curl -X POST -H "Content-Type: application/json" \
  --data-binary @report.json \
  "https://badgeia.com/api/v1/verify/report"
```

If the latest-report call returns 404 with "No savings report has been generated for this agent", generate one first. A 404 with "Savings report is not enabled" means the feature is off on that deployment.
