> ## Documentation Index
> Fetch the complete documentation index at: https://opensandbox-oc-s-762ac928075c46d2828bcb22.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Usage Aggregator

Aggregate usage grouped by sandbox or by tag. Dollars are not exposed
— the platform returns the same physical quantities the invoice is
computed from. For per-sandbox time-series drilldown (memory
utilization over time, 1-minute resolution), see
[`GET /api/sandboxes/:id/usage`](/api-reference/usage/get-sandbox-usage).

<Note>
  **Preview:** Usage and tag APIs are new. Endpoints, response fields,
  and SDK method names may change before GA; temporary inaccuracies or
  rough edges are possible while the surface settles.
</Note>

<Note>
  All `*GbSeconds` fields are **GiB-seconds** (binary, 2<sup>30</sup>
  bytes/GiB), not decimal GB. For a sandbox provisioned at 1024 MiB
  running for one second, `memoryGbSeconds` is `1`.
</Note>

<Note>
  Freshness: accurate to the minute under normal conditions. A sandbox
  that exited uncleanly continues to accrue against its provisioned
  tier until the platform reconciles state — the same behavior the
  billing pipeline uses, so the numbers here match the invoice.
</Note>

## Request

<ParamField query="groupBy" type="string" required>
  `sandbox` for top sandboxes, or `tag:<key>` for usage by tag value.
  Keys may contain `:` — everything after the first `:` is the tag
  key (so `groupBy=tag:team:payments` groups by the key
  `team:payments`).
</ParamField>

<ParamField query="from" type="string">
  Inclusive lower bound. Accepts either an ISO date (`2026-05-27`,
  interpreted as UTC midnight) or an RFC3339 timestamp. Default: now
  minus 30 days. Max window: 90 days.
</ParamField>

<ParamField query="to" type="string">
  Exclusive upper bound. Same accepted formats as `from`. Default:
  now.
</ParamField>

<ParamField query="filter[tag:<key>]" type="string">
  One param per dimension. Comma-separated values are OR'd within
  that dimension; different `filter[...]` params are AND'd across
  dimensions. Passing the same `filter[...]` key twice returns 400 —
  put multiple OR values in one comma-separated string instead. Empty
  value (`filter[tag:team]=`) matches sandboxes without the `team`
  key.
</ParamField>

<ParamField query="sort" type="string">
  `-memoryGbSeconds` (default) or `-diskOverageGbSeconds`.
</ParamField>

<ParamField query="limit" type="integer">
  Max items per page. Default 50, max 500.
</ParamField>

<ParamField query="cursor" type="string">
  Opaque cursor from a prior response's `nextCursor`.
</ParamField>

## Response

<ResponseField name="from" type="string" required>
  RFC3339 echo of the effective window lower bound.
</ResponseField>

<ResponseField name="to" type="string" required>
  RFC3339 echo of the effective window upper bound.
</ResponseField>

<ResponseField name="groupBy" type="string" required>
  Echo of the requested `groupBy` value.
</ResponseField>

