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 for the cost ledger and budget model, and the Sidecar coverage section for the known limitation around agent CLI bypass.
All read endpoints require authentication and are workspace-scoped. The write path is exclusively internal via the LLM middleware — there are no POST endpoints on the public surface.
Endpoints
| Method | Endpoint | Purpose |
|---|
| GET | /api/v1/paymaster/spend/by-crew | Cost rollup grouped by crew |
| GET | /api/v1/paymaster/spend/by-agent/{crewId} | Cost rollup by agent within a crew |
| GET | /api/v1/paymaster/spend/by-mission/{missionId} | Total spend for one mission |
| GET | /api/v1/paymaster/top-spenders | Leaderboard of agents by spend |
| GET | /api/v1/paymaster/subscriptions | Flat-rate credential usage rollup |
| POST | /api/v1/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
{
"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
{
"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
{
"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
{
"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
{
"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
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.
See Internal IPC API for the full request/response schema and the surrounding security model.