Skip to main content
Most integrations are pull: you hand CostGraph a read-only credential and it fetches your bill once a day. Push is the other direction. You send FOCUS 1.2 records to our API yourself, and no vendor credential ever leaves your infrastructure. Use it for billing data CostGraph cannot reach: an internal chargeback system, a data warehouse job, a vendor whose export you already normalise, or a private cloud nobody else bills for. Whatever you send lands beside every other provider in Cost Overview, anomaly detection, and the MCP tools.

Create the connection

  1. Open Settings -> Integrations and choose FOCUS push.
  2. Pick the tenant the charges belong to and name the connection.
  3. Copy the connection id. It looks like fpc_... and identifies this stream.
A connection is created in push mode and stays that way. Posting to a connection CostGraph syncs itself is refused with a 409.

Create the API key

  1. Open Settings -> API keys and create a key.
  2. Grant it the focus:write scope. Nothing else is required.
Install tokens are rejected on this endpoint. Use an ordinary API key.

Send the records

Every row is validated, and a row that fails validation rejects the whole batch, so nothing lands half-applied. The 422 response names the offending row index.

Request body

Row fields

Costs and quantities are strings, not numbers, so no precision is lost in JSON. Timestamps are RFC 3339.

Required on every row

Constrained when present

Optional

Everything else in FOCUS 1.2 is accepted and stored, and omitting it is fine: AvailabilityZone, BillingAccountName, BillingAccountType, CapacityReservationId, CapacityReservationStatus, ChargeFrequency, CommitmentDiscountCategory, CommitmentDiscountId, CommitmentDiscountName, CommitmentDiscountQuantity, CommitmentDiscountStatus, CommitmentDiscountType, CommitmentDiscountUnit, ConsumedQuantity, ConsumedUnit, ContractedUnitPrice, InvoiceId, InvoiceIssuer, ListUnitPrice, PricingCategory, PricingCurrency, PricingCurrencyContractedUnitPrice, PricingCurrencyEffectiveCost, PricingCurrencyListUnitPrice, PricingQuantity, PricingUnit, Publisher, RegionId, RegionName, ResourceId, ResourceName, ResourceType, ServiceSubcategory, SkuId, SkuMeter, SkuPriceId, SkuPriceDetails, SubAccountId, Tags.
Send ResourceId wherever you have one. Without it a charge can be totalled but not attributed to a thing, so it will not appear in resource-level breakdowns.
Any key CostGraph does not recognise is kept as a provider extension rather than rejected, so a dialect that carries extra columns round-trips.

Multi-tenancy with SubAccountId

One connection can carry charges for many of your own customers. Set SubAccountId on each row to the identifier of the customer the charge belongs to, and optionally SubAccountName for a human label and SubAccountType to say what kind of thing that identifier names.
SubAccountId is part of the identity of a row, alongside the billing account, provider, resource and charge period. Two customers billed for the same SKU over the same period stay separate records rather than collapsing into one, and a replayed window updates each customer’s rows in place.
SubAccountName and SubAccountType are only accepted when SubAccountId is set. A name without an id has nothing to attach to and the batch is rejected.
Send the same SubAccountId values on every push. Changing the identifier for a customer starts a new sub account rather than renaming the old one; the name is free to change.

The window and the complete flag

Each push declares the charge period it covers, and what you are claiming about it. complete: true deletes everything previously ingested for that connection inside the window and replaces it with this batch. That is what makes re-sending a month idempotent, and it is how you correct a restatement: send the corrected month again.
complete: true with an empty rows array is refused, because it would silently delete the window. Omit complete if you mean to append nothing.
Every row’s ChargePeriodStart must fall inside the declared window, and all rows in a batch must carry the same provider. The first push sets the connection’s provider; later pushes must agree with it.

Batch size

The endpoint accepts 32 MB per request by default. A larger payload is rejected with a 413 telling you to split the batch. Narrow the window rather than dropping rows - a month is usually the natural unit. Only one ingest runs per connection at a time. A concurrent push gets a 409, so retry rather than posting two batches to one connection in parallel.

A daily loop

Send yesterday once the day closes, with complete: true so a re-run is harmless:
Then re-send the whole month after your own books close, again with complete: true. The month replaces the days it covers, so late charges and restatements correct themselves without any deletion on your side.

What CostGraph does with it

Records land as raw billing rows, are normalised into line items, then roll up into daily cost and reconcile against the invoice totals for each month the window touches.

Responses