<ResponseField name="total" type="object" required>
  Aggregate across **all rows matching the window and filters**, not
  just the current page. Use this for headline numbers; sum `items[]`
  if you need a per-page subtotal instead.

  <Expandable title="properties">
    <ResponseField name="memoryGbSeconds" type="number" required>
      Total provisioned memory × time across matching sandboxes, in
      GiB-seconds.
    </ResponseField>

    <ResponseField name="diskOverageGbSeconds" type="number" required>
      Total disk-overage × time. Disk usage within the free
      allowance does not contribute; this is the billable excess.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="items" type="object[]" required>
  Paginated rows, sorted by `sort` (default: largest first).

  When `groupBy=sandbox`, each row is one sandbox:

  <Expandable title="groupBy=sandbox row">
    <ResponseField name="sandboxId" type="string" required>
      The sandbox identifier.
    </ResponseField>

    <ResponseField name="alias" type="string">
      Display name set at create time. Absent when none was provided.
    </ResponseField>

    <ResponseField name="status" type="string">
      Current sandbox status (e.g., `running`, `hibernated`,
      `stopped`). Absent when no session exists.
    </ResponseField>

    <ResponseField name="tags" type="object" required>
      Current tag set on the sandbox. Empty object when none.
    </ResponseField>

    <ResponseField name="tagsLastUpdatedAt" type="string">
      RFC3339 timestamp of the most recent tag change, or `null` if
      no tags exist.
    </ResponseField>

    <ResponseField name="memoryGbSeconds" type="number" required>
      Provisioned memory × time for this sandbox in the window.
    </ResponseField>

    <ResponseField name="diskOverageGbSeconds" type="number" required>
      Disk overage × time for this sandbox.
    </ResponseField>
  </Expandable>

  When `groupBy=tag:<key>`, each row is one tag value:

  <Expandable title="groupBy=tag row">
    <ResponseField name="tagKey" type="string" required>
      The tag key from the request (e.g., `team`).
    </ResponseField>

    <ResponseField name="tagValue" type="string" required>
      The tag value for this group.
    </ResponseField>

    <ResponseField name="memoryGbSeconds" type="number" required>
      Total provisioned memory × time across sandboxes carrying
      `tagKey=tagValue`.
    </ResponseField>

    <ResponseField name="diskOverageGbSeconds" type="number" required>
      Total disk overage × time for the same group.
    </ResponseField>

    <ResponseField name="sandboxCount" type="integer" required>
      Distinct sandboxes contributing to this row.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="untagged" type="object">
  Only present when `groupBy=tag:<key>`. Sibling bucket for sandboxes
  that lack the grouping key — without it, untagged sandboxes would
  silently disappear from the rollup. Same shape as a tag row minus
  `tagKey`/`tagValue`.

  <Expandable title="properties">
    <ResponseField name="memoryGbSeconds" type="number" required>
      Total provisioned memory × time across untagged sandboxes.
    </ResponseField>

    <ResponseField name="diskOverageGbSeconds" type="number" required>
      Total disk overage × time.
    </ResponseField>

    <ResponseField name="sandboxCount" type="integer" required>
      Distinct sandboxes in the untagged bucket.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="nextCursor" type="string">
  Opaque cursor to pass as `cursor=` on the next request, or `null`
  when no further pages exist.
</ResponseField>

<ResponseExample>
  ```json groupBy=sandbox theme={null}
  {
    "from": "2026-03-23T00:00:00Z",
    "to":   "2026-04-22T00:00:00Z",
    "groupBy": "sandbox",
    "total": { "memoryGbSeconds": 20000, "diskOverageGbSeconds": 360 },
    "items": [
      {
        "sandboxId": "sb-abc",
        "alias": "my-agent",
        "status": "running",
        "tags": { "env": "prod", "team": "payments" },
        "tagsLastUpdatedAt": "2026-04-19T14:02:00Z",
        "memoryGbSeconds": 8000,
        "diskOverageGbSeconds": 120
      }
    ],
    "nextCursor": null
  }
  ```

  ```json groupBy=tag:team theme={null}
  {
    "from": "2026-03-23T00:00:00Z",
    "to":   "2026-04-22T00:00:00Z",
    "groupBy": "tag:team",
    "total":    { "memoryGbSeconds": 19000, "diskOverageGbSeconds": 340 },
    "untagged": { "memoryGbSeconds": 1000,  "diskOverageGbSeconds":  20, "sandboxCount": 2 },
    "items": [
      { "tagKey": "team", "tagValue": "payments",
        "memoryGbSeconds": 8000, "diskOverageGbSeconds": 120, "sandboxCount": 12 },
      { "tagKey": "team", "tagValue": "growth",
        "memoryGbSeconds": 4000, "diskOverageGbSeconds":  50, "sandboxCount":  5 }
    ],
    "nextCursor": null
  }
  ```
</ResponseExample>
