> ## Documentation Index
> Fetch the complete documentation index at: https://docs.crewship.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Paymaster

> Cost rollups across crew, agent, and mission scopes.

Paymaster exposes read-only cost rollups across crew, agent, and mission scopes, plus a leaderboard of top spenders and a flat-rate subscription usage view. Every spend figure comes from the `cost_ledger`, which is written exclusively by the internal cost-record path — there are no public write endpoints. See the [Paymaster guide](/guides/paymaster) for the cost ledger and budget model, and the [Sidecar coverage section](/guides/paymaster#sidecar-coverage-of-agent-cli-calls) for the known limitation around agent CLI bypass.

<Note>
  All read endpoints require authentication and are workspace-scoped. The write path is **exclusively internal** via the [LLM middleware](/guides/llm-middleware) — there are no POST endpoints on the public surface.
</Note>

## Endpoints

| Method | Endpoint                                                                     | Purpose                                    |
| ------ | ---------------------------------------------------------------------------- | ------------------------------------------ |
| GET    | [`/api/v1/paymaster/spend/by-crew`](#spend-by-crew)                          | Cost rollup grouped by crew                |
| GET    | [`/api/v1/paymaster/spend/by-agent/{crewId}`](#spend-by-agent-within-a-crew) | Cost rollup by agent within a crew         |
| GET    | [`/api/v1/paymaster/spend/by-mission/{missionId}`](#spend-by-mission)        | Total spend for one mission                |
| GET    | [`/api/v1/paymaster/top-spenders`](#top-spenders)                            | Leaderboard of agents by spend             |
| GET    | [`/api/v1/paymaster/subscriptions`](#subscriptions)                          | Flat-rate credential usage rollup          |
| POST   | [`/api/v1/internal/cost/record`](#internal-cost-record)                      | Internal: sidecar reports parsed LLM usage |

## Time window parameters

Every endpoint accepts the same window parameters:

| Param   | Type   | Description                                               |
| ------- | ------ | --------------------------------------------------------- |
| `range` | string | One of `1h`, `24h`, `7d`, `30d`. Default `7d`.            |
| `since` | string | Explicit RFC3339 lower bound. Overrides `range` when set. |
| `until` | string | Explicit RFC3339 upper bound. Defaults to now.            |

***

## Spend by crew

```
GET /api/v1/paymaster/spend/by-crew?range=7d
```

**Response:** `200 OK`

```json theme={null}
{
  "rows": [
    {
      "crew_id": "crw_backend",
      "cost_usd": 14.3210,
      "call_count": 132,
      "input_tokens": 1024583,
      "output_tokens": 457758
    }
  ],
  "since": "2026-04-10T10:00:00Z",
  "until": "2026-04-17T10:00:00Z"
}
```

| Field                  | Type    | Description                                               |
| ---------------------- | ------- | --------------------------------------------------------- |
| `rows[].crew_id`       | string  | Crew identifier.                                          |
| `rows[].cost_usd`      | number  | Sum of `cost_ledger.cost_usd` for the crew in the window. |
| `rows[].call_count`    | integer | Number of LLM calls.                                      |
| `rows[].input_tokens`  | integer | Sum of `input_tokens`.                                    |
| `rows[].output_tokens` | integer | Sum of `output_tokens`.                                   |

**Errors:** `401` no workspace; `500` DB error.

***

## Spend by agent (within a crew)

```
GET /api/v1/paymaster/spend/by-agent/{crewId}?range=24h
```

**Path parameters:**

| Param    | Description                                     |
| -------- | ----------------------------------------------- |
| `crewId` | Crew ID. Must belong to the caller's workspace. |

**Response:** `200 OK`

```json theme={null}
{
  "crew_id": "crw_backend",
  "rows": [
    {
      "agent_id": "agt_viktor",
      "cost_usd": 4.2103,
      "call_count": 42,
      "input_tokens": 158421,
      "output_tokens": 73119
    }
  ]
}
```

**Errors:**

| Status | Condition                                                   |
| ------ | ----------------------------------------------------------- |
| 400    | Missing `crewId`.                                           |
| 401    | No workspace.                                               |
| 404    | `crewId` not in your workspace (same shape as "not found"). |
| 500    | DB error.                                                   |

***

## Spend by mission

```
GET /api/v1/paymaster/spend/by-mission/{missionId}
```

Mission-scoped spend. Window-less -- sums the full mission.

**Response:** `200 OK`

```json theme={null}
{
  "mission_id": "MIS-42",
  "row": {
    "mission_id": "MIS-42",
    "cost_usd": 7.8945,
    "call_count": 28,
    "input_tokens": 542108,
    "output_tokens": 192743,
    "first_ts": "2026-04-10T10:14:02Z",
    "last_ts": "2026-04-17T09:58:31Z"
  }
}
```

`first_ts` / `last_ts` span the activity window for the mission. When no
spend has been recorded they come back as the zero time
(`"0001-01-01T00:00:00Z"`); the other numeric fields are `0`.

**Errors:**

| Status | Condition                          |
| ------ | ---------------------------------- |
| 400    | Missing `missionId`.               |
| 404    | `missionId` not in your workspace. |

***

## Top spenders

```
GET /api/v1/paymaster/top-spenders?limit=10&range=7d
```

**Query parameters:**

| Param                       | Type    | Default | Description |
| --------------------------- | ------- | ------- | ----------- |
| `limit`                     | integer | 10      | 1-100.      |
| `range` / `since` / `until` |         | 7d      | Window.     |

**Response:** `200 OK`

```json theme={null}
{
  "rows": [
    {
      "scope_kind": "agent",
      "scope_id": "agt_viktor",
      "cost_usd": 14.3210,
      "call_count": 132
    },
    {
      "scope_kind": "agent",
      "scope_id": "agt_eva",
      "cost_usd": 4.2103,
      "call_count": 42
    }
  ],
  "limit": 10,
  "since": "2026-04-10T10:00:00Z"
}
```

| Field               | Type   | Description                                                                                        |
| ------------------- | ------ | -------------------------------------------------------------------------------------------------- |
| `rows[].scope_kind` | string | Always `agent` — the leaderboard ranks agents by spend (rows with a NULL `agent_id` are excluded). |
| `rows[].scope_id`   | string | The agent ID.                                                                                      |

***

## Subscriptions

```
GET /api/v1/paymaster/subscriptions?range=30d
```

Rollup of flat-rate credential usage grouped by `(subscription_plan, provider)`. Returned rows carry call counts and token totals only — **no \$ figure**. The subscription is a flat fee paid up front, so the marginal token cost is structurally \$0; rendering "\$0.00" would imply the calls are free, which they are not.

**Query parameters:**

| Param                       | Type | Default | Description                                             |
| --------------------------- | ---- | ------- | ------------------------------------------------------- |
| `range` / `since` / `until` |      | `30d`   | Time window. Same semantics as the `spend/*` endpoints. |

**Response:** `200 OK`

```json theme={null}
{
  "rows": [
    {
      "subscription_plan": "Anthropic Max 20×",
      "provider": "anthropic",
      "call_count": 1284,
      "input_tokens": 14_205_338,
      "output_tokens": 5_127_912,
      "last_ts": "2026-04-30T11:42:18.221Z"
    },
    {
      "subscription_plan": "ChatGPT Plus",
      "provider": "openai",
      "call_count": 312,
      "input_tokens": 1_802_443,
      "output_tokens": 622_501,
      "last_ts": "2026-04-30T09:11:02.083Z"
    }
  ],
  "since": "2026-03-31T11:42:18Z",
  "until": "2026-04-30T11:42:18Z"
}
```

| Field                      | Type             | Description                                                                                  |
| -------------------------- | ---------------- | -------------------------------------------------------------------------------------------- |
| `rows[].subscription_plan` | string           | Display label set by the orchestrator from the credential type (e.g. `"Anthropic Max 20×"`). |
| `rows[].provider`          | string           | LLM provider (`anthropic`, `openai`, ...).                                                   |
| `rows[].call_count`        | integer          | Number of `cost_ledger` rows with `billing_mode='flat_rate'` for this `(plan, provider)`.    |
| `rows[].input_tokens`      | integer          | Sum of `input_tokens`.                                                                       |
| `rows[].output_tokens`     | integer          | Sum of `output_tokens`.                                                                      |
| `rows[].last_ts`           | string (RFC3339) | Most recent `ts` in the group.                                                               |

**Errors:** `401` no workspace; `500` DB error.

***

## Internal: cost record

```
POST /api/v1/internal/cost/record
```

<Note>
  **Internal only — `X-Internal-Token` required.** This is how the sidecar reports parsed LLM usage from inside a crew container. Agents cannot reach this endpoint directly; the sidecar runs as UID 1002 and holds the token. Authoritative scope (workspace / crew / agent IDs) is taken from the sidecar's `IPCConfig` set at exec time, so an agent that captures the token still cannot forge cross-tenant attribution.
</Note>

See [Internal IPC API](/api-reference/internal#cost-record) for the full request/response schema and the surrounding security model.

## Related

* [Paymaster guide](/guides/paymaster).
* [`crewship paymaster`](/cli/paymaster).
