Skip to main content Skip to content

Billing Integration

ZopNight replaces rack-rate cost estimates with billed cost from your cloud provider. Endpoints for sync status, per-account verification, raw line items, and the FOCUS 1.4 dataset.

8 min read Last updated: 

ZopNight replaces its rack-rate cost estimates with the billed cost from each connected cloud account. It syncs billing data nightly. The endpoints on this page let you check sync state, verify one account, and read the raw billing data.

Cost Source

You choose where cost comes from under Settings → Preferences → Cost source. Each cloud account can override it under Settings → Cloud Accounts.

OptionWhat you get
Estimates onlyRack-rate cost from public list prices. No billing access needed and no charges.
Your billing exportBilled cost from the Azure cost export and the GCP BigQuery billing export. No per-request charge. AWS accounts stay on estimates.
Export + billing APIAlso reads AWS cost from AWS Cost Explorer. AWS bills each request ($0.01) to your own AWS account, so you must type CONFIRM to pick it.

What each provider’s billed figure means:

  • AWS: unblended cost.
  • Azure: amortized cost. Reservation fees are spread across their term.
  • GCP: net of credits. Committed-use discounts, sustained-use discounts, promotions and free tier are netted off the list charge. Nothing is spread across a term.

Report responses carry a costSourceLabel that names this basis: Unblended Cost, Amortized Cost, Net of Credits, Mixed (by provider) when providers differ, or Rack Rate when no billed rows exist for the window.

Org-Wide Sync Status

GET /billing/sync-status

Returns the billing sync state of every cloud account in the org.

Response
{
"data": {
"accounts": [
{
"id": "7f3c…",
"orgId": "org_123",
"cloudAccountId": "b1d2…",
"provider": "aws",
"lastSyncAt": "2026-09-29 19:40:12",
"lastSuccessAt": "2026-09-29 19:40:12",
"syncState": "success",
"hasResourceLevel": true,
"backfillComplete": true,
"backfillFailedAttempts": 0,
"billingCurrency": "USD",
"invoiceTotalUsd": 12450.5,
"invoiceTotalMonth": "2026-08",
"effectiveFeed": "ce_api",
"effectiveReason": "ce_api (paid CE request)",
"effectiveSince": "2026-09-01 19:40:12",
"createdAt": "2026-06-02T10:15:00Z",
"updatedAt": "2026-09-29T19:40:12Z"
}
]
}
}
FieldDescription
cloudAccountId, providerThe account the row describes
lastSyncAtWhen the last sync ran
lastSuccessAtWhen the last successful sync finished
lastErrorThe last failure reason, if any
syncStateCurrent state. The values are listed below
effectiveFeed, effectiveReason, effectiveSinceWhich billing feed the last capture used, why, and since when
billingCurrency, invoiceTotalUsd, invoiceTotalMonthThe account’s billing currency and its last invoice total
hasResourceLevelWhether the billing data includes per-resource costs
backfillCursor, backfillComplete, backfillFailedAttemptsProgress of the historical backfill

syncState is one of:

ValueMeaning
pendingNo sync has run yet.
syncingA sync or verification is in progress.
successThe last sync worked.
failedThe last sync failed. See lastError.
no_permissionThe credentials lack billing read access.
skippedNot attempted, because the cost source setting says not to.
unavailableZopNight can’t reach this account’s billing with the credential it holds.

effectiveFeed is the billing feed the last capture used: cur2 (AWS CUR 2.0 export), ce_api (AWS Cost Explorer), azure_dcr (Azure cost export), gcp_bq (GCP BigQuery export), or none. Fields with no value yet are omitted.

Verifying a Cloud Account

POST /billing/verify

Check that one cloud account's billing access works. Needs org-wide access.

Request
{
"cloudAccountId": "b1d2…",
"provider": "aws"
}
Response
{
"data": {
"id": "",
"orgId": "org_123",
"cloudAccountId": "b1d2…",
"provider": "aws",
"syncState": "syncing",
"hasResourceLevel": false,
"backfillComplete": false,
"backfillFailedAttempts": 0,
"createdAt": "0001-01-01T00:00:00Z",
"updatedAt": "0001-01-01T00:00:00Z"
}
}

Verification runs per cloud account. Both body fields are required. provider is one of aws, azure, gcp or snowflake.

The call returns at once with syncState: "syncing". The check runs in the background and reads about one recent day of billing data. Poll GET /billing/sync-status until that account shows success, no_permission or failed.

  • 403: only users with org-wide access can verify.
  • 404: the cloud account isn’t in this org.
  • 429: the account was synced in the last 60 seconds, or too many verifications are running.

