Create the connection
- Open Settings -> Integrations and choose FOCUS push.
- Pick the tenant the charges belong to and name the connection.
- Copy the connection id. It looks like
fpc_...and identifies this stream.
409.
Create the API key
- Open Settings -> API keys and create a key.
- Grant it the
focus:writescope. Nothing else is required.
Send the records
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.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.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.
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 a413 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, withcomplete: true so a re-run is harmless:
complete: true.
The month replaces the days it covers, so late charges and restatements correct
themselves without any deletion on your side.