# Reports & Analytics

> Cost flow Sankey, anomaly detection, trends, snapshots, team and tag attribution, budgets, and unit economics — all from a single cost_records fact table.

Source: https://zop.dev/developer-docs/operations/reports

---

Track cloud costs, savings from automated scheduling, and monitor resource utilization
across all your connected accounts.

## Dashboard Summary

### GET /reports/summary

Get an overall cost and savings summary for the current period.

```json title="Response"
{
  "data": {
    "totalCostUSD": 12450.50,
    "totalSavingsUSD": 3280.75,
    "resourceCount": 156,
    "period": "2025-01"
  }
}
```

## Cost Flow (Sankey)

A configurable Sankey view of how cost flows across providers, accounts, regions,
resource types, services, purchase types, and teams. The default flow is
`Provider -> Account -> ResourceType -> Team` and can be reshaped to any
ordered sequence of dimensions; the URL is shareable so a drilldown can be sent in
chat.

### Supported dimensions

- `provider`
- `cloud_account_id`
- `region`
- `resource_type`
- `service_name`
- `purchase_type`
- `team_id`
- `resource_uid` (drilldown only)

### Overlays

- **Savings overlay** — highlights links carrying idle, orphan, or right-sizing savings opportunities surfaced by Recommendations.
- **Unattributed overlay** — surfaces cost flowing to resources missing team or tag attribution. Use this to find gaps in [Smart Tags](https://zop.dev/docs/operations/smart-tags) coverage.

### GET /reports/costs/breakdown

Generic GROUP BY on cost_records. Reusable for the Sankey, treemaps, custom dashboards, anything future.

Query parameters:

- `dims` — comma-separated dimension list, in order. Allowlisted to the dimensions above.
- `from` / `to` — ISO 8601 date range.
- `filter[dim]=value` — repeatable, scopes the breakdown.

```bash title="Example"
curl "https://zopnight.com/api/reports/costs/breakdown?dims=provider,cloud_account_id,resource_type,team_id&from=2026-04-01&to=2026-04-27" \
  -H "Authorization: Bearer <token>"
```

```json title="Response"
{
  "data": {
    "nodes": [
      { "id": "aws", "dim": "provider" },
      { "id": "aws|123456789012", "dim": "cloud_account_id", "label": "Production AWS" },
      { "id": "aws|123456789012|aws-ec2", "dim": "resource_type" },
      { "id": "aws|123456789012|aws-ec2|team_platform", "dim": "team_id", "label": "Platform" }
    ],
    "links": [
      { "source": "aws", "target": "aws|123456789012", "valueUSD": 4200.50 },
      { "source": "aws|123456789012", "target": "aws|123456789012|aws-ec2", "valueUSD": 2800.00 },
      { "source": "aws|123456789012|aws-ec2", "target": "aws|123456789012|aws-ec2|team_platform", "valueUSD": 1900.50 }
    ],
    "totalUSD": 4200.50
  }
}
```

**Tip**

Every cost-reporting endpoint — `/reports/summary`, `/reports/trends`,
`/reports/costs/breakdown`, `/reports/teams`, `/reports/tags` — reads
from the same `cost_records` fact table. Team and tag attribution is resolved at
read-time, so totals always reconcile to the provider total without a snapshot rebuild.

### Export

The Sankey supports CSV and PNG export. CSV exports the underlying flow rows
(source, target, dimension, value); PNG exports the rendered diagram with the same
URL-encoded state.

## Cost Anomaly Detection

ZopNight detects unusual cost spikes across five dimensions (org, cloud account,
resource group, resource, team) using percentage deviation and z-score analysis, with
14-day root-cause attribution.
See [Cost Anomaly Detection](https://zop.dev/docs/operations/anomaly-detection) for
the full feature, including detection algorithm, notification cooldown, dedup, and the
two-level drawer.

### GET /reports/anomalies

List anomaly records for a window. Suppressed records map to severity info.

## Cost Trends

### GET /reports/trends

Get daily cost and savings trend data for charts and dashboards.

```json title="Response"
{
  "data": {
    "items": [
      {
        "date": "2025-01-15",
        "costUSD": 425.30,
        "savingsUSD": 112.50
      },
      {
        "date": "2025-01-16",
        "costUSD": 418.20,
        "savingsUSD": 119.80
      }
    ]
  }
}
```

### GET /reports/cost-history

Long-range cost history for charts that span multiple months. Accepts granularity (daily, weekly, monthly) and a date range.

### GET /reports/metrics

Aggregate utilization metrics across the org (CPU, memory, disk) used by the dashboard hero. Backed by the discoverer

### GET /reports/resources/metrics

Per-resource utilization metrics over a window (CPU, memory, network). Used by the resource detail drawer.

### GET /reports/resources/activity

Per-resource activity timeline (state transitions, scheduler events, action submissions, configuration changes).

### POST /reports/export

Export a cost report as CSV or JSON. Accepts the same dimensions and filters as the report endpoints; returns a download URL.

```bash title="Request"
curl -X POST https://zopnight.com/api/reports/export \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "report": "costs/breakdown",
    "format": "csv",
    "dimensions": ["provider", "team_id"],
    "from": "2026-04-01",
    "to": "2026-04-30"
  }'
```

```json title="Response"
{
  "data": {
    "downloadURL": "https://zopnight.com/exports/cost-2026-04.csv",
    "expiresAt": "2026-04-30T01:00:00Z"
  }
}
```

## Resource Costs

### GET /reports/costs/resources

Get per-resource cost breakdown.

```bash title="Example"
curl "https://zopnight.com/api/reports/costs/resources?page=1&limit=20" \
  -H "Authorization: Bearer <token>"
```

## Provider Summary

### GET /reports/providers/summary

Get cost and resource count breakdown by cloud provider.

```json title="Response"
{
  "data": {
    "aws": {
      "count": 85,
      "costUSD": 7200.00
    },
    "gcp": {
      "count": 42,
      "costUSD": 3150.50
    },
    "azure": {
      "count": 29,
      "costUSD": 2100.00
    }
  }
}
```

## Resource Summary

### GET /reports/resources/summary

Get resource count hierarchy grouped by provider and cloud account.

```json title="Response"
{
  "data": {
    "providers": [
      {
        "name": "aws",
        "total": 85,
        "accounts": [
          {
            "id": "ca_001",
            "name": "Production AWS",
            "total": 60,
            "byType": {
              "aws-ec2": 25,
              "aws-rds": 8,
              "aws-eks": 3
            }
          }
        ]
      }
    ],
    "total": 156
  }
}
```

## Cost Attribution (Showback)

Slice cost by **team** (derived from resource group ownership) or by
**tag** (cloud tags, GCP labels, Azure tags, and accepted smart tags).
Both dimensions are computed from the same daily billing rollup, so team and tag totals
always reconcile to the provider total.

### By Team

### GET /reports/teams

List cost per team for the selected period.

```json title="Response"
{
  "data": [
    {
      "teamID": "team_001",
      "teamName": "Platform",
      "costUSD": 4200.50,
      "resourceCount": 42,
      "percentOfTotal": 33.7
    }
  ]
}
```

### GET /reports/teams/{teamID}

Get a per-team breakdown by provider and resource type.

### GET /reports/teams/{teamID}/resources

List resources attributed to a team with their individual cost.

### GET /reports/teams/trends

Daily/weekly/monthly trend series for all teams.

### By Tag

### GET /reports/tags

List tag keys that have attributed cost (cloud tags, GCP labels, Azure tags, accepted smart tags).

```json title="Response"
{
  "data": [
    {
      "tagKey": "Environment",
      "coverage": {
        "tagged": 128,
        "untagged": 18
      },
      "costUSD": 12450.50
    }
  ]
}
```

### GET /reports/tags/{tagKey}

List tag values for a given key and their attributed cost.

### GET /reports/tags/{tagKey}/values/{tagValue}/resources

List resources that carry a given tag value with individual cost.

### GET /reports/tags/trends

Daily/weekly/monthly trend series filtered by tag key / value / provider / type.

**Info**

`coverage.tagged` vs `coverage.untagged` reflects how many discovered
resources carry the key in the current period. Low coverage usually means the tag key is
inconsistent across accounts —
[Smart Tags](https://zop.dev/docs/operations/smart-tags)
can help close the gap.

## Monthly Snapshots

### GET /reports/snapshots

Get historical monthly report snapshots.

```json title="Response"
{
  "data": [
    {
      "id": "snap_001",
      "period": "2025-01",
      "totalCostUSD": 12450.50,
      "totalSavingsUSD": 3280.75,
      "resourceCount": 156,
      "createdAt": "2025-02-01T00:00:00Z"
    },
    {
      "id": "snap_002",
      "period": "2024-12",
      "totalCostUSD": 13100.00,
      "totalSavingsUSD": 2900.00,
      "resourceCount": 148,
      "createdAt": "2025-01-01T00:00:00Z"
    }
  ]
}
```

## Team Budgets

Set monthly cost budgets for teams and track spending against them.

### GET /budgets/summary

Get organization-wide budget summary.

```json title="Response"
{
  "data": {
    "totalBudgetUSD": 25000.00,
    "totalSpendUSD": 18500.00,
    "percentUsed": 74.0,
    "budgetCount": 5,
    "overBudgetCount": 1
  }
}
```

### GET /budgets

List all team budgets.

### POST /budgets

Create a team budget.

```bash title="Request"
curl -X POST https://zopnight.com/api/budgets \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "teamName": "Platform Team",
    "resourceGroupID": "grp_abc123",
    "monthlyBudgetUSD": 5000.00,
    "alertThresholdPercent": 80
  }'
```

```json title="Response"
{
  "data": {
    "id": "bgt_abc123",
    "teamName": "Platform Team",
    "resourceGroupID": "grp_abc123",
    "monthlyBudgetUSD": 5000.00,
    "alertThresholdPercent": 80,
    "currentSpendUSD": 3200.00,
    "remainingUSD": 1800.00,
    "percentUsed": 64.0,
    "status": "green",
    "createdAt": "2025-01-01T00:00:00Z",
    "updatedAt": "2025-01-20T08:00:00Z"
  }
}
```

### PUT /budgets/{budgetID}

Update a team budget.

### DELETE /budgets/{budgetID}

Delete a team budget.

## Budget Status Indicators

{(() => {
  const rows = [
    ['green', 'Spend < alert threshold', 'Spending is within normal range'],
    ['yellow', 'Spend >= alert threshold', 'Spending has crossed the configured alert threshold'],
    ['red', 'Spend >= 100% of budget', 'Budget has been exceeded'],
  ];
  return (
    <table>
      <thead>
        <tr>
          <th>Status</th>
          <th>Condition</th>
          <th>Description</th>
        </tr>
      </thead>
      <tbody>
        {rows.map(([status, condition, desc]) => (
          <tr key={status}>
            <td>
              <span className={`inline-block w-3 h-3 rounded-full mr-2 ${
                status === 'green' ? 'bg-emerald-500' : status === 'yellow' ? 'bg-amber-500' : 'bg-red-500'
              }`} />
              <code>{status}</code>
            </td>
            <td>{condition}</td>
            <td>{desc}</td>
          </tr>
        ))}
      </tbody>
    </table>
  );
})()}

## Scheduler Events

View a log of scheduler-triggered events showing which schedules fired and what actions were taken.

### GET /events

List scheduler events.

```json title="Response"
{
  "data": [
    {
      "id": "evt_001",
      "scheduleID": "sch_abc123",
      "action": "stop",
      "triggeredAt": "2025-01-20T18:00:00Z",
      "resources": ["i-0abc123def456", "i-0def789abc012"],
      "status": "completed"
    }
  ]
}
```

### POST /events

Record a scheduler event (primarily used internally by the scheduler service).

## Unit Economics

Unit economics correlate spend with business KPIs — cost per order, cost per active
user, cost per build minute — so teams can compare cost movement against demand rather
than treating spend as a raw line item. Define a metric in
`/unit-metrics` (push, pull, or CSV ingestion), then send values for it and
read the resulting series back through Reports.

### POST /unit-metric-values/{id}

Push a batch of values for a unit metric. Returns the number of rows ingested. Use for low-volume push integrations and webhooks.

### POST /unit-metric-values/{id}/csv

Upload a CSV of historical values. Accepts a single CSV file per request; rows are upserted by timestamp.

### POST /unit-metric-values/{id}/test-pull

Run the configured pull integration once and return the rows it produced — without persisting anything. Used by the metric editor

### DELETE /unit-metric-values/{id}

Drop every stored value for a metric. Useful when reseeding from a corrected source.

### GET /reports/unit-economics

Time-series of cost-per-unit for a chosen metric, with optional filters (provider, team, tag). Used by the Unit Economics chart on the cost dashboard.

**Info**

The metric definitions themselves (name, ingestion mode, pull config) are managed at
`/unit-metrics` and `/unit-metrics/{id}`. The endpoints above
handle values and reads only. The aggregator runs a daily
`unit-metrics-pull` cron at 04:15 UTC that fetches pull-mode metrics on
schedule.
