Agent Cloud Billing
Billing service for cloud agent resource consumption. Extends the EndpointCredits credit system with per-agent, per-customer metering across six resource types.
Refs #1981, #1983
Overview
Agent Cloud Billing tracks every resource an agent consumes on behalf of a customer. Each usage event is recorded in the agent_resource_usage table with a calculated credit cost. Credits are deducted from the customer's balance in credit_transactions.
Agent performs work
|
v
record_usage() called with resource_type + quantity
|
v
Credit cost calculated (quantity * credits_per_unit)
|
v
AgentResourceUsage row inserted
|
v
deduct_credits() removes credits from customer balance
Resource Types and Pricing
Six resource types are metered. Each has a fixed credit-per-unit rate.
| Resource Type | Credits per Unit | Unit | Description |
|---|---|---|---|
compute | 2 | seconds | Agent CPU execution time |
memory_ops | 5 | operations | ZeroMemory read/write operations |
vector_search | 8 | queries | Vector similarity search queries |
storage | 0.001 | bytes | Object/file storage consumption |
a2a | 3 | messages | Agent-to-agent communication messages |
postgresql | 20 | queries | Dedicated PostgreSQL queries |
Cost Examples
| Operation | Quantity | Calculation | Credit Cost |
|---|---|---|---|
| 60 seconds of compute | 60 | 60 * 2 | 120.000 |
| 10 memory operations | 10 | 10 * 5 | 50.000 |
| 5 vector searches | 5 | 5 * 8 | 40.000 |
| 1 MB storage (1,048,576 bytes) | 1048576 | 1048576 * 0.001 | 1048.576 |
| 25 agent-to-agent messages | 25 | 25 * 3 | 75.000 |
| 3 PostgreSQL queries | 3 | 3 * 20 | 60.000 |
All credit costs are calculated as Decimal values and quantized to 3 decimal places.
AgentResourceUsage Model
The agent_resource_usage table stores every metered event.
| Column | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| agent_id | VARCHAR(255) | Agent identifier (indexed) |
| customer_id | UUID | Foreign key to users.id (indexed) |
| resource_type | VARCHAR(32) | One of the six resource types |
| quantity | NUMERIC | Amount of resource consumed |
| unit | VARCHAR(32) | Unit of measurement (seconds, operations, etc.) |
| credit_cost | NUMERIC | Calculated credit cost for this usage |
| period_start | DATETIME | Start of the metering period |
| period_end | DATETIME | End of the metering period |
| metadata | JSON | Optional additional context |
| created_at | DATETIME | Record creation timestamp |
Service API
AgentCloudBillingService
Instantiated with a SQLAlchemy Session. All methods operate within the caller's transaction.
from app.services.agent_cloud_billing_service import AgentCloudBillingService
service = AgentCloudBillingService(db=session)
calculate_credit_cost(resource_type, quantity)
Calculate the credit cost for a given resource type and quantity without recording usage.
Parameters:
resource_type(str) -- One of:compute,memory_ops,vector_search,storage,a2a,postgresqlquantity(Decimal) -- Amount of resource consumed
Returns: Decimal -- credit cost quantized to 3 decimal places
Raises: ValueError if the resource type is unknown
cost = service.calculate_credit_cost("vector_search", Decimal("15"))
## cost = Decimal("120.000")
record_usage(agent_id, customer_id, resource_type, quantity, period_start, period_end, metadata=None)
Record a resource usage event and calculate its credit cost. Inserts a row into agent_resource_usage and flushes (but does not commit -- the caller controls the transaction).
Parameters:
agent_id(str) -- Agent identifiercustomer_id(str) -- Customer UUID as stringresource_type(str) -- One of the six valid resource typesquantity(float) -- Amount consumed (must be positive)period_start(datetime) -- Start of metering periodperiod_end(datetime) -- End of metering period (must be after start)metadata(dict, optional) -- Additional context
Returns: Dictionary with the recorded usage details:
{
"id": "uuid-string",
"agent_id": "aurora",
"customer_id": "customer-uuid",
"resource_type": "compute",
"quantity": 120.0,
"unit": "seconds",
"credit_cost": 240.0,
"period_start": "2026-06-01T00:00:00",
"period_end": "2026-06-01T01:00:00"
}
Raises:
ValueErrorif resource_type is unknownValueErrorif period_end is not after period_startValueErrorif quantity is not positive
deduct_credits(customer_id, credits, description)
Deduct credits from a customer's balance via the credit_transactions table. Checks the current balance before deducting and returns False if insufficient.
Parameters:
customer_id(str) -- Customer UUID as stringcredits(float) -- Number of credits to deduct (positive value)description(str) -- Reason for the deduction
Returns: True if deduction succeeded, False if insufficient balance
Raises: ValueError if credits is not positive
success = service.deduct_credits(
customer_id="customer-uuid",
credits=240.0,
description="Agent aurora compute: 120s execution"
)
get_agent_usage_summary(agent_id, period_start, period_end)
Retrieve aggregated usage for a specific agent over a time period, grouped by resource type.
Parameters:
agent_id(str) -- Agent identifierperiod_start(datetime) -- Start of query periodperiod_end(datetime) -- End of query period
Returns:
{
"agent_id": "aurora",
"period_start": "2026-06-01T00:00:00",
"period_end": "2026-06-01T23:59:59",
"total_credit_cost": 510.0,
"total_records": 42,
"by_resource_type": {
"compute": {
"total_quantity": 300.0,
"total_credit_cost": 600.0,
"unit": "seconds",
"record_count": 15
},
"vector_search": {
"total_quantity": 50.0,
"total_credit_cost": 400.0,
"unit": "queries",
"record_count": 12
}
}
}
get_customer_cloud_spend(customer_id, period_start, period_end)
Retrieve total cloud spend for a customer across all agents, grouped by agent and resource type.
Parameters:
customer_id(str) -- Customer UUID as stringperiod_start(datetime) -- Start of query periodperiod_end(datetime) -- End of query period
Returns:
{
"customer_id": "customer-uuid",
"period_start": "2026-06-01T00:00:00",
"period_end": "2026-06-30T23:59:59",
"total_credit_cost": 1250.0,
"total_records": 87,
"by_agent": {
"aurora": {
"total_credit_cost": 800.0,
"record_count": 52,
"by_resource_type": {
"compute": {
"total_quantity": 200.0,
"total_credit_cost": 400.0,
"unit": "seconds",
"record_count": 20
}
}
},
"sage": {
"total_credit_cost": 450.0,
"record_count": 35,
"by_resource_type": {}
}
}
}
Budget Alerts and Cost Management
Monitoring Spend
Use get_customer_cloud_spend to monitor a customer's total spend across all agents. Compare against budget thresholds to trigger alerts:
spend = service.get_customer_cloud_spend(
customer_id="customer-uuid",
period_start=month_start,
period_end=month_end,
)
if spend["total_credit_cost"] > budget_threshold:
# Trigger budget alert notification
pass
Pre-flight Balance Checks
Before executing expensive operations, check whether the customer has sufficient credits:
estimated_cost = service.calculate_credit_cost("postgresql", Decimal("10"))
## Check balance before proceeding
can_deduct = service.deduct_credits(
customer_id="customer-uuid",
credits=float(estimated_cost),
description="Agent sage: 10 PostgreSQL queries"
)
if not can_deduct:
# Reject the operation or notify the customer
pass
Per-Agent Cost Attribution
Use get_agent_usage_summary to attribute costs to individual agents. This enables:
- Identifying which agents consume the most resources
- Optimizing expensive agents (e.g., reducing unnecessary vector searches)
- Billing breakdowns for customers running multiple agents
Data Lake Integration
Agent resource usage is exported to the data lake as the platform/agent_resource_usage partition. This enables historical analysis and trend detection via the lake query endpoint:
SELECT agent_id, resource_type, SUM(credit_cost) as total_cost
FROM platform_agent_resource_usage
GROUP BY agent_id, resource_type
ORDER BY total_cost DESC
See docs/api/DATA_LAKE_API.md for lake query details.
Pydantic Schemas
AgentResourceUsageCreate
Request schema for creating a usage record.
| Field | Type | Required | Constraints |
|---|---|---|---|
| agent_id | str | yes | 1-255 chars |
| customer_id | UUID | yes | |
| resource_type | enum | yes | One of the 6 resource types |
| quantity | Decimal | yes | Must be > 0 |
| unit | enum | yes | Matching unit for type |
| credit_cost | Decimal | yes | Must be >= 0 |
| period_start | datetime | yes | |
| period_end | datetime | yes | Must be after period_start |
| metadata | dict | no | Default: empty dict |
AgentResourceUsageResponse
Response schema returned by API endpoints.
Includes all fields from AgentResourceUsageCreate plus:
| Field | Type | Description |
|---|---|---|
| id | UUID | Record primary key |
| created_at | datetime | Creation timestamp |
AgentResourceUsageSummary
Aggregated summary schema.
| Field | Type | Description |
|---|---|---|
| agent_id | str | Agent identifier |
| resource_type | enum | Resource type |
| total_quantity | Decimal | Sum of quantity |
| total_credit_cost | Decimal | Sum of credit cost |
| unit | enum | Unit of measurement |
| period_start | datetime | Summary period start |
| period_end | datetime | Summary period end |
| record_count | int | Number of usage records |
Spending Caps (Budgets)
Separate from credit metering and x402 wallets, agents can have a USD-denominated spending
cap that gates real-time operations — including A2A messages (POST /cloud/a2a/{agent_id}/message
and POST /cloud/a2a/route). If an agent's budget is exceeded, the operation is blocked with
402 Payment Required before the operation executes, not after.
This is the mechanism to understand if you're building agents that call /cloud/a2a/* and get
an unexpected 402 — a budget gate is almost certainly why, not a credit-balance issue.
Base path: /api/v1/billing
POST /budgets
Create a spending cap for an agent.
{
"agent_id": "agent-uuid",
"limit_usd": 50.00,
"period": "month",
"max_task_cost_usd": 2.00,
"auto_pause": true
}
| Field | Type | Required | Description |
|---|---|---|---|
agent_id | string | yes | Agent this budget applies to |
limit_usd | float | yes | Total spend allowed per period (> 0, ≤ 1,000,000) |
period | string | yes | day, week, or month |
max_task_cost_usd | float | no | Reject any single operation costing more than this |
auto_pause | bool | no (default false) | Automatically pause the agent when the period limit is hit |
Response (201 Created): a BudgetItem — same fields plus id, current_spend_usd (starts
at 0), created_at, updated_at.
GET /budgets
List budgets, optionally filtered by agent_id query param.
How the gate works
Every budgeted operation checks two things, in order:
- Per-task cap: if
max_task_cost_usdis set and this operation's estimated cost exceeds it, reject immediately regardless of remaining period budget. - Period cap: if
current_spend_usd + estimated_cost > limit_usd, reject. Ifauto_pauseis enabled, the agent is also paused (its registration is suspended) so subsequent calls fail fast instead of repeatedly hitting the same budget check.
An agent with no budget row at all has no spending cap — budgets are opt-in, not a default restriction.
Agent Wallets (x402)
In addition to credit-based billing, agents can hold USDC wallets for x402 micropayments. Wallets provide per-agent spending isolation with configurable limits. Refs #5008
Wallet Spending Guards
Each wallet enforces three layers of protection:
| Guard | Default | Description |
|---|---|---|
| Single payment cap | $10.00 | Maximum amount per individual transaction |
| Period spending limit | $100.00/month | Maximum total spend per billing period |
| Balance check | — | Transaction rejected if wallet balance insufficient |
Wallet Endpoints
| Method | Path | Description |
|---|---|---|
POST | /api/v1/cloud/x402/wallets | Create per-agent wallet |
GET | /api/v1/cloud/x402/wallets/{agent_id} | Get wallet balance and limits |
POST | /api/v1/cloud/x402/wallets/{agent_id}/fund | Add funds to wallet |
POST | /api/v1/cloud/x402/wallets/{agent_id}/withdraw | Withdraw from wallet |
PUT | /api/v1/cloud/x402/wallets/{agent_id}/limits | Update spending limits |
GET | /api/v1/cloud/x402/wallets/{agent_id}/transactions | Transaction history |
Integration with Credit Billing
There are three independent billing/spend systems, each covering a different concern:
- Credits (
AgentCloudBillingService) meter platform resource consumption (compute, vectors, storage, etc.) and deduct from the customer's credit balance. - Budgets (
/billing/budgets, above) are a real-time USD spending cap that gates operations like A2A messages before they execute — independent of credit balance. - Wallets hold USDC for tool marketplace payments via x402.
An agent can consume platform credits for infrastructure, be gated by a USD budget cap on A2A
traffic, AND spend USDC for external tool calls — all at once, as separate concerns. Use
get_customer_cloud_spend for credit costs, GET /billing/budgets for spending-cap status, and
the wallet transaction history for x402 payment costs.
Full wallet API documentation: Agent402 API — Agent Wallets
Key Source Files
| File | Purpose |
|---|---|
src/backend/app/services/agent_cloud_billing_service.py | Core billing service with pricing and metering |
src/backend/app/models/agent_resource_usage.py | SQLAlchemy model for agent_resource_usage table |
src/backend/app/schemas/agent_resource_usage.py | Pydantic schemas (ResourceType, ResourceUnit, Create/Response/Summary) |
src/backend/app/services/agent_wallet_service.py | Per-agent USDC wallet service with spending guards |
src/backend/app/services/x402_revenue_share_service.py | Marketplace 90/5/5 revenue split service |