Skip to content

Cost per run and per customer

Kindgi records every model call a run makes: the model, the tokens, and what the call cost at the prices its provider was registered with (see Connect a model). Read the records of one run or of a flow's whole run, sum them for one customer's month, or get each run's total when it finishes. These costs are Kindgi's estimates from the vendors' published prices; your provider's invoice is what you pay.

run-cost.ts
import { createClient } from '@kindgi/sdk/client';
import type { RunId } from '@kindgi/sdk/types';
const kindgi = createClient(); // KINDGI_API_URL, KINDGI_API_TOKEN, or the running kindgi dev
const calls = await kindgi.cost.usage.query({ runId: process.argv[2] as RunId });
for (const call of calls.data) {
console.log(call.step, call.model, call.status, call.usage?.promptTokens, call.usage?.completionTokens, call.costUsd);
}

For an agent turn that called a tool once, both print two model calls, the newest first:

2 claude-sonnet-5-5 ok 606 152 0.002732
1 claude-sonnet-5-5 ok 513 57 0.0015960000000000002

Each record is one model call:

{
"id": "bfe7ccd4-ca6c-411c-918f-e605b4943bdd",
"tenantId": "c963c8d8-5afc-403f-8c4b-8617327c424e",
"category": "llm.inference",
"providerId": "anthropic",
"runId": "f3835200-e4c6-4579-a3fc-4b70f7444333",
"agentId": "acme.order-desk",
"conversationId": "936af311-f1c8-4260-a1ed-973876b4ac71",
"quantity": 570,
"unit": "tokens",
"costUsd": 0.0015960000000000002,
"occurredAt": "2026-10-08T15:13:23.128Z",
"callId": "00a26a40-58c5-41ce-af41-a5c583a577a6",
"projectId": "18af3731-67e6-4720-aa50-0972ff4b5eca",
"rootRunId": "f3835200-e4c6-4579-a3fc-4b70f7444333",
"agentVersion": "0.1.0",
"nodeId": "model-call",
"step": 1,
"model": "claude-sonnet-5-5",
"servedModel": "claude-sonnet-5-5",
"status": "ok",
"usage": { "promptTokens": 513, "completionTokens": 57, "cacheReadTokens": 0, "cacheWriteTokens": 0 },
"durationMs": 1438,
"finishReason": "tool-use",
"providerRequestId": "req_011CfpzCE92VRAVmBURYMRFu",
"attempts": 1
}
Field What it is
model, servedModel The model the agent asked for, and the exact version the provider answered with.
usage The tokens: promptTokens, completionTokens, and the cache and reasoning counts. A count the provider reports is there, 0 included; one it doesn't report is absent.
costUsd The call's cost in US dollars.
status ok, or failed (see A failed call).
step The model call's number in the turn.
runId, rootRunId The run that made the call, and the run at the top of its tree.
flowId For an agent step in a flow, the flow. An agent run on its own has none.
projectId, agentId, agentVersion, conversationId Where the call belongs.
providerRequestId The provider's id for the request, to quote to its support. Not every provider sends one.
callId The call's id. The model call's node in the turn's provenance has the same callId.

To see the usage exactly as the provider sent it, add includeRawUsage: true (Python: include="rawUsage"). Each record then has rawUsage:

"rawUsage": {
"provider": "anthropic",
"model": "claude-sonnet-5-5",
"usage": { "input_tokens": 513, "output_tokens": 57, "cache_read_input_tokens": 0, "cache_creation_input_tokens": 0, … }
}

Each agent step of a flow runs as a run of its own, under the flow's run. The flow's run makes no model calls itself, so ask for its whole tree with rootRunId:

const calls = await kindgi.cost.usage.query({ rootRunId: flowRunId });

Each record's runId is the agent step's run, and its rootRunId is the flow's run.

Give each of your customers an org, and run their work in a project of that org (Organize work by org and project). One call then sums a customer's spend, here by month and model:

customer-cost.ts
import { createClient } from '@kindgi/sdk/client';
import type { Timestamp } from '@kindgi/sdk/types';
const kindgi = createClient(); // KINDGI_API_URL, KINDGI_API_TOKEN, or the running kindgi dev
// Once per customer: an org, and a project in it.
const org = await kindgi.orgs.create({ slug: 'acme-customer-one', name: 'Customer one' });
const project = await kindgi.projects.create({ orgId: org.id, slug: 'support', name: 'Support' });
// Each run for that customer names the project.
await kindgi.runs.start({
agent: 'acme-ops.echo-agent',
input: { userMessage: 'Echo hello.' },
projectId: project.id,
});
// The customer's month so far, by model.
const now = new Date();
const monthStart = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
const month = await kindgi.cost.usage.summary({
scope: { kind: 'org', orgId: org.id },
from: monthStart.toISOString() as Timestamp,
to: now.toISOString() as Timestamp,
groupBy: ['month', 'model'],
});
console.log(JSON.stringify(month, null, 2));

The sum covers only that customer's runs:

{
"groups": [
{
"key": {"month": "2026-10", "model": "claude-sonnet-5-5"},
"count": 2,
"totalUsd": 0.00449,
"tokens": {"prompt": 1385, "completion": 172, "cacheRead": 0, "cacheWrite": 0, "reasoning": 0}
}
],
"totalGroups": 1,
"truncated": false,
"totalUsd": 0.00449,
"totalRecords": 2,
"tokens": {"prompt": 1385, "completion": 172, "cacheRead": 0, "cacheWrite": 0, "reasoning": 0},
"timeRange": {"from": "2026-10-01T00:00:00.000Z", "to": "2026-10-08T15:13:28.571Z"},
"groupBy": ["month", "model"]
}
  • scope narrows the sum to an org ({ kind: 'org', orgId }) or a project ({ kind: 'project', projectId }). Python: scope_kind and scope_id.
  • Who sees what: the sum needs read on its scope. Across projects (no scope, or an org), it counts only the projects the caller may read, and records with no project; a tenant admin's sum counts every project. Where the deployment's cost store can't apply the limit, anyone but a tenant admin is refused (403).
  • from and to: the time window; to is exclusive.
  • The groups come most expensive first, at most 1000 of them; ?limit= on the API (Python: limit=), up to 10000, sets another cap. truncated says whether some were left out, and totalGroups how many there were; the totals always cover every record. To read every record instead, page through cost.usage.query.
  • groupBy splits the sum. Besides month and model: day, orgId, projectId, agentId, flowId, runId, rootRunId, conversationId, providerId, servedModel, category and tenant. With no groupBy, you get the totals only.

