Cost of change
Compare each Terraform change's planned monthly cost with the cost measured once it is applied, and post the estimates from CI through the Public API.
Cost of change sets what an infrastructure change was estimated to cost a month beside what it measured once applied. CI posts the estimate of each pull request or named plan, such as the one Infracost makes, through the Public API and records the day the change is applied. Once the 30 days from that day have cost data, Costfluent finds each resource the change touched in your billing data by its ids and measures it. An estimate is a statement about a plan, not cost: Costfluent stores its figures as posted, prices nothing itself, and counts it in no report, budget, forecast or anomaly. Open Cost of change under Planning.
Before you start
Included in the Pro, Business and Custom plans. Compare plans
- Every team role can view the page and open its estimates.
- Estimates arrive only through the Public API, posted with an API token that holds Submit cost estimates for the workspace; see Post an estimate.
- An estimate is measured in the cost of the connections the workspace uses and their accounts; see Manage connections.
Read the list

Each row is one estimate, its figures in the currency it was posted in:
- Change names the pull request, as the repository and its number such as
acme/infrastructure#42, or the plan the estimate was posted for, with the estimator beneath it. - Planned is the estimated change in monthly cost, after the change less before it, over the resources the estimator priced, with a plus sign when the change costs more. When some were not priced, it counts them beneath, as in "… resources not priced", rather than reading them as free.
- Actual is the measured change in monthly cost once the estimate is reconciled. Until then it reads "Not applied yet" for a proposed estimate and "Awaiting cost data" for an applied one.
- Variance is the actual change less the planned one: above zero, the change costs more a month than planned.
- State is where the estimate stands:
| State | The estimate |
|---|---|
| "Proposed" | Was posted and is not applied yet. Each post for the same pull request or plan replaces it. |
| "Applied" | Records the day the change was applied and waits for its cost data. |
| "Reconciled" | Was measured, so Actual and Variance hold figures. |
| "Inconclusive" | Could not be measured, and says why (Inconclusive estimates). |
| "Expired" | Was not applied within 90 days of its last post. |
Open an estimate
Select an estimate's change to open it. Its page shows the Planned change, the Actual change and the Variance, why the estimate closed as it did once it has, and, while it is applied, "Measured once cost data covers the 30 days after the apply". Below them are its Source, its Commit, the day it was Applied on and, when it was posted with an https address, "Open the change", a link to the pull request or plan. Under Resources is one row for each resource:
- Resource is its Terraform address and type, and Change what the change does to it: "Created", "Updated", "Replaced" or "Deleted".
- Planned is the monthly cost the estimator gave the resource before and after the change. A created resource has no cost before, and a deleted one none after. A cost the estimator could not price shows "Not priced", never zero, and leaves the resource out of the planned totals.
- Actual is the monthly cost the reconciliation measured before and after the change, for a resource it found; for one it did not, it reads "Not found" or "No cost found".
- In the bill is how the reconciliation found the resource:
| In the bill | The resource |
|---|---|
| "Found" | Was found in the bill, in every window where the estimator gave it a cost other than zero. Its measured cost counts in the estimate's actual totals. |
| "Not found" | Has no cost under its ids in a window where the estimator gave it a cost other than zero, or has no ids. One such resource makes the estimate "Inconclusive". |
| "No cost" | Was planned to cost nothing, or was not priced, and has no cost in the bill. It does not hold the reconciliation back. |
A deleted resource whose cost stopped at the apply is "Found" at zero after it; the estimate is reconciled only while the accounts that carried its cost still report cost (Inconclusive estimates).
How an estimate is reconciled
Costfluent checks the open estimates once a day.
- The windows. The apply date is the day the apply was recorded, in the workspace's time zone. Costfluent measures the 30 days before it and the 30 days from it, the apply date included. The days before count only for resources the change updates, replaces or deletes, since a created resource had no cost before.
- The resources. Before the change, an updated, replaced or deleted resource is found under the ids posted with the estimate. After it, a created or replaced resource is found under the ids recorded with the apply, an updated one under its same ids, and a deleted one is expected to show nothing under its old ids. Ids are compared with the resource ids in your billing data ignoring case, so a resource billed under another id is "Not found".
- The cost. Actual figures are amortized cost, with commitments such as reserved instances and savings plans spread over the usage they cover (Billed or effective cost), so usage a commitment covers does not read as free.
- A month. A window's cost is divided by its 30 days and multiplied by a month of a year over twelve, 365/12 days. That is the 730-hour month Infracost prices by, so planned and actual figures stand on the same scale.
- The wait for data. An applied estimate is measured once the 30 days from the apply date have passed and the cost data of every connection with cost in its windows runs through the last of those days. Until then it stays "Applied", for at most 90 days from the apply.
- Coverage. A window the plan says should hold cost counts only with cost on at least 24 of its 30 days, 80%, across the estimate's resources. A window with cost on fewer days closes the estimate "Inconclusive"; one with none leaves its resources "Not found".
- Currency. Actual figures are in the estimate's currency. Cost billed in one other currency is converted at the workspace's exchange rate on the apply date, by the method and source its reports convert with (Currency settings): one rate for both windows, so a move in the rate between them does not read as a change in cost.
- The totals. The planned totals count the priced resources, and the actual totals the resources "Found". A resource the estimator did not price but the bill shows counts in Actual, so its cost reads as variance.
Planned and actual stand on different bases, which each estimate's page states. Planned is the estimator's list price; actual is amortized cost after your discounts. A negotiated discount, or a commitment that covers a resource the change creates, therefore reads as variance, even when the change went exactly as planned.
Inconclusive estimates
An inconclusive estimate keeps its planned figures and says why it could not be measured:
| Reason | When |
|---|---|
| "A resource planned to cost something was not found in the bill." | A resource the estimator gave a cost other than zero in a window has no cost under its ids there, or has no ids, such as a created resource the apply named no ids for. |
| "Its cost was billed in more than one currency." | The cost found in the two windows came in more than one billing currency. |
| "Its cost was billed in a currency the workspace has no exchange rate for." | The cost came in one currency other than the estimate's, and the workspace's rates hold no rate from it into the estimate's currency on the apply date. |
| "A window had cost on fewer than 24 of its 30 days." | A window the plan says should hold cost has cost on some of its days, but on fewer than 24. |
| "A deleted resource’s account stopped reporting cost, so its deletion could not be told apart." | A deleted resource shows no cost after the apply, and an account that carried its cost before reports cost of any kind on fewer than 24 of the 30 days from the apply, so its deletion cannot be told from cost that stopped arriving. |
| "Cost data did not cover the 30 days after the apply within 90 days." | 90 days after the apply, the cost data still did not run through the 30 days from it. |
Post an estimate
- Create an API token for the workspace that holds only Submit cost estimates, as API authentication describes, and keep it in your CI's secrets.
- On every push to the pull request, post the estimate to
POST /v1/cost-estimates: the workspace's ID, therepositoryandpullRequestnumber, or aplanname instead, the estimator assource, thecurrency, and one entry underresourcesfor each resource. Each names its Terraformaddress, itstype, itschange(create,update,replaceordelete) and itsmonthlyCostBeforeandmonthlyCostAfteras decimal strings, ornullwhere the estimator could not price it. An updated, replaced or deleted resource adds up to threeresourceIdsit had before the change. - After the change is applied, record it with
POST /v1/cost-estimates/apply, naming the same repository and pull request, or plan, and underresourcesthe one to three ids each created or replaced resource got.
An estimate, saved as estimate.json:
Code
Post it:
Code
Record the apply, with the ids the new database got:
Code
The first post for a pull request or plan creates its estimate and answers 201; each later post replaces it whole and answers 200, until the change is applied. Both calls answer only the estimate's id, its state and, once it is applied, appliedOn, never its figures, so a token holding only Submit cost estimates reads no cost. A token that holds viewCosts lists estimates through GET /v1/cost-estimates and reads one with its resources and measured figures through GET /v1/cost-estimates/{estimateId}; see the API reference.
How it behaves
- Once an estimate has left "Proposed", a post for its pull request or plan answers
409; post the next change under its own pull request or plan name. An apply repeated with the same ids changes nothing; with other ids it answers409. An apply for a pull request or plan the workspace holds no estimate for answers404. - An apply records the day it is made, in the workspace's time zone. It takes ids only for the resources the estimate creates or replaces, and ignores the other addresses it names, such as free resources the estimator left out.
- A proposed estimate expires 90 days after it was last posted, so a pull request that is still being pushed to keeps its estimate.
- An estimate names 1 to 2,000 resources, each with at most three ids of at most 512 characters, and a request body is at most 5 MiB. The
urlmust be an https address; Costfluent links it and never fetches it. - A workspace holds at most 1,000 estimates that are "Proposed" or "Applied". A new estimate past that is refused with
422, while a post for an open one still replaces it. - An estimate posted with a
taginstead of resource ids, for a change that carries that tag on every resource it creates or changes, is measured on the tag as a whole: its totals get actual figures, and its resources none.