If the account is already syncing, the call returns its current status and starts nothing new.

Historical Backfill

After an account connects, a nightly job walks billing history backwards in 7-day chunks. It reaches back up to:

ProviderHistory
AWS365 days
GCP365 days
Azure90 days
Others30 days

backfillComplete turns true in the sync-status response once the walk is done.

Unattributed Spend

Some billed charges match no discovered resource. Examples are tax, support, data transfer and services ZopNight doesn’t discover. ZopNight keeps these charges so totals match your invoice. It groups them by service and reports them as unattributedCostUsd on GET /reports/summary and on the team report. See Showback.

Raw Line Items

GET /billing/line-items

Query raw billed line items. Group, filter and sum any dimension. Needs org-wide access.

Required: from and to (YYYY-MM-DD, inclusive).

Optional:

  • groupBy, up to 8 dimensions, comma-separated. For example service,region.
  • groupByTag: group by one tag key. groupByCostCategory does the same for a cost category.
  • measures, which sums to return. For example billedCost,effectiveCost,listCost,usageAmount,rowCount.
  • Filters, pass a dimension name as the parameter, with comma-separated values. For example service=AmazonEC2&region=us-east-1. Use tag=team:platform for tags.
  • orderBy, desc=true, limit (default 100, max 1000) and offset.

Common dimensions: date, provider, account, service, region, resource, chargeCategory, purchaseType, currency, grain, commitment. An unknown dimension or a limit above 1000 returns 400.

Response
{
"data": {
"rows": [
{
"dimensions": { "service": "AmazonEC2" },
"measures": { "billedCost": 4200.5, "rowCount": 1830 }
}
],
"hasMore": false
}
}

The response may also carry notes (advice about the query) and caveats (what the answer is missing). When you group by resource, also filter grain=resource. Otherwise service-level rows show up with an empty resource.

POST /billing/line-items/refetch

Capture a date range again for one account. Runs synchronously. Needs org-wide access.

Request
{
"cloudAccountId": "b1d2…",
"provider": "aws",
"from": "2026-09-01",
"to": "2026-09-08"
}
Response
{
"data": { "daysRecaptured": 7 }
}

Here to is exclusive, so one day is from=D, to=D+1.

Invoice Breakdown and Non-Resource Costs

POST /billing/invoice-breakdown

The provider invoice split by charge type and linked account, next to the stored line items for the same window. AWS only. Needs the cloud-account:update permission, org-wide resource view and a cost-capture mode that allows paid calls.

POST /billing/invoice-issues

The invoice documents AWS issued for one account, with the issue date, due date and days after month end. Reads the last 3 closed billing months unless fromMonth and toMonth are given (at most 12 months, none after the current month). AWS only. Needs the cloud-account:update permission, org-wide resource view and a cost-capture mode that allows paid calls. Reads the Invoicing API, not Cost Explorer.

POST /billing/invoice-reconcile

Compares the charge types, services, linked account and days you name with the AWS invoice, and rewrites the stored rows when apply is true. Tax only. AWS only. Same permissions as invoice-breakdown.

Required in the JSON body: cloudAccountId, provider (aws), from and to (YYYY-MM-DD, to excluded). The window can be at most 31 days. Optional: linkedAccount (a 12-digit AWS account id) and basis (cash, the default, or amortized).

Omit linkedAccount to reconcile everything the connected account stores. On a payer connection, that is the whole payer. A linkedAccount write on a payer connection is refused when payer-wide Tax is stored.

Each call makes up to five paid Cost Explorer requests, at most $0.05. Do not poll it. An org in measured_free or modeled_only mode, a missing grant and a rejected key answer 409. A throttled call answers 503. An unreadable provider response answers 502.

GET /billing/non-resource-costs

Billed cost that no resource owns (tax, support, credits, discounts, commitment fees), by charge type and service. Needs org-wide access.

Required: from and to (YYYY-MM-DD, both included). The window can be at most 366 days. The rows hold stored cost. Nothing checks them against a provider invoice.

FOCUS 1.4 Dataset

GET /billing/focus

The same billed line items as FOCUS 1.4 Cost-and-Usage rows. Needs org-wide access.

Required: from and to (YYYY-MM-DD). The window can be at most 92 days. limit defaults to 1000 and is capped at 5000. Use offset to page, and read hasMore to know when to stop.

The response carries focusVersion, rows, conformant, hasMore, and per-currency totals for the page (totalBilledCost, includedBilledCost, totalEffectiveCost, includedEffectiveCost). omitted lists columns the dataset leaves out and why. excluded lists rows left out and why.

See Cloud Accounts for the full onboarding flow, including the steps before billing verification.

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·