A tool can check that a run belongs to the right customer: see Check the run's org.

The run.finished webhook carries the run's total under data.run.usage:

{
"id": "8004eba4-0d7a-4b81-9cd2-9c1884295f9c",
"type": "run.finished",
"createdAt": "2026-10-05T07:08:42.227Z",
"data": {
"run": {
"id": "695492e1-0569-48cf-bf5b-e92170ebcd38",
"projectId": "cb742f19-68f2-4594-9cd6-b555617dc851",
"flowId": "acme-ops.echo-flow",
"flowVersion": "0.1.0",
"status": "completed",
"dryRun": false,
"failureMessage": null,
"createdAt": "2026-10-05T07:08:39.834Z",
"completedAt": "2026-10-05T07:08:42.227Z",
"usage": {
"calls": 2,
"costUsd": 0.001994,
"tokens": { "prompt": 1669, "completion": 65, "cacheRead": 0, "cacheWrite": 0, "reasoning": 0 }
}
}
}
}

Only the run at the top of a tree sends run.finished, and its usage covers the whole tree: this flow's two calls were made by its agent step.

A model call that fails is recorded too, at no cost. A turn whose provider key was wrong:

{
"runId": "28a40635-eb0b-44db-9b8f-3748c2f8165e",
"model": "claude-sonnet-5-5",
"status": "failed",
"costUsd": 0,
"durationMs": 252,
"attempts": 1,
"error": { "message": "401 {\"type\":\"error\",\"error\":{\"type\":\"authentication_error\",\"message\":\"invalid x-api-key\"},\"request_id\":null}" },
…
}

It has no usage, and it counts in run.finished's calls.

The console shows the same numbers on three pages: a project's Cost, a run's Cost tab, and the tenant's Tenant costs. Days are in UTC, and a failed call costs $0.

Anyone who can read the project sees its Cost page: what its model calls cost over a range (7 days, 30 days, 90 days, 12 months or Custom), against the range before.

A project's Cost page over 30 days: "$24.02 in the last 30 days, 78% more than the 30 days before.", "1,509 model calls · 8.4M tokens", spend by day stacked by agent, and Where it went by agent, with Support agent at $14.94 (62%), 34% more than the 30 days before.

  • The headline: "$24.02 in the last 30 days, 78% more than the 30 days before." and "1,509 model calls · 8.4M tokens".
  • Spend by day (UTC), by agent: a stacked chart, with a sentence under it ("the costliest day was 8 Oct ($1.19). Support agent is 62% of it."). Show as a table gives the numbers. Over 12 months it's by month.
  • Where it went: By agent, By model, By flow or By kind, the costliest against the range before ("34% more (was $11.12)", "new"). An agent's or a model's row filters the page to it.
  • Biggest changes: against the range before, by dollars ("Research agent new: $8.49", "Legacy summarizer −$1.81 (none now)").
  • Per-turn caps: each agent with a budget for one turn, and how close its turns came ("Costliest turn $0.14 of $0.12; 38 turns used over 80% of it, 6 turns went over.").
  • Details: the token split, and the exact GET /v1/cost/aggregate queries the page made.

The range and the breakdown are in the page's address, so a link opens the same view.

Every run's page has a Cost tab: the run's calls, as cost.usage.query returns them.

A flow run's Cost tab: "$0.08 over 7 model calls, its child runs included." and "The costliest call: step 2 of answer · Dev-Echo Agent (large-model), $0.05.", then each child run's calls with step, model, tokens, cost and time.

  • The total, with a flow's child runs included: "$0.08 over 7 model calls, its child runs included."
  • The costliest call: "The costliest call: step 2 of answer · Dev-Echo Agent (large-model), $0.05."
  • Each model call, grouped by the run that made it: its step, model, tokens in and out, cost and how long it took. A failed call says why ("Failed: upstream timeout") and costs $0.00. When a fallback answered, the call says so ("A fallback provider answered").
  • Details, for one call: the call's id, the provider and its request id, how it finished, its attempts, cache-read tokens and when it was made.

An agent turn's Cost tab with the first call's Details open: its call id, provider, provider request, finish, attempts and cache-read tokens; the second call failed with "upstream timeout" and cost $0.00.

A flow run's Overview tab gives the same total: "Cost: $0.08 over 7 model calls, its child runs included. Each call →".

Tenant admins see Tenant costs: every project's spend, laid out like a project's Cost page.

Tenant costs over 30 days: "$48.03 in the last 30 days, 78% more than the 30 days before.", "3,018 model calls · 16.8M tokens", spend by day stacked by project, and Where it went by project: Default and Ops, $24.02 each.

  • The headline, and Spend by day (UTC), by project.
  • Where it went: By project, By org, By agent or By model. A project's row opens that project's Cost page at the same range, and an agent's or a model's row filters this page.
  • Biggest changes and Details, as on a project's page.

Anyone else who opens it is pointed to each project's Cost page instead. As with the API (see One customer's month), a person's numbers count only the projects they may read.