Skip to main content
Your progress
0 of 7 lessons complete0%
T3 / M3.5 / L7 OF 7 / Architect TIER / 9 min

Unattributed spend and invoice reconciliation

Outcome

By the end of this lesson, you will be able to explain why org totals include a residual that showback views deliberately do not, locate unattributed spend, and reason about the CUR 2.0 source and credit handling.


TierArchitect
JTBD”Make our headline number reconcile with the invoice, without pretending we can attribute what we cannot.”
PersonasFinOps Lead · Finance Partner · Engineering Leader
PrerequisitesM3.5.L1 · T0.M0.4 (rack rate vs billing cost)
Time9 minutes
Bloom verbExplain (Understand), Locate (Apply), Reason (Evaluate)

1. Concept

Some billing line items never match a discovered resource, and no amount of tagging discipline changes that.

Terminal window
WHAT NEVER MATCHES
networking and data-transfer charges
taxes and fees
AWS rows carrying no resource id at all
services ZopNight does not discover
AWS history older than 14 days, where only
service-level granularity exists

These are stored under a sentinel resource id of the form svc:<service>[:<region>], so they are kept rather than discarded.

The residual, and where it is added

Terminal window
FOR BILLING-ENABLED ORGS, Summary and Trends add a
PER-DATE RESIDUAL on top of the resource-level totals:
max(0, billed - captured)
It appears as its own field and is INCLUDED in the
headline total, so the headline RECONCILES WITH THE
INVOICE instead of silently under-reporting.

The max(0, ...) matters: where captured exceeds billed for a date, the residual is zero rather than negative. It is a floor, not a signed correction.

Where it is deliberately NOT added

This is the design decision that makes showback trustworthy, and it is worth being able to state.

Terminal window
ADDED (org totals)
Summary, Trends, per-provider summary, and the
internal admin dashboard's cost surfaces
NOT ADDED (showback views)
Breakdown, Snapshots, Teams, Tags, K8s
BECAUSE: SHOWBACK IS NOT THE INVOICE.
Unattributed spend has no resource to attribute, so
folding it into a team or tag view would mean inventing
an owner for spend that has none.
Terminal window
ALSO NOT ADDED: RBAC-SCOPED VIEWS.
A team-scoped user's view skips the residual entirely,
because the spend has no resource to scope by. Adding it
would show a scoped user spend that is not theirs and
that they cannot drill into.

So two numbers in the product disagree, legitimately, and you can now say why. The org total reconciles with the invoice. The team view attributes what can be attributed. Knowing that difference is what lets you defend both figures in the same meeting.

It is folded everywhere consistently

The residual is applied to every org-total surface so they cannot disagree with each other: the summary, the trends, the per-provider breakdown (where billing_records carries a provider, making it attributable at that level), and the internal admin dashboard.

It also rides the gRPC contract, so the MCP get_costs tool can label the invoice-reconciliation component rather than reporting an unexplained inflated total to an agent.

Two data-handling details worth knowing

Terminal window
COLLISION MERGING
Unmatched records collapsing onto the same sentinel id
are MERGED (summed) BEFORE UPSERT. Without that, the
unique key would keep only the last colliding row and
the residual would under-count.
NEGATIVE LINE ITEMS
Credits and refunds are stored and overlaid AS-IS,
negative. They are not filtered out.
An FX conversion failure, NOT a negative sign, is the
only thing that leaves a cost column null.

That second point is the one that answers “why is our total lower this month than the sum of our resources”: a large credit is a real negative line item and it belongs in the total.

The CUR 2.0 source, and what it changes

Terminal window
OPT-IN. AWS accounts whose credentials carry the export
bucket and name have per-resource billing read from the
customer's own Data Exports (CUR 2.0) gzip-CSV in S3,
INSTEAD OF Cost Explorer.
WHAT IT BUYS
1. It LIFTS THE 14-DAY RESOURCE-LEVEL LIMIT. Backfill
stays at resource granularity for the export's
whole history, rather than collapsing to
service-level beyond 14 days.
2. It ingests EVERY invoice line-item type: taxes,
fees, credits, refunds, reservation and
savings-plan rows.
Unmatched rows still flow to the svc: sentinel as usual.

The first point is the one that changes reporting materially. Without CUR 2.0, anything older than 14 days on AWS has no resource attribution available at all, so historical showback simply cannot be reconstructed. With it, it can.

Terminal window
FAILURE BEHAVIOUR
S3 access denials surface as `no_permission` in
billing sync status.
Other export errors FAIL THE SYNC EXPLICITLY rather
than silently degrading to the 14-day API.

That last clause is the important guarantee: a broken CUR export does not quietly become a lower-fidelity data source that nobody notices. You get an error, not a downgrade.

GCP is net of credits

Terminal window
The BigQuery export's cost column is PRE-CREDIT.
The daily per-resource and service-level queries fold
the (negative) credit amounts into each row with
NUMERIC-exact math, so committed-use, sustained-use and
promotional discounts are reflected the way the invoice
bills them.
Credit-only rows (zero cost, nonzero credits) are NOT
dropped.

A GCP figure that ignored credits would overstate spend by the entire value of the customer’s CUD and SUD discounts, which is exactly the population most likely to be looking closely.


2. Demo

Reconciling a month, and explaining a discrepancy that was not one:

Terminal window
THE CHALLENGE
"Your Teams report totals $412,000. Your Summary says
$448,700. Which one do we take to the board?"
BOTH. FOR DIFFERENT QUESTIONS.
Summary $448,700
resource-attributed $412,000
unattributed residual $36,700
data transfer $19,400
taxes and fees $8,100
NoResourceId rows $6,200
undiscovered services $3,000
AWS invoice for the month $448,700 EXACT
Teams report $412,000
Every dollar attributed to an owning team.
The $36,700 is ABSENT BY DESIGN: it has no resource,
so it has no team.
THE ANSWER GIVEN
"Summary for the board, because it reconciles with the
invoice. Teams for the chargeback conversation,
because every dollar in it has an owner. The $36,700
gap is data transfer, taxes and untagged services,
and inventing an owner for it would make the
chargeback dishonest."
That answer was accepted and the two-number reporting
became standard.
THE SECOND FINDING
$19,400 of data transfer in one month was itself worth
investigating. It had been invisible while the team
looked only at the Teams report, because it never had
a resource to appear against.
Unattributed spend is not just an accounting residual.
It is frequently where the un-owned waste lives.
THE CUR 2.0 DECISION
They wanted per-resource history for the previous
year. On Cost Explorer, anything older than 14 days
has only service-level granularity, so it could not be
reconstructed.
They enabled the CUR 2.0 export. Backfill then carried
resource granularity across the whole export history,
and taxes, fees, credits and savings-plan rows came in
as line items instead of being absent.
One caution they were given: if S3 access is later
revoked, the sync FAILS EXPLICITLY as no_permission
rather than quietly falling back to the 14-day API. An
error is the intended outcome, not a silent downgrade.

3. Hands-on (6 min)

Terminal window
1. Compare, for last month:
Summary total $______
Teams report total $______
Difference $______
That difference is your unattributed residual.
2. Break the residual down. What is in it?
data transfer $______
taxes and fees $______
no-resource-id rows $______
undiscovered services $______
3. Is the largest component something you could act on?
______________________________________________
(Data transfer usually is.)
4. Compare Summary against the actual invoice:
Summary $______
invoice $______
Do they reconcile? Y / N
If not, check whether billing sync is complete for
every account.
5. On AWS: are you using Cost Explorer or a CUR 2.0
export? ____________________
If Cost Explorer, note that per-resource history
older than 14 days is not reconstructable.

4. Knowledge check

Q1

Why is the unattributed residual added to Summary and Trends but not to the Teams or Tags views?

A. Because showback is not the invoice
B. Because showback views are cached and would be stale
C. Because the residual is provider-level and teams are cross-provider
D. Because it would double-count against resource-level rows

Show answer

Correct: A. Unattributed spend has no resource to attribute, so folding it into a team or tag view would mean inventing an owner for spend that has none. Org totals reconcile with the invoice; showback attributes what can be attributed. Views limited to one team skip it too, for the same reason. You cannot filter by a resource that does not exist, so including it would show somebody spend that is not theirs and that they cannot open.

Q2

An AWS org enables the CUR 2.0 export instead of Cost Explorer. The most significant reporting change:

A. The costs become amortized rather than unblended right across the whole of the commitment term in the export
B. The 14-day resource-level limit is lifted, so backfill carries resource granularity across the export’s whole history
C. The unmatched rows stop appearing in the unattributed residual bucket altogether from then on
D. The credits are excluded from the reported total from that point onward in every report

Show answer

Correct: B. On Cost Explorer, anything older than 14 days has only service-level granularity, so historical per-resource showback simply cannot be reconstructed. It also ingests every invoice line-item type including taxes, fees, credits, refunds and reservation rows. Unmatched rows still flow to the svc: sentinel as before, so C is wrong.

Q3

A GCP org’s monthly figure would be materially overstated if credits were ignored. How are they handled?

A. Credits are applied as a single monthly adjustment at the organisation level, so a per-resource view never sees them at all and the monthly total is already net
B. The daily per-resource and service-level queries fold the negative credit amounts into each row with exact numeric math, because the BigQuery export’s cost column is pre-credit
C. Credits are excluded entirely, and the invoice is used instead of the export whenever a reconciliation against finance is actually needed for the month
D. GCP costs are stored pre-credit and then adjusted at read time, so the stored rows are gross and every query has to subtract the credits out for itself

Show answer

Correct: B. Credit-only rows carrying zero cost and nonzero credits are not dropped. Ignoring them would overstate spend by the entire value of the customer’s committed-use and sustained-use discounts, which is exactly the population most likely to be checking the numbers closely.


5. Apply

Report two numbers routinely: the org total that reconciles with the invoice, and the attributed total that has an owner for every dollar. Explaining the gap once establishes both; letting someone discover it undermines both.

Then look at what is actually in your residual. Data transfer with no resource to appear against is invisible on every showback view and is frequently the largest single un-owned line in the estate.


Glossary terms touched

Unattributed spend · Sentinel resource id · CUR 2.0 · Net of credits


Start with the bill.

Foundations takes about five hours. The first lesson is nine minutes.

Open curriculum. No login. No paywall. 290 lessons across 7 courses, three publicly verifiable credentials. Read it on the train, take the exam on a Saturday, list the credential on your résumé Monday.

5h median time to finish Foundations
0 logins, paywalls, or marketing forms
open curriculum, public credential verifier
Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console· Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console·