# Agent: Statistics

**Page:** api/agent/statistics

[Download Raw Markdown](./api/agent/statistics.md)

---

{/* AUTO-GENERATED — Do not edit manually. Regenerate with: npm run docs:api:generate */}



The Agent Statistics API exposes cross-session usage aggregates and rollups for the Hoody platform. Use these endpoints to power dashboards, billing reconciliation, and observability tooling. The `statistics.aggregate` route rolls up per-session counters over persisted records, while `usage.by_account` and `usage.by_model` surface per-account and per-model rollups from the daemon-global `usage_calls` store.


All three endpoints are **active-realm-scoped / global-no-realm**. The underlying stores are not realm-scoped, so supplying the `X-Hoody-Realm` header (or its `?realm=` query alias) returns `400 realm_scope_unsupported`.


## Cross-session statistics

### `GET /api/v1/agent/statistics`

Returns the cross-session usage aggregate (the `statistics.aggregate` daemon action): per-session counters rolled up over persisted records. The `scope` query parameter selects between the current working directory (`cwd`, default) and every session (`all`).

### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `scope` | query | string | No | `cwd` (default) rolls up the current working directory; `all` rolls up every session. |
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope (§6.1): the `.hoody` project layer / record cwd / tool+workflow cwd. Required by routes that resolve a cwd (e.g. `POST /todos`; `createTodo` also accepts a body cwd). |
| `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override (§6.1) selecting which on-disk `.hoody` install a stateless read/write resolves (HoodyPaths). |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (§6.1; omitted = local). Rejected (`400`) on routes with no container dimension. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector (§6.1): `"global"` or a 24-hex id (also accepted as `?realm=`). Rejected (`400 realm_scope_unsupported`) on active-only / no-realm routes. |
| `realm` | query | string | No | Per-request realm selector (§6.1) — the in:query alias of the `X-Hoody-Realm` header (read only when the header is absent): `"global"` or a 24-hex id. Rejected (`400 realm_scope_unsupported`) on active-only / no-realm routes. |

### Response




Statistics report. The response body is a verbatim daemon reply; the field set is the forwarded daemon action's own.

```json
{
  "scope": "cwd",
  "sessions": 42,
  "totalCalls": 1847,
  "totalInputTokens": 482934,
  "totalOutputTokens": 198234,
  "totalCostUsd": 23.47,
  "byModel": {
    "claude-opus-4-6": {
      "calls": 234,
      "inputTokens": 102938,
      "outputTokens": 38291,
      "costUsd": 18.2
    },
    "claude-sonnet-4-5": {
      "calls": 1613,
      "inputTokens": 379996,
      "outputTokens": 159943,
      "costUsd": 5.27
    }
  }
}
```




```json
{
  "code": "bad_request",
  "message": "invalid request"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `bad_request` | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. |
| `realm_scope_unsupported` | Realm scope unsupported | A per-request realm header was supplied to an active-only / global-no-realm RPC, which has no realm dimension to scope. | Omit the realm header on this route, or open a session to scope by realm. |




```json
{
  "code": "forbidden",
  "message": "request must arrive through the Hoody proxy"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `forbidden` | Forbidden (not via the Hoody proxy) | The request did not arrive through `hoody-proxy`: its source IP is private/local (in-container loopback, the bridge, the host, or a sibling container). The gateway has no service-level auth — authorization is owned by `hoody-proxy`, which is the only party that can reach it (TPROXY preserves the real public client IP). Kit network-position trust. | Reach the agent through `hoody-proxy` (e.g. `hoody agent …` → platform → proxy), not by connecting to the container directly. |




```json
{
  "code": "rate_limited",
  "message": "request rate limit exceeded"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `rate_limited` | Too many requests | The per-client request rate limit was exceeded; the gateway throttled the request before dispatch. | Honor the `Retry-After` header and retry; reduce the request rate. |




```json
{
  "code": "internal_error",
  "message": "internal server error"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `internal_error` | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. |




### Example request




```bash
curl -G https://api.hoody.ai/api/v1/agent/statistics \
  --data-urlencode "scope=cwd" \
  -H "X-Hoody-Cwd: /home/user/project"
```




```ts
const report = await client.agent.statistics.getStatistics({
  scope: "cwd",
});
```




## Usage rollup by account

### `GET /api/v1/agent/usage/by-account`

Returns the per-(`provider`, `account`) usage rollup from the daemon-global `usage_calls` store. Optionally bound by `?since=` (Unix seconds) to filter out records older than the given timestamp. Omit `since` for all-time. This is a net-new daemon RPC (`usage.by_account`) and is global/no-realm: supplying a realm header returns `400 realm_scope_unsupported`.

### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `since` | query | integer | No | Unix-seconds lower bound; omit for all-time. A negative or non-numeric value is rejected (`400`). |
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope (§6.1): the `.hoody` project layer / record cwd / tool+workflow cwd. Required by routes that resolve a cwd (e.g. `POST /todos`; `createTodo` also accepts a body cwd). |
| `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override (§6.1) selecting which on-disk `.hoody` install a stateless read/write resolves (HoodyPaths). |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (§6.1; omitted = local). Rejected (`400`) on routes with no container dimension. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector (§6.1): `"global"` or a 24-hex id (also accepted as `?realm=`). Rejected (`400 realm_scope_unsupported`) on active-only / no-realm routes. |
| `realm` | query | string | No | Per-request realm selector (§6.1) — the in:query alias of the `X-Hoody-Realm` header (read only when the header is absent): `"global"` or a 24-hex id. Rejected (`400 realm_scope_unsupported`) on active-only / no-realm routes. |

### Response




Per-account rollup. The response body is a verbatim daemon reply; the field set is the forwarded daemon action's own.

```json
{
  "accounts": [
    {
      "provider": "anthropic",
      "accountId": "acc_main",
      "calls": 1204,
      "successCount": 1198,
      "errorCount": 6,
      "inputTokens": 298123,
      "outputTokens": 128443,
      "costUsd": 19.83
    },
    {
      "provider": "openai",
      "accountId": "acc_backup",
      "calls": 643,
      "successCount": 643,
      "errorCount": 0,
      "inputTokens": 184811,
      "outputTokens": 69791,
      "costUsd": 3.64
    }
  ]
}
```




```json
{
  "code": "bad_request",
  "message": "invalid request"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `bad_request` | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. |
| `realm_scope_unsupported` | Realm scope unsupported | A per-request realm header was supplied to an active-only / global-no-realm RPC, which has no realm dimension to scope. | Omit the realm header on this route, or open a session to scope by realm. |




```json
{
  "code": "forbidden",
  "message": "request must arrive through the Hoody proxy"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `forbidden` | Forbidden (not via the Hoody proxy) | The request did not arrive through `hoody-proxy`: its source IP is private/local (in-container loopback, the bridge, the host, or a sibling container). The gateway has no service-level auth — authorization is owned by `hoody-proxy`, which is the only party that can reach it (TPROXY preserves the real public client IP). Kit network-position trust. | Reach the agent through `hoody-proxy` (e.g. `hoody agent …` → platform → proxy), not by connecting to the container directly. |




```json
{
  "code": "rate_limited",
  "message": "request rate limit exceeded"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `rate_limited` | Too many requests | The per-client request rate limit was exceeded; the gateway throttled the request before dispatch. | Honor the `Retry-After` header and retry; reduce the request rate. |




```json
{
  "code": "internal_error",
  "message": "internal server error"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `internal_error` | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. |




### Example request




```bash
curl -G https://api.hoody.ai/api/v1/agent/usage/by-account \
  --data-urlencode "since=1700000000"
```




```ts
const rollup = await client.agent.statistics.usageByAccount({
  since: 1700000000,
});
```




## Usage rollup by model

### `GET /api/v1/agent/usage/by-model`

Returns the per-(`model`, `provider`) usage rollup from the daemon-global `usage_calls` store: call counts, success rate, cost, and latency. Optionally bound by `?since=` (Unix seconds) to filter out records older than the given timestamp; default is all-time. The usage store is daemon-global, cwd-independent, and not realm-scoped, so a realm header returns `400 realm_scope_unsupported` and the rollup is identical regardless of the active realm. This is a net-new daemon RPC (`usage.by_model`).

### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `since` | query | integer | No | Unix-seconds lower bound; omit for all-time. A negative or non-numeric value is rejected (`400`). |
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope (§6.1): the `.hoody` project layer / record cwd / tool+workflow cwd. Required by routes that resolve a cwd (e.g. `POST /todos`; `createTodo` also accepts a body cwd). |
| `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override (§6.1) selecting which on-disk `.hoody` install a stateless read/write resolves (HoodyPaths). |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (§6.1; omitted = local). Rejected (`400`) on routes with no container dimension. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector (§6.1): `"global"` or a 24-hex id (also accepted as `?realm=`). Rejected (`400 realm_scope_unsupported`) on active-only / no-realm routes. |
| `realm` | query | string | No | Per-request realm selector (§6.1) — the in:query alias of the `X-Hoody-Realm` header (read only when the header is absent): `"global"` or a 24-hex id. Rejected (`400 realm_scope_unsupported`) on active-only / no-realm routes. |

### Response




Per-model rollup. The response body is a verbatim daemon reply; the field set is the forwarded daemon action's own.

```json
{
  "models": [
    {
      "model": "claude-opus-4-6",
      "provider": "anthropic",
      "calls": 234,
      "successCount": 231,
      "errorCount": 3,
      "successRate": 0.987,
      "inputTokens": 102938,
      "outputTokens": 38291,
      "costUsd": 18.2,
      "avgLatencyMs": 4321,
      "p95LatencyMs": 7820
    },
    {
      "model": "claude-sonnet-4-5",
      "provider": "anthropic",
      "calls": 1613,
      "successCount": 1613,
      "errorCount": 0,
      "successRate": 1.0,
      "inputTokens": 379996,
      "outputTokens": 159943,
      "costUsd": 5.27,
      "avgLatencyMs": 1834,
      "p95LatencyMs": 3211
    }
  ]
}
```




```json
{
  "code": "bad_request",
  "message": "invalid request"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `bad_request` | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. |
| `realm_scope_unsupported` | Realm scope unsupported | A per-request realm header was supplied to an active-only / global-no-realm RPC, which has no realm dimension to scope. | Omit the realm header on this route, or open a session to scope by realm. |




```json
{
  "code": "forbidden",
  "message": "request must arrive through the Hoody proxy"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `forbidden` | Forbidden (not via the Hoody proxy) | The request did not arrive through `hoody-proxy`: its source IP is private/local (in-container loopback, the bridge, the host, or a sibling container). The gateway has no service-level auth — authorization is owned by `hoody-proxy`, which is the only party that can reach it (TPROXY preserves the real public client IP). Kit network-position trust. | Reach the agent through `hoody-proxy` (e.g. `hoody agent …` → platform → proxy), not by connecting to the container directly. |




```json
{
  "code": "rate_limited",
  "message": "request rate limit exceeded"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `rate_limited` | Too many requests | The per-client request rate limit was exceeded; the gateway throttled the request before dispatch. | Honor the `Retry-After` header and retry; reduce the request rate. |




```json
{
  "code": "internal_error",
  "message": "internal server error"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `internal_error` | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. |




### Example request




```bash
curl -G https://api.hoody.ai/api/v1/agent/usage/by-model \
  --data-urlencode "since=1700000000"
```




```ts
const rollup = await client.agent.statistics.usageByModel({
  since: 1700000000,
